From 67171a89446153b20d0e02e623c56c3611789dd0 Mon Sep 17 00:00:00 2001 From: Edward Mack Date: Mon, 28 Sep 2020 14:17:31 -0400 Subject: [PATCH 1/3] implement DeleteChildStorage interface --- dot/state/trie.go | 7 +++++++ lib/runtime/imports.go | 1 + lib/runtime/imports_test.go | 17 +++++++++++++++++ lib/runtime/interface.go | 1 + lib/runtime/test_helpers.go | 4 ++++ lib/trie/child_storage.go | 6 ++++++ 6 files changed, 36 insertions(+) diff --git a/dot/state/trie.go b/dot/state/trie.go index 02520592fb..6c1e6a2784 100644 --- a/dot/state/trie.go +++ b/dot/state/trie.go @@ -137,3 +137,10 @@ func (s *TrieState) GetBalance(key [32]byte) (uint64, error) { return binary.LittleEndian.Uint64(bal), nil } + +// DeleteChildStorage deletes child storage from the trie +func (s *TrieState) DeleteChildStorage(key []byte) error { + s.lock.Lock() + defer s.lock.Unlock() + return s.t.DeleteFromChild(key) +} diff --git a/lib/runtime/imports.go b/lib/runtime/imports.go index 5b1641a825..dba4d82eae 100644 --- a/lib/runtime/imports.go +++ b/lib/runtime/imports.go @@ -85,6 +85,7 @@ import ( func ext_kill_child_storage(context unsafe.Pointer, a, b C.int32_t) { logger.Trace("[ext_kill_child_storage] executing...") logger.Warn("[ext_kill_child_storage] not yet implemented") + } //export ext_sandbox_memory_new diff --git a/lib/runtime/imports_test.go b/lib/runtime/imports_test.go index 7f2a71238b..430d5a766b 100644 --- a/lib/runtime/imports_test.go +++ b/lib/runtime/imports_test.go @@ -2,6 +2,7 @@ package runtime import ( "bytes" + "fmt" "testing" "github.com/ChainSafe/gossamer/lib/common" @@ -36,3 +37,19 @@ func Test_ext_twox_256(t *testing.T) { t.Fatalf("fail: got %x expected %x", mem[out:out+32], expected[:]) } } + +func Test_ext_kill_child_storage(t *testing.T) { + runtime := NewTestRuntime(t, TEST_RUNTIME) + + //mem := runtime.vm.Memory.Data() + + // call wasm function + testFunc, ok := runtime.vm.Exports["test_ext_kill_child_storage"] + if !ok { + t.Fatal("could not find exported function") + } + + res, err := testFunc(1, 2) + require.NoError(t, err) + fmt.Printf("RES %v\n", res) +} diff --git a/lib/runtime/interface.go b/lib/runtime/interface.go index 52fbdf9ac6..78453506c5 100644 --- a/lib/runtime/interface.go +++ b/lib/runtime/interface.go @@ -33,6 +33,7 @@ type Storage interface { Entries() map[string][]byte SetBalance(key [32]byte, balance uint64) error GetBalance(key [32]byte) (uint64, error) + DeleteChildStorage(key []byte) error } // BasicNetwork interface for functions used by runtime network state function diff --git a/lib/runtime/test_helpers.go b/lib/runtime/test_helpers.go index e5824cf734..cbbb6b5ca5 100644 --- a/lib/runtime/test_helpers.go +++ b/lib/runtime/test_helpers.go @@ -283,6 +283,10 @@ func (trs testRuntimeStorage) GetBalance(key [32]byte) (uint64, error) { return binary.LittleEndian.Uint64(bal), nil } +func (trs testRuntimeStorage) DeleteChildStorage(key []byte) error { + return trs.trie.DeleteFromChild(key) +} + type testRuntimeNetwork struct { } diff --git a/lib/trie/child_storage.go b/lib/trie/child_storage.go index a589f498a8..5d849ecb14 100644 --- a/lib/trie/child_storage.go +++ b/lib/trie/child_storage.go @@ -98,3 +98,9 @@ func (t *Trie) GetFromChild(keyToChild, key []byte) ([]byte, error) { return child.Get(key) } + +// DeleteFromChild deletes from child storage +func (t *Trie) DeleteFromChild(keyToChild []byte) error { + key := append(ChildStorageKeyPrefix, keyToChild...) + return t.Delete(key) +} From 5910429ace94c0dab9a98e4951c482e7dd7a72e3 Mon Sep 17 00:00:00 2001 From: Edward Mack Date: Mon, 28 Sep 2020 15:42:40 -0400 Subject: [PATCH 2/3] implement ext_kill_child_storage and tests --- lib/runtime/imports.go | 14 ++++++++-- lib/runtime/imports_test.go | 52 ++++++++++++++++++++++++++++++++----- lib/trie/child_storage.go | 2 +- 3 files changed, 58 insertions(+), 10 deletions(-) diff --git a/lib/runtime/imports.go b/lib/runtime/imports.go index dba4d82eae..cb38b625a9 100644 --- a/lib/runtime/imports.go +++ b/lib/runtime/imports.go @@ -82,10 +82,20 @@ import ( ) //export ext_kill_child_storage -func ext_kill_child_storage(context unsafe.Pointer, a, b C.int32_t) { +func ext_kill_child_storage(context unsafe.Pointer, storageKeyData, storageKeyLen C.int32_t) { logger.Trace("[ext_kill_child_storage] executing...") - logger.Warn("[ext_kill_child_storage] not yet implemented") + instanceContext := wasm.IntoInstanceContext(context) + memory := instanceContext.Memory().Data() + + runtimeCtx := instanceContext.Data().(*Ctx) + s := runtimeCtx.storage + keyToChild := memory[storageKeyData : storageKeyData+storageKeyLen] + + err := s.DeleteChildStorage(keyToChild) + if err != nil { + logger.Error("[ext_kill_child_storage]", "error", err) + } } //export ext_sandbox_memory_new diff --git a/lib/runtime/imports_test.go b/lib/runtime/imports_test.go index 430d5a766b..51192424e5 100644 --- a/lib/runtime/imports_test.go +++ b/lib/runtime/imports_test.go @@ -2,11 +2,10 @@ package runtime import ( "bytes" - "fmt" "testing" "github.com/ChainSafe/gossamer/lib/common" - + "github.com/ChainSafe/gossamer/lib/trie" "github.com/stretchr/testify/require" ) @@ -40,16 +39,55 @@ func Test_ext_twox_256(t *testing.T) { func Test_ext_kill_child_storage(t *testing.T) { runtime := NewTestRuntime(t, TEST_RUNTIME) + mem := runtime.vm.Memory.Data() + // set child storage + storageKey := []byte("childstore1") + childKey := []byte("key1") + value := []byte("value") + err := runtime.ctx.storage.SetChild(storageKey, trie.NewEmptyTrie()) + require.Nil(t, err) - //mem := runtime.vm.Memory.Data() + storageKeyLen := uint32(len(storageKey)) + storageKeyPtr, err := runtime.malloc(storageKeyLen) + require.NoError(t, err) - // call wasm function - testFunc, ok := runtime.vm.Exports["test_ext_kill_child_storage"] + childKeyLen := uint32(len(childKey)) + childKeyPtr, err := runtime.malloc(childKeyLen) + require.NoError(t, err) + + valueLen := uint32(len(value)) + valuePtr, err := runtime.malloc(valueLen) + require.NoError(t, err) + + copy(mem[storageKeyPtr:storageKeyPtr+storageKeyLen], storageKey) + copy(mem[childKeyPtr:childKeyPtr+childKeyLen], childKey) + copy(mem[valuePtr:valuePtr+valueLen], value) + + // call wasm function to set child storage + testFunc, ok := runtime.vm.Exports["test_ext_set_child_storage"] if !ok { t.Fatal("could not find exported function") } - res, err := testFunc(1, 2) + _, err = testFunc(int32(storageKeyPtr), int32(storageKeyLen), int32(childKeyPtr), int32(childKeyLen), int32(valuePtr), int32(valueLen)) + require.Nil(t, err) + + // confirm set + checkValue, err := runtime.ctx.storage.GetChildStorage(storageKey, childKey) require.NoError(t, err) - fmt.Printf("RES %v\n", res) + require.Equal(t, value, checkValue) + + // call wasm function to kill child storage + testDelete, ok := runtime.vm.Exports["test_ext_kill_child_storage"] + if !ok { + t.Fatal("could not find exported function") + } + + _, err = testDelete(int32(storageKeyPtr), int32(storageKeyLen)) + require.NoError(t, err) + + // confirm value is deleted + checkDelete, err := runtime.ctx.storage.GetChildStorage(storageKey, childKey) + require.EqualError(t, err, "child trie does not exist at key :child_storage:default:"+string(storageKey)) + require.Equal(t, []byte(nil), checkDelete) } diff --git a/lib/trie/child_storage.go b/lib/trie/child_storage.go index 5d849ecb14..bf35f49fb4 100644 --- a/lib/trie/child_storage.go +++ b/lib/trie/child_storage.go @@ -23,7 +23,7 @@ import ( ) // ChildStorageKeyPrefix is the prefix for all child storage keys -var ChildStorageKeyPrefix = []byte(":child_storage:") +var ChildStorageKeyPrefix = []byte(":child_storage:default:") // PutChild inserts a child trie into the main trie at key :child_storage:[keyToChild] func (t *Trie) PutChild(keyToChild []byte, child *Trie) error { From b328845cf40be131faa95b6e4b6f51394ca8d6dd Mon Sep 17 00:00:00 2001 From: Edward Mack Date: Mon, 28 Sep 2020 17:34:40 -0400 Subject: [PATCH 3/3] implement ext_get_allocated_child_storage --- lib/runtime/imports.go | 36 +++++++++++++++++++++-- lib/runtime/imports_test.go | 57 +++++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 3 deletions(-) diff --git a/lib/runtime/imports.go b/lib/runtime/imports.go index cb38b625a9..fad93d1a59 100644 --- a/lib/runtime/imports.go +++ b/lib/runtime/imports.go @@ -132,10 +132,40 @@ func ext_sandbox_instance_teardown(context unsafe.Pointer, a C.int32_t) { } //export ext_get_allocated_child_storage -func ext_get_allocated_child_storage(context unsafe.Pointer, a, b, c, d, e C.int32_t) C.int32_t { +func ext_get_allocated_child_storage(context unsafe.Pointer, storageKeyData, storageKeyLen, keyData, keyLen, writtenOut C.int32_t) int32 { logger.Trace("[ext_get_allocated_child_storage] executing...") - logger.Warn("[ext_get_allocated_child_storage] not yet implemented") - return 0 + instanceContext := wasm.IntoInstanceContext(context) + memory := instanceContext.Memory().Data() + + runtimeCtx := instanceContext.Data().(*Ctx) + s := runtimeCtx.storage + + keyToChild := memory[storageKeyData : storageKeyData+storageKeyLen] + key := memory[keyData : keyData+keyLen] + + value, err := s.GetChildStorage(keyToChild, key) + if err != nil { + logger.Error("[ext_get_allocated_child_storage]", "error", err) + return 0 + } + valueLen := uint32(len(value)) + if valueLen == 0 { + copy(memory[writtenOut:writtenOut+4], []byte{0xff, 0xff, 0xff, 0xff}) + return 0 + } + + // copy length to memory + byteLen := make([]byte, 4) + binary.LittleEndian.PutUint32(byteLen, valueLen) + copy(memory[writtenOut:writtenOut+4], byteLen) + + resPtr, err := runtimeCtx.allocator.Allocate(valueLen) + if err != nil { + logger.Error("[ext_get_allocated_child_storage]", "error", err) + return 0 + } + copy(memory[resPtr:resPtr+valueLen], value) + return int32(resPtr) } //export ext_child_storage_root diff --git a/lib/runtime/imports_test.go b/lib/runtime/imports_test.go index 51192424e5..ecffb56211 100644 --- a/lib/runtime/imports_test.go +++ b/lib/runtime/imports_test.go @@ -91,3 +91,60 @@ func Test_ext_kill_child_storage(t *testing.T) { require.EqualError(t, err, "child trie does not exist at key :child_storage:default:"+string(storageKey)) require.Equal(t, []byte(nil), checkDelete) } + +func Test_ext_get_allocated_child_storage(t *testing.T) { + runtime := NewTestRuntime(t, TEST_RUNTIME) + mem := runtime.vm.Memory.Data() + + // set child storage + storageKey := []byte("childstore1") + childKey := []byte("key1") + err := runtime.ctx.storage.SetChild(storageKey, trie.NewEmptyTrie()) + require.Nil(t, err) + + storageKeyLen := uint32(len(storageKey)) + storageKeyPtr, err := runtime.malloc(storageKeyLen) + require.NoError(t, err) + + childKeyLen := uint32(len(childKey)) + childKeyPtr, err := runtime.malloc(childKeyLen) + require.NoError(t, err) + + copy(mem[storageKeyPtr:storageKeyPtr+storageKeyLen], storageKey) + copy(mem[childKeyPtr:childKeyPtr+childKeyLen], childKey) + + // call wasm function to get child value (should be not found since we haven't set it yet) + getValueFunc, ok := runtime.vm.Exports["test_ext_get_allocated_child_storage"] + if !ok { + t.Fatal("could not find exported function") + } + + writtenOut, err := runtime.malloc(4) + require.NoError(t, err) + res, err := getValueFunc(int32(storageKeyPtr), int32(storageKeyLen), int32(childKeyPtr), int32(childKeyLen), int32(writtenOut)) + require.NoError(t, err) + require.Equal(t, []byte{0xff, 0xff, 0xff, 0xff}, mem[writtenOut:writtenOut+4]) + require.Equal(t, int32(0), res.ToI32()) + + // store the child value + value := []byte("value") + valueLen := uint32(len(value)) + valuePtr, err := runtime.malloc(valueLen) + require.NoError(t, err) + copy(mem[valuePtr:valuePtr+valueLen], value) + + // call wasm function to set child storage + setValueFunc, ok := runtime.vm.Exports["test_ext_set_child_storage"] + if !ok { + t.Fatal("could not find exported function") + } + + _, err = setValueFunc(int32(storageKeyPtr), int32(storageKeyLen), int32(childKeyPtr), int32(childKeyLen), int32(valuePtr), int32(valueLen)) + require.Nil(t, err) + + // call wasm function to check for value, this should be set now + res, err = getValueFunc(int32(storageKeyPtr), int32(storageKeyLen), int32(childKeyPtr), int32(childKeyLen), int32(writtenOut)) + require.NoError(t, err) + require.Equal(t, []byte{0x5, 0x0, 0x0, 0x0}, mem[writtenOut:writtenOut+4]) + require.Equal(t, value, mem[res.ToI32():res.ToI32()+5]) +}