diff --git a/lib/runtime/interfaces.go b/lib/runtime/interfaces.go index 996b59946f..e0a3fcf12c 100644 --- a/lib/runtime/interfaces.go +++ b/lib/runtime/interfaces.go @@ -24,6 +24,7 @@ type Storage interface { ClearChildStorage(keyToChild, key []byte) error NextKey([]byte) []byte ClearPrefixInChild(keyToChild, prefix []byte) error + ClearPrefixInChildWithLimit(keyToChild, prefix []byte, limit uint32) error GetChildNextKey(keyToChild, key []byte) ([]byte, error) GetChild(keyToChild []byte) (*trie.Trie, error) ClearPrefix(prefix []byte) (err error) diff --git a/lib/runtime/storage/trie.go b/lib/runtime/storage/trie.go index 53d78d31be..af68722886 100644 --- a/lib/runtime/storage/trie.go +++ b/lib/runtime/storage/trie.go @@ -253,6 +253,26 @@ func (s *TrieState) ClearPrefixInChild(keyToChild, prefix []byte) error { return nil } +func (s *TrieState) ClearPrefixInChildWithLimit(keyToChild, prefix []byte, limit uint32) error { + s.lock.Lock() + defer s.lock.Unlock() + + child, err := s.t.GetChild(keyToChild) + if err != nil { + return err + } + if child == nil { + return nil + } + + _, _, err = child.ClearPrefixLimit(prefix, limit) + if err != nil { + return fmt.Errorf("clearing prefix in child trie located at key 0x%x: %w", keyToChild, err) + } + + return nil +} + // GetChildNextKey returns the next lexicographical larger key from child storage. If it does not exist, it returns nil. func (s *TrieState) GetChildNextKey(keyToChild, key []byte) ([]byte, error) { s.lock.RLock() diff --git a/lib/runtime/wasmer/imports.go b/lib/runtime/wasmer/imports.go index 76fce540b5..2a56bf3006 100644 --- a/lib/runtime/wasmer/imports.go +++ b/lib/runtime/wasmer/imports.go @@ -48,11 +48,13 @@ package wasmer // extern int64_t ext_default_child_storage_next_key_version_1(void *context, int64_t a, int64_t b); // extern int64_t ext_default_child_storage_read_version_1(void *context, int64_t a, int64_t b, int64_t c, int32_t d); // extern int64_t ext_default_child_storage_root_version_1(void *context, int64_t a); +// extern int64_t ext_default_child_storage_root_version_2(void *context, int64_t a, int32_t b); // extern void ext_default_child_storage_set_version_1(void *context, int64_t a, int64_t b, int64_t c); // extern void ext_default_child_storage_storage_kill_version_1(void *context, int64_t a); // extern int32_t ext_default_child_storage_storage_kill_version_2(void *context, int64_t a, int64_t b); // extern int64_t ext_default_child_storage_storage_kill_version_3(void *context, int64_t a, int64_t b); // extern void ext_default_child_storage_clear_prefix_version_1(void *context, int64_t a, int64_t b); +// extern int64_t ext_default_child_storage_clear_prefix_version_2(void *context, int64_t a, int64_t b, int64_t c); // extern int32_t ext_default_child_storage_exists_version_1(void *context, int64_t a, int64_t b); // // extern void ext_allocator_free_version_1(void *context, int32_t a); @@ -66,6 +68,7 @@ package wasmer // extern int32_t ext_hashing_twox_128_version_1(void *context, int64_t a); // extern int32_t ext_hashing_twox_64_version_1(void *context, int64_t a); // +// extern void ext_offchain_index_clear_version_1(void *context, int64_t a); // extern void ext_offchain_index_set_version_1(void *context, int64_t a, int64_t b); // extern int32_t ext_offchain_is_validator_version_1(void *context); // extern void ext_offchain_local_storage_clear_version_1(void *context, int32_t a, int64_t b); @@ -1060,6 +1063,32 @@ func ext_default_child_storage_clear_prefix_version_1(context unsafe.Pointer, ch } } +//export ext_default_child_storage_clear_prefix_version_2 +func ext_default_child_storage_clear_prefix_version_2(context unsafe.Pointer, childStorageKey, prefixSpan, limitSpan C.int64_t) C.int64_t { + logger.Debug("executing...") + + instanceContext := wasm.IntoInstanceContext(context) + ctx := instanceContext.Data().(*runtime.Context) + storage := ctx.Storage + + keyToChild := asMemorySlice(instanceContext, childStorageKey) + prefix := asMemorySlice(instanceContext, prefixSpan) + + var limit *uint32 + err := scale.Unmarshal(asMemorySlice(instanceContext, limitSpan), limit) + if err != nil { + logger.Errorf("failed to decode limit: %s", err) + } + + err = storage.ClearPrefixInChildWithLimit(keyToChild, prefix, *limit) + if err != nil { + logger.Errorf("failed to clear prefix in child with limit: %s", err) + } + + // TODO: Should this always be 0 or could this be something else as well? + return C.int64_t(0) +} + //export ext_default_child_storage_exists_version_1 func ext_default_child_storage_exists_version_1(context unsafe.Pointer, childStorageKey, key C.int64_t) C.int32_t { @@ -1158,6 +1187,13 @@ func ext_default_child_storage_root_version_1(context unsafe.Pointer, return C.int64_t(root) } +//export ext_default_child_storage_root_version_2 +func ext_default_child_storage_root_version_2(context unsafe.Pointer, + childStorageKey C.int64_t, stateVersion C.int32_t) (ptrSize C.int64_t) { + // TODO: Implement this after we have storage trie version 1 implemented #2418 + return ext_default_child_storage_root_version_1(context, childStorageKey) +} + //export ext_default_child_storage_set_version_1 func ext_default_child_storage_set_version_1(context unsafe.Pointer, childStorageKeySpan, keySpan, valueSpan C.int64_t) { @@ -1503,6 +1539,22 @@ func ext_offchain_index_set_version_1(context unsafe.Pointer, keySpan, valueSpan } } +//export ext_offchain_index_clear_version_1 +func ext_offchain_index_clear_version_1(context unsafe.Pointer, keySpan C.int64_t) { + // Remove a key and its associated value from the Offchain DB. + // https://github.com/paritytech/substrate/blob/4d608f9c42e8d70d835a748fa929e59a99497e90/primitives/io/src/lib.rs#L1213 + logger.Trace("executing...") + + instanceContext := wasm.IntoInstanceContext(context) + runtimeCtx := instanceContext.Data().(*runtime.Context) + + storageKey := asMemorySlice(instanceContext, keySpan) + err := runtimeCtx.NodeStorage.BaseDB.Del(storageKey) + if err != nil { + logger.Errorf("failed to set value in raw storage: %s", err) + } +} + //export ext_offchain_local_storage_clear_version_1 func ext_offchain_local_storage_clear_version_1(context unsafe.Pointer, kind C.int32_t, key C.int64_t) { logger.Trace("executing...") @@ -2116,12 +2168,14 @@ func importsNodeRuntime() (imports *wasm.Imports, err error) { {"ext_crypto_sr25519_verify_version_2", ext_crypto_sr25519_verify_version_2, C.ext_crypto_sr25519_verify_version_2}, {"ext_crypto_start_batch_verify_version_1", ext_crypto_start_batch_verify_version_1, C.ext_crypto_start_batch_verify_version_1}, {"ext_default_child_storage_clear_prefix_version_1", ext_default_child_storage_clear_prefix_version_1, C.ext_default_child_storage_clear_prefix_version_1}, + {"ext_default_child_storage_clear_prefix_version_2", ext_default_child_storage_clear_prefix_version_2, C.ext_default_child_storage_clear_prefix_version_2}, {"ext_default_child_storage_clear_version_1", ext_default_child_storage_clear_version_1, C.ext_default_child_storage_clear_version_1}, {"ext_default_child_storage_exists_version_1", ext_default_child_storage_exists_version_1, C.ext_default_child_storage_exists_version_1}, {"ext_default_child_storage_get_version_1", ext_default_child_storage_get_version_1, C.ext_default_child_storage_get_version_1}, {"ext_default_child_storage_next_key_version_1", ext_default_child_storage_next_key_version_1, C.ext_default_child_storage_next_key_version_1}, {"ext_default_child_storage_read_version_1", ext_default_child_storage_read_version_1, C.ext_default_child_storage_read_version_1}, {"ext_default_child_storage_root_version_1", ext_default_child_storage_root_version_1, C.ext_default_child_storage_root_version_1}, + {"ext_default_child_storage_root_version_2", ext_default_child_storage_root_version_2, C.ext_default_child_storage_root_version_2}, {"ext_default_child_storage_set_version_1", ext_default_child_storage_set_version_1, C.ext_default_child_storage_set_version_1}, {"ext_default_child_storage_storage_kill_version_1", ext_default_child_storage_storage_kill_version_1, C.ext_default_child_storage_storage_kill_version_1}, {"ext_default_child_storage_storage_kill_version_2", ext_default_child_storage_storage_kill_version_2, C.ext_default_child_storage_storage_kill_version_2}, @@ -2139,6 +2193,7 @@ func importsNodeRuntime() (imports *wasm.Imports, err error) { {"ext_misc_print_num_version_1", ext_misc_print_num_version_1, C.ext_misc_print_num_version_1}, {"ext_misc_print_utf8_version_1", ext_misc_print_utf8_version_1, C.ext_misc_print_utf8_version_1}, {"ext_misc_runtime_version_version_1", ext_misc_runtime_version_version_1, C.ext_misc_runtime_version_version_1}, + {"ext_offchain_index_clear_version_1", ext_offchain_index_clear_version_1, C.ext_offchain_index_clear_version_1}, {"ext_offchain_http_request_add_header_version_1", ext_offchain_http_request_add_header_version_1, C.ext_offchain_http_request_add_header_version_1}, {"ext_offchain_http_request_start_version_1", ext_offchain_http_request_start_version_1, C.ext_offchain_http_request_start_version_1}, {"ext_offchain_index_set_version_1", ext_offchain_index_set_version_1, C.ext_offchain_index_set_version_1}, diff --git a/lib/runtime/wasmer/imports_test.go b/lib/runtime/wasmer/imports_test.go index c2f0521608..4bf06884c8 100644 --- a/lib/runtime/wasmer/imports_test.go +++ b/lib/runtime/wasmer/imports_test.go @@ -33,6 +33,27 @@ var testChildKey = []byte("childKey") var testKey = []byte("key") var testValue = []byte("value") +func Test_ext_offchain_index_clear_version_1(t *testing.T) { + inst := NewTestInstance(t, runtime.HOST_API_TEST_RUNTIME) + + testKey := []byte("testkey") + testValue := []byte("testvalue") + + err := inst.ctx.NodeStorage.BaseDB.Put(testKey, testValue) + require.NoError(t, err) + + encKey, err := scale.Marshal(testKey) + require.NoError(t, err) + + _, err = inst.Exec("ext_offchain_index_clear_version_1", encKey) + require.NoError(t, err) + + value, err := inst.ctx.NodeStorage.BaseDB.Get(testKey) + require.NoError(t, err) + + require.True(t, bytes.Equal(testValue, value)) +} + func Test_ext_offchain_timestamp_version_1(t *testing.T) { inst := NewTestInstance(t, runtime.HOST_API_TEST_RUNTIME) runtimeFunc, ok := inst.vm.Exports["rtm_ext_offchain_timestamp_version_1"] @@ -1283,6 +1304,54 @@ func Test_ext_default_child_storage_clear_version_1(t *testing.T) { require.Nil(t, val) } +func Test_ext_default_child_storage_clear_prefix_version_2(t *testing.T) { + inst := NewTestInstance(t, runtime.HOST_API_TEST_RUNTIME) + + prefix := []byte("key") + limit := uint32(0) + + testKeyValuePair := []struct { + key []byte + value []byte + }{ + {[]byte("keyOne"), []byte("value1")}, + {[]byte("keyTwo"), []byte("value2")}, + {[]byte("keyThree"), []byte("value3")}, + } + + err := inst.ctx.Storage.SetChild(testChildKey, trie.NewEmptyTrie()) + require.NoError(t, err) + + for _, kv := range testKeyValuePair { + err = inst.ctx.Storage.SetChildStorage(testChildKey, kv.key, kv.value) + require.NoError(t, err) + } + + // Confirm if value is set + keys, err := inst.ctx.Storage.(*storage.TrieState).GetKeysWithPrefixFromChild(testChildKey, prefix) + require.NoError(t, err) + require.Equal(t, 3, len(keys)) + + encChildKey, err := scale.Marshal(testChildKey) + require.NoError(t, err) + + encPrefix, err := scale.Marshal(prefix) + require.NoError(t, err) + + encLimit, err := scale.Marshal(limit) + require.NoError(t, err) + + data := append(encChildKey, encPrefix...) + data = append(data, encLimit...) + + _, err = inst.Exec("rtm_ext_default_child_storage_clear_prefix_version_2", data) + require.NoError(t, err) + + keys, err = inst.ctx.Storage.(*storage.TrieState).GetKeysWithPrefixFromChild(testChildKey, prefix) + require.NoError(t, err) + require.Equal(t, 0, len(keys)) +} + func Test_ext_default_child_storage_clear_prefix_version_1(t *testing.T) { t.Parallel() inst := NewTestInstance(t, runtime.HOST_API_TEST_RUNTIME)