Skip to content

Commit

Permalink
Merge branch 'development' into noot/sync-babe-fix
Browse files Browse the repository at this point in the history
  • Loading branch information
noot authored Oct 20, 2021
2 parents b62dd86 + 67bb5ef commit 8d9dd14
Show file tree
Hide file tree
Showing 10 changed files with 346 additions and 86 deletions.
5 changes: 5 additions & 0 deletions dot/state/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,11 @@ func (s *BaseState) Get(key []byte) ([]byte, error) {
return s.db.Get(key)
}

// Del deletes key from database
func (s *BaseState) Del(key []byte) error {
return s.db.Del(key)
}

func (s *BaseState) storeSkipToEpoch(epoch uint64) error {
buf := make([]byte, 8)
binary.LittleEndian.PutUint64(buf, epoch)
Expand Down
2 changes: 1 addition & 1 deletion lib/runtime/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ const (
// v0.8 test API wasm
HOST_API_TEST_RUNTIME = "hostapi_runtime"
HOST_API_TEST_RUNTIME_FP = "hostapi_runtime.compact.wasm"
HOST_API_TEST_RUNTIME_URL = "https://github.com/ChainSafe/polkadot-spec/blob/e8770476a4b8445cddd2bd1e8f0060a83eaccb38/test/hostapi_runtime.compact.wasm?raw=true"
HOST_API_TEST_RUNTIME_URL = "https://github.com/ChainSafe/polkadot-spec/blob/f9f8c94397d155c4f2edc9c59828dc4ef2c62dd3/test/hostapi_runtime.compact.wasm?raw=true"

// v0.8 substrate runtime with modified name and babe C=(1, 1)
DEV_RUNTIME = "dev_runtime"
Expand Down
1 change: 1 addition & 0 deletions lib/runtime/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ type BasicNetwork interface {
type BasicStorage interface {
Put(key []byte, value []byte) error
Get(key []byte) ([]byte, error)
Del(key []byte) error
}

// TransactionState interface for adding transactions to pool
Expand Down
35 changes: 32 additions & 3 deletions lib/runtime/wasmer/imports.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ package wasmer
// extern int32_t ext_offchain_random_seed_version_1(void *context);
// extern int64_t ext_offchain_submit_transaction_version_1(void *context, int64_t a);
// extern int64_t ext_offchain_timestamp_version_1(void *context);
// extern void ext_offchain_sleep_until_version_1(void *context, int64_t a);
//
// extern void ext_storage_append_version_1(void *context, int64_t a, int64_t b);
// extern int64_t ext_storage_changes_root_version_1(void *context, int64_t a);
Expand Down Expand Up @@ -1366,9 +1367,28 @@ func ext_offchain_index_set_version_1(context unsafe.Pointer, keySpan, valueSpan
}

//export ext_offchain_local_storage_clear_version_1
func ext_offchain_local_storage_clear_version_1(context unsafe.Pointer, a C.int32_t, b C.int64_t) {
func ext_offchain_local_storage_clear_version_1(context unsafe.Pointer, kind C.int32_t, key C.int64_t) {
logger.Trace("[ext_offchain_local_storage_clear_version_1] executing...")
logger.Warn("[ext_offchain_local_storage_clear_version_1] unimplemented")
instanceContext := wasm.IntoInstanceContext(context)
runtimeCtx := instanceContext.Data().(*runtime.Context)

storageKey := asMemorySlice(instanceContext, key)

memory := instanceContext.Memory().Data()
kindInt := binary.LittleEndian.Uint32(memory[kind : kind+4])

var err error

switch runtime.NodeStorageType(kindInt) {
case runtime.NodeStorageTypePersistent:
err = runtimeCtx.NodeStorage.PersistentStorage.Del(storageKey)
case runtime.NodeStorageTypeLocal:
err = runtimeCtx.NodeStorage.LocalStorage.Del(storageKey)
}

if err != nil {
logger.Error("[ext_offchain_local_storage_clear_version_1] failed to clear value from storage", "error", err)
}
}

//export ext_offchain_is_validator_version_1
Expand Down Expand Up @@ -1557,6 +1577,12 @@ func ext_offchain_timestamp_version_1(context unsafe.Pointer) C.int64_t {
return 0
}

//export ext_offchain_sleep_until_version_1
func ext_offchain_sleep_until_version_1(_ unsafe.Pointer, deadline C.int64_t) {
logger.Trace("executing...")
logger.Warn("unimplemented")
}

func storageAppend(storage runtime.Storage, key, valueToAppend []byte) error {
nextLength := big.NewInt(1)
var valueRes []byte
Expand Down Expand Up @@ -2172,7 +2198,10 @@ func ImportsNodeRuntime() (*wasm.Imports, error) { //nolint
if err != nil {
return nil, err
}

_, err = imports.Append("ext_offchain_sleep_until_version_1", ext_offchain_sleep_until_version_1, C.ext_offchain_sleep_until_version_1)
if err != nil {
return nil, err
}
_, err = imports.Append("ext_sandbox_instance_teardown_version_1", ext_sandbox_instance_teardown_version_1, C.ext_sandbox_instance_teardown_version_1)
if err != nil {
return nil, err
Expand Down
44 changes: 44 additions & 0 deletions lib/runtime/wasmer/imports_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,50 @@ func Test_ext_storage_clear_version_1(t *testing.T) {
require.Nil(t, val)
}

func Test_ext_offchain_local_storage_clear_version_1_Persistent(t *testing.T) {
inst := NewTestInstance(t, runtime.HOST_API_TEST_RUNTIME)

testkey := []byte("key1")
err := inst.NodeStorage().PersistentStorage.Put(testkey, []byte{1})
require.NoError(t, err)

kind := int32(1)
encKind, err := scale.Marshal(kind)
require.NoError(t, err)

encKey, err := scale.Marshal(testkey)
require.NoError(t, err)

_, err = inst.Exec("rtm_ext_offchain_local_storage_clear_version_1", append(encKind, encKey...))
require.NoError(t, err)

val, err := inst.NodeStorage().PersistentStorage.Get(testkey)
require.EqualError(t, err, "Key not found")
require.Nil(t, val)
}

func Test_ext_offchain_local_storage_clear_version_1_Local(t *testing.T) {
inst := NewTestInstance(t, runtime.HOST_API_TEST_RUNTIME)

testkey := []byte("key1")
err := inst.NodeStorage().LocalStorage.Put(testkey, []byte{1})
require.NoError(t, err)

kind := int32(2)
encKind, err := scale.Marshal(kind)
require.NoError(t, err)

encKey, err := scale.Marshal(testkey)
require.NoError(t, err)

_, err = inst.Exec("rtm_ext_offchain_local_storage_clear_version_1", append(encKind, encKey...))
require.NoError(t, err)

val, err := inst.NodeStorage().LocalStorage.Get(testkey)
require.EqualError(t, err, "Key not found")
require.Nil(t, val)
}

func Test_ext_storage_clear_prefix_version_1_hostAPI(t *testing.T) {
inst := NewTestInstance(t, runtime.HOST_API_TEST_RUNTIME)

Expand Down
62 changes: 62 additions & 0 deletions lib/trie/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,17 @@ package trie

import (
"bytes"
"errors"
"fmt"

"github.com/ChainSafe/gossamer/lib/common"

"github.com/ChainSafe/chaindb"
)

// ErrEmptyProof indicates the proof slice is empty
var ErrEmptyProof = errors.New("proof slice empty")

// Store stores each trie node in the database, where the key is the hash of the encoded node and the value is the encoded node.
// Generally, this will only be used for the genesis trie.
func (t *Trie) Store(db chaindb.Database) error {
Expand Down Expand Up @@ -73,6 +77,64 @@ func (t *Trie) store(db chaindb.Batch, curr node) error {
return nil
}

// LoadFromProof create a partial trie based on the proof slice, as it only contains nodes that are in the proof afaik.
func (t *Trie) LoadFromProof(proof [][]byte, root []byte) error {
if len(proof) == 0 {
return ErrEmptyProof
}

mappedNodes := make(map[string]node, len(proof))

// map all the proofs hash -> decoded node
// and takes the loop to indentify the root node
for _, rawNode := range proof {
decNode, err := decodeBytes(rawNode)
if err != nil {
return err
}

decNode.setDirty(false)
decNode.setEncodingAndHash(rawNode, nil)

_, computedRoot, err := decNode.encodeAndHash()
if err != nil {
return err
}

mappedNodes[common.BytesToHex(computedRoot)] = decNode

if bytes.Equal(computedRoot, root) {
t.root = decNode
}
}

t.loadProof(mappedNodes, t.root)
return nil
}

// loadProof is a recursive function that will create all the trie paths based
// on the mapped proofs slice starting by the root
func (t *Trie) loadProof(proof map[string]node, curr node) {
c, ok := curr.(*branch)
if !ok {
return
}

for i, child := range c.children {
if child == nil {
continue
}

proofNode, ok := proof[common.BytesToHex(child.getHash())]
if !ok {
continue
}

c.children[i] = proofNode
t.loadProof(proof, proofNode)
}
}

// Load reconstructs the trie from the database from the given root hash. Used when restarting the node to load the current state trie.
func (t *Trie) Load(db chaindb.Database, root common.Hash) error {
if root == EmptyHash {
Expand Down
95 changes: 23 additions & 72 deletions lib/trie/lookup.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,86 +2,37 @@ package trie

import (
"bytes"
"errors"

"github.com/ChainSafe/chaindb"
)

var (
// ErrProofNodeNotFound when a needed proof node is not in the database
ErrProofNodeNotFound = errors.New("cannot find a trie node in the database")
)

// lookup struct holds the state root and database reference
// used to retrieve trie information from database
type lookup struct {
// root to start the lookup
root []byte
db chaindb.Database
// findAndRecord search for a desired key recording all the nodes in the path including the desired node
func findAndRecord(t *Trie, key []byte, recorder *recorder) error {
return find(t.root, key, recorder)
}

// newLookup returns a Lookup to helps the proof generator
func newLookup(rootHash []byte, db chaindb.Database) *lookup {
lk := &lookup{db: db}
lk.root = make([]byte, len(rootHash))
copy(lk.root, rootHash)

return lk
}

// find will return the desired value or nil if key cannot be found and will record visited nodes
func (l *lookup) find(key []byte, recorder *recorder) ([]byte, error) {
partial := key
hash := l.root

for {
nodeData, err := l.db.Get(hash)
if err != nil {
return nil, ErrProofNodeNotFound
}

nodeHash := make([]byte, len(hash))
copy(nodeHash, hash)

recorder.record(nodeHash, nodeData)

decoded, err := decodeBytes(nodeData)
if err != nil {
return nil, err
}
func find(parent node, key []byte, recorder *recorder) error {
enc, hash, err := parent.encodeAndHash()
if err != nil {
return err
}

switch currNode := decoded.(type) {
case nil:
return nil, nil
recorder.record(hash, enc)

case *leaf:
if bytes.Equal(currNode.key, partial) {
return currNode.value, nil
}
return nil, nil
b, ok := parent.(*branch)
if !ok {
return nil
}

case *branch:
switch len(partial) {
case 0:
return currNode.value, nil
default:
if !bytes.HasPrefix(partial, currNode.key) {
return nil, nil
}
length := lenCommonPrefix(b.key, key)

if bytes.Equal(partial, currNode.key) {
return currNode.value, nil
}
// found the value at this node
if bytes.Equal(b.key, key) || len(key) == 0 {
return nil
}

length := lenCommonPrefix(currNode.key, partial)
switch child := currNode.children[partial[length]].(type) {
case nil:
return nil, nil
default:
partial = partial[length+1:]
copy(hash, child.getHash())
}
}
}
// did not find value
if bytes.Equal(b.key[:length], key) && len(key) < len(b.key) {
return nil
}

return find(b.children[key[length]], key[length+1:], recorder)
}
Loading

0 comments on commit 8d9dd14

Please sign in to comment.