Skip to content
New issue

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

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

Already on GitHub? # to your account

fix(lib/trie): create an empty child trie if not found #3459

Merged
merged 10 commits into from
Aug 30, 2023
38 changes: 26 additions & 12 deletions dot/rpc/modules/state_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,19 +105,30 @@ func TestStateModule_GetPairs(t *testing.T) {
randomHash, err := common.HexToHash(RandomHash)
require.NoError(t, err)

hexEncode := func(s string) string {
return "0x" + hex.EncodeToString([]byte(s))
}

testCases := []struct {
params []string
expected []interface{}
errMsg string
}{
{params: []string{"0x00"}, expected: nil},
{params: []string{""}, expected: []interface{}{[]string{":key1", "value1"}, []string{":key2", "value2"}}},
{params: []string{":key1"}, expected: []interface{}{[]string{":key1", "value1"}}},
{params: []string{""}, expected: []interface{}{
[]string{hexEncode(":child_storage:default::child1"),
"0x8f733acc98dff0e6527f97e2a87e4834cd8b2e601f56fb003084e9d43183d7ff"},
[]string{hexEncode(":key1"), hexEncode("value1")},
[]string{hexEncode(":key2"), hexEncode("value2")}}},
{params: []string{hexEncode(":key1")}, expected: []interface{}{[]string{hexEncode(":key1"), hexEncode("value1")}}},
{params: []string{"0x00", hash.String()}, expected: nil},
{params: []string{"", hash.String()}, expected: []interface{}{
[]string{":key1", "value1"},
[]string{":key2", "value2"}}},
{params: []string{":key1", hash.String()}, expected: []interface{}{[]string{":key1", "value1"}}},
[]string{hexEncode(":child_storage:default::child1"),
"0x8f733acc98dff0e6527f97e2a87e4834cd8b2e601f56fb003084e9d43183d7ff"},
[]string{hexEncode(":key1"), hexEncode("value1")},
[]string{hexEncode(":key2"), hexEncode("value2")}}},
{params: []string{hexEncode(":key1"), hash.String()},
expected: []interface{}{[]string{hexEncode(":key1"), hexEncode("value1")}}},
{params: []string{"", randomHash.String()}, errMsg: "pebble: not found"},
}

Expand All @@ -134,6 +145,7 @@ func TestStateModule_GetPairs(t *testing.T) {

if len(test.params) > 1 && test.params[1] != "" {
req.Bhash = &common.Hash{}
var err error
*req.Bhash, err = common.HexToHash(test.params[1])
require.NoError(t, err)
}
Expand All @@ -160,10 +172,7 @@ func TestStateModule_GetPairs(t *testing.T) {

// Convert human-readable result value to hex.
expectedKV, _ := val.([]string)
expectedKey := "0x" + hex.EncodeToString([]byte(expectedKV[0]))
expectedVal := "0x" + hex.EncodeToString([]byte(expectedKV[1]))

require.Equal(t, []string{expectedKey, expectedVal}, kv)
require.Equal(t, []string{expectedKV[0], expectedKV[1]}, kv)
}
})
}
Expand Down Expand Up @@ -551,9 +560,14 @@ func setupStateModule(t *testing.T) (*StateModule, *common.Hash, *common.Hash) {
ts, err := chain.Storage.TrieState(nil)
require.NoError(t, err)

ts.Put([]byte(`:key2`), []byte(`value2`))
ts.Put([]byte(`:key1`), []byte(`value1`))
ts.SetChildStorage([]byte(`:child1`), []byte(`:key1`), []byte(`:childValue1`))
err = ts.Put([]byte(`:key2`), []byte(`value2`))
require.NoError(t, err)

err = ts.Put([]byte(`:key1`), []byte(`value1`))
require.NoError(t, err)

err = ts.SetChildStorage([]byte(`:child1`), []byte(`:key1`), []byte(`:childValue1`))
require.NoError(t, err)

sr1, err := ts.Root()
require.NoError(t, err)
Expand Down
5 changes: 3 additions & 2 deletions lib/runtime/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ const (
// 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/raw/master/test/" +
"runtimes/hostapi/hostapi_runtime.compact.wasm"
HOST_API_TEST_RUNTIME_URL = "https://github.com/ChainSafe/polkadot-spec/tree" +
"/b50f72fd57c563da7183e7768d348933e80c3106" +
"/test/runtimes/hostapi/hostapi_runtime.compact.wasm"

// v0.9.29 polkadot
POLKADOT_RUNTIME_v0929 = "polkadot_runtime-v929"
Expand Down
94 changes: 76 additions & 18 deletions lib/runtime/wazero/imports_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -812,31 +812,89 @@ func Test_ext_default_child_storage_read_version_1(t *testing.T) {
}

func Test_ext_default_child_storage_set_version_1(t *testing.T) {
inst := NewTestInstance(t, runtime.HOST_API_TEST_RUNTIME)
cases := map[string]struct {
setupInstance func(*testing.T) *Instance
existsBeforehand bool
}{
"child_trie_exists_should_not_panic": {
existsBeforehand: true,
setupInstance: func(t *testing.T) *Instance {
inst := NewTestInstance(t, runtime.HOST_API_TEST_RUNTIME)

err := inst.Context.Storage.SetChild(testChildKey, trie.NewEmptyTrie())
require.NoError(t, err)
err := inst.Context.Storage.SetChild(testChildKey, trie.NewEmptyTrie())
require.NoError(t, err)

// Check if value is not set
val, err := inst.Context.Storage.GetChildStorage(testChildKey, testKey)
require.NoError(t, err)
require.Nil(t, val)
return inst
},
},
"child_trie_not_found_should_create_a_empty_one": {
existsBeforehand: false,
setupInstance: func(t *testing.T) *Instance {
inst := NewTestInstance(t, runtime.HOST_API_TEST_RUNTIME)
return inst
},
},
}

encChildKey, err := scale.Marshal(testChildKey)
require.NoError(t, err)
insertKeyAndValue := func(t *testing.T, inst *Instance, childKey, key, value []byte) {
encChildKey, err := scale.Marshal(childKey)
require.NoError(t, err)

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

encVal, err := scale.Marshal(testValue)
require.NoError(t, err)
encVal, err := scale.Marshal(value)
require.NoError(t, err)

_, err = inst.Exec("rtm_ext_default_child_storage_set_version_1", append(append(encChildKey, encKey...), encVal...))
require.NoError(t, err)
args := bytes.Join([][]byte{
encChildKey, encKey, encVal,
}, nil)

val, err = inst.Context.Storage.GetChildStorage(testChildKey, testKey)
require.NoError(t, err)
require.Equal(t, testValue, val)
_, err = inst.Exec("rtm_ext_default_child_storage_set_version_1", args)
require.NoError(t, err)
}

getValueFromChildStorage := func(t *testing.T, inst *Instance, childKey, key []byte) *[]byte {
encChildKey, err := scale.Marshal(childKey)
require.NoError(t, err)

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

ret, err := inst.Exec("rtm_ext_default_child_storage_get_version_1", append(encChildKey, encKey...))
require.NoError(t, err)

var retrieved *[]byte
err = scale.Unmarshal(ret, &retrieved)
require.NoError(t, err)

return retrieved
}

for tname, tt := range cases {
tt := tt

t.Run(tname, func(t *testing.T) {
inst := tt.setupInstance(t)

exampleChildKey := []byte("example_child_key")
exampleKey := []byte("key_to_account")
exampleValue := []byte("some_acc_address")

insertKeyAndValue(t, inst, exampleChildKey, exampleKey, exampleValue)

anotherKey := []byte("key_to_account_2")
anotherValue := []byte("some_acc_address_2")
insertKeyAndValue(t, inst, exampleChildKey, anotherKey, anotherValue)

// should be possible to retrieve the first address and the new inserted one
acc1 := getValueFromChildStorage(t, inst, exampleChildKey, exampleKey)
require.Equal(t, &exampleValue, acc1)

acc2 := getValueFromChildStorage(t, inst, exampleChildKey, anotherKey)
require.Equal(t, &anotherValue, acc2)
})
}
}

func Test_ext_default_child_storage_clear_version_1(t *testing.T) {
Expand Down
13 changes: 5 additions & 8 deletions lib/trie/child_storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,11 @@ func (t *Trie) GetChild(keyToChild []byte) (*Trie, error) {
func (t *Trie) PutIntoChild(keyToChild, key, value []byte) error {
child, err := t.GetChild(keyToChild)
if err != nil {
return err
if errors.Is(err, ErrChildTrieDoesNotExist) {
child = NewEmptyTrie()
} else {
return fmt.Errorf("getting child: %w", err)
}
}

origChildHash, err := child.Hash()
Expand All @@ -68,14 +72,7 @@ func (t *Trie) PutIntoChild(keyToChild, key, value []byte) error {
return fmt.Errorf("putting into child trie located at key 0x%x: %w", keyToChild, err)
}

childHash, err := child.Hash()
if err != nil {
return err
}

delete(t.childTries, origChildHash)
t.childTries[childHash] = child

return t.SetChild(keyToChild, child)
}

Expand Down