Skip to content

Commit

Permalink
lib/runtime: implement ext_kill_child_storage and ext_get_allocated_c…
Browse files Browse the repository at this point in the history
…hild_storage (#1110)

* implement DeleteChildStorage interface

* implement ext_kill_child_storage and tests

* implement ext_get_allocated_child_storage
  • Loading branch information
edwardmack authored Sep 30, 2020
1 parent 2615560 commit 25393f3
Show file tree
Hide file tree
Showing 6 changed files with 178 additions and 7 deletions.
7 changes: 7 additions & 0 deletions dot/state/trie.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
51 changes: 46 additions & 5 deletions lib/runtime/imports.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +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
Expand Down Expand Up @@ -121,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
Expand Down
114 changes: 113 additions & 1 deletion lib/runtime/imports_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"testing"

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

"github.com/ChainSafe/gossamer/lib/trie"
"github.com/stretchr/testify/require"
)

Expand Down Expand Up @@ -36,3 +36,115 @@ 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()
// set child storage
storageKey := []byte("childstore1")
childKey := []byte("key1")
value := []byte("value")
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)

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")
}

_, 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)
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)
}

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])
}
1 change: 1 addition & 0 deletions lib/runtime/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions lib/runtime/test_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
}

Expand Down
8 changes: 7 additions & 1 deletion lib/trie/child_storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
}

0 comments on commit 25393f3

Please sign in to comment.