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
4 changes: 3 additions & 1 deletion lib/runtime/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ 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://github.com/ChainSafe/polkadot-spec/raw/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) (uint32, bool, error)
GetChildNextKey(keyToChild, key []byte) ([]byte, error)
GetChild(keyToChild []byte) (*trie.Trie, error)
ClearPrefix(prefix []byte) (err error)
Expand Down
12 changes: 12 additions & 0 deletions lib/runtime/storage/trie.go
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,18 @@ func (s *TrieState) ClearPrefixInChild(keyToChild, prefix []byte) error {
return nil
}

func (s *TrieState) ClearPrefixInChildWithLimit(keyToChild, prefix []byte, limit uint32) (uint32, bool, error) {
s.lock.Lock()
defer s.lock.Unlock()

child, err := s.t.GetChild(keyToChild)
if err != nil || child == nil {
return 0, false, err
}

return child.ClearPrefixLimit(prefix, limit)
}

// 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
97 changes: 97 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,74 @@ func ext_default_child_storage_clear_prefix_version_1(context unsafe.Pointer, ch
}
}

// NewDigestItem returns a new VaryingDataType to represent a DigestItem
func NewKillStorageResult(deleted uint32, allDeleted bool) scale.VaryingDataType {
killStorageResult := scale.MustNewVaryingDataType(new(noneRemain), new(someRemain))

var err error
if allDeleted {
err = killStorageResult.Set(noneRemain(deleted))
} else {
err = killStorageResult.Set(someRemain(deleted))
}

if err != nil {
panic(err)
}
return killStorageResult
}

//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)

limitBytes := asMemorySlice(instanceContext, limitSpan)

var limit []byte
err := scale.Unmarshal(limitBytes, &limit)
if err != nil {
logger.Warnf("failed scale decoding limit: %s", err)
return mustToWasmMemoryNil(instanceContext)
}

if len(limit) == 0 {
// limit is None, set limit to max
limit = []byte{0xff, 0xff, 0xff, 0xff}
}

limitUint := binary.LittleEndian.Uint32(limit)

deleted, allDeleted, err := storage.ClearPrefixInChildWithLimit(
keyToChild, prefix, limitUint)
if err != nil {
logger.Errorf("failed to clear prefix in child with limit: %s", err)
}

killStorageResult := NewKillStorageResult(deleted, allDeleted)

encodedKillStorageResult, err := scale.Marshal(killStorageResult)
if err != nil {
logger.Errorf("failed to encode result: %s", err)
return 0
}

resultSpan, err := toWasmMemoryOptional(instanceContext, encodedKillStorageResult)
if err != nil {
logger.Errorf("failed to allocate: %s", err)
return 0
}

return C.int64_t(resultSpan)
}

//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 +1229,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 +1581,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 +2210,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 +2235,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
72 changes: 72 additions & 0 deletions lib/runtime/wasmer/imports_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,26 @@ 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)

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

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

_, err = inst.Exec("rtm_ext_offchain_index_clear_version_1", encKey)
require.NoError(t, err)

_, err = inst.ctx.NodeStorage.BaseDB.Get(testKey)
require.ErrorIs(t, err, chaindb.ErrKeyNotFound)
}

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 +1303,58 @@ 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) {
kishansagathiya marked this conversation as resolved.
Show resolved Hide resolved
inst := NewTestInstance(t, runtime.HOST_API_TEST_RUNTIME)

prefix := []byte("key")

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)

testLimit := uint32(1)
testLimitBytes := make([]byte, 4)
binary.LittleEndian.PutUint32(testLimitBytes, testLimit)

encLimit, err := scale.Marshal(&testLimitBytes)
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)
// since one key is removed, there will be two remaining.
require.Equal(t, 2, 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
91 changes: 91 additions & 0 deletions lib/runtime/wazero/imports.go
Original file line number Diff line number Diff line change
Expand Up @@ -1111,6 +1111,73 @@ func ext_default_child_storage_clear_prefix_version_1(
}
}

// NewDigestItem returns a new VaryingDataType to represent a DigestItem
func NewKillStorageResult(deleted uint32, allDeleted bool) scale.VaryingDataType {
killStorageResult := scale.MustNewVaryingDataType(new(noneRemain), new(someRemain))

var err error
if allDeleted {
err = killStorageResult.Set(noneRemain(deleted))
} else {
err = killStorageResult.Set(someRemain(deleted))
}

if err != nil {
panic(err)
}
return killStorageResult
}

//export ext_default_child_storage_clear_prefix_version_2
func ext_default_child_storage_clear_prefix_version_2(ctx context.Context, m api.Module,
childStorageKey, prefixSpan, limitSpan uint64) uint64 {

rtCtx := ctx.Value(runtimeContextKey).(*runtime.Context)
if rtCtx == nil {
panic("nil runtime context")
}
storage := rtCtx.Storage

keyToChild := read(m, childStorageKey)
prefix := read(m, prefixSpan)
limitBytes := read(m, limitSpan)

var limit []byte
err := scale.Unmarshal(limitBytes, &limit)
if err != nil {
logger.Warnf("failed scale decoding limit: %s", err)
panic(err)
}

if len(limit) == 0 {
// limit is None, set limit to max
limit = []byte{0xff, 0xff, 0xff, 0xff}
}

limitUint := binary.LittleEndian.Uint32(limit)

deleted, allDeleted, err := storage.ClearPrefixInChildWithLimit(
keyToChild, prefix, limitUint)
if err != nil {
logger.Errorf("failed to clear prefix in child with limit: %s", err)
}

killStorageResult := NewKillStorageResult(deleted, allDeleted)

encodedKillStorageResult, err := scale.Marshal(killStorageResult)
if err != nil {
logger.Errorf("failed to encode result: %s", err)
return 0
}

resultSpan, err := write(m, rtCtx.Allocator, scale.MustMarshal(&encodedKillStorageResult))
if err != nil {
panic(err)
}

return resultSpan
}

func ext_default_child_storage_exists_version_1(ctx context.Context, m api.Module, childStorageKey, key uint64) uint32 {
rtCtx := ctx.Value(runtimeContextKey).(*runtime.Context)
if rtCtx == nil {
Expand Down Expand Up @@ -1203,6 +1270,13 @@ func ext_default_child_storage_root_version_1(
return ret
}

//export ext_default_child_storage_root_version_2
func ext_default_child_storage_root_version_2(ctx context.Context, m api.Module, childStorageKey uint64,
stateVersion uint32) (ptrSize uint64) {
// TODO: Implement this after we have storage trie version 1 implemented #2418
return ext_default_child_storage_root_version_1(ctx, m, childStorageKey)
}

func ext_default_child_storage_storage_kill_version_1(ctx context.Context, m api.Module, childStorageKeySpan uint64) {
rtCtx := ctx.Value(runtimeContextKey).(*runtime.Context)
if rtCtx == nil {
Expand Down Expand Up @@ -1519,6 +1593,23 @@ func ext_offchain_index_set_version_1(ctx context.Context, m api.Module, keySpan
}
}

//export ext_offchain_index_clear_version_1
func ext_offchain_index_clear_version_1(ctx context.Context, m api.Module, keySpan uint64) {
// Remove a key and its associated value from the Offchain DB.
// https://github.com/paritytech/substrate/blob/4d608f9c42e8d70d835a748fa929e59a99497e90/primitives/io/src/lib.rs#L1213

rtCtx := ctx.Value(runtimeContextKey).(*runtime.Context)
if rtCtx == nil {
panic("nil runtime context")
}

storageKey := read(m, keySpan)
err := rtCtx.NodeStorage.BaseDB.Del(storageKey)
if err != nil {
logger.Errorf("failed to set value in raw storage: %s", err)
}
}

func ext_offchain_local_storage_clear_version_1(ctx context.Context, m api.Module, kind uint32, key uint64) {
rtCtx := ctx.Value(runtimeContextKey).(*runtime.Context)
if rtCtx == nil {
Expand Down
Loading
Loading