Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(lib/runtime): add extra required runtime imports for parachain validation #3254

Merged
merged 15 commits into from
Jul 27, 2023
Merged
3 changes: 2 additions & 1 deletion lib/runtime/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ package runtime

const (
// v0.9 test API wasm
// This wasm is generated using https://github.com/ChainSafe/polkadot-spec.
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/4d190603d21d4431888bcb1ec546c4dc03b7bf93/test/runtimes/hostapi/hostapi_runtime.compact.wasm?raw=true" //nolint:lll
HOST_API_TEST_RUNTIME_URL = "https://raw.githubusercontent.com/ChainSafe/polkadot-spec/master/test/runtimes/hostapi/hostapi_runtime.compact.wasm"

// v0.9.29 polkadot
POLKADOT_RUNTIME_v0929 = "polkadot_runtime-v929"
Expand Down
1 change: 1 addition & 0 deletions lib/runtime/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
20 changes: 20 additions & 0 deletions lib/runtime/storage/trie.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
56 changes: 56 additions & 0 deletions lib/runtime/wasmer/imports.go
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -1060,6 +1063,33 @@ 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 {
Expand Down Expand Up @@ -1158,6 +1188,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) {
Expand Down Expand Up @@ -1503,6 +1540,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...")
Expand Down Expand Up @@ -2116,12 +2169,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},
Expand All @@ -2139,6 +2194,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},
Expand Down
66 changes: 66 additions & 0 deletions lib/runtime/wasmer/imports_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,24 @@ 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)

err := inst.ctx.NodeStorage.BaseDB.Put(testKey, testValue)
require.NoError(t, err)

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

_, err = inst.Exec("rtm_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"]
Expand Down Expand Up @@ -1283,6 +1301,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)
Expand Down