Skip to content

Commit

Permalink
fix(runtime/storage): root mismatch error (#3952)
Browse files Browse the repository at this point in the history
  • Loading branch information
jimjbrettj authored May 15, 2024
1 parent 6dcc6bb commit 30d7f27
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 31 deletions.
10 changes: 7 additions & 3 deletions lib/runtime/storage/trie.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,9 @@ func (t *TrieState) MustRoot() common.Hash {
// Root returns the trie's root hash
func (t *TrieState) Root() (common.Hash, error) {
// Since the Root function is called without running transactions we can do:
if currentTx := t.getCurrentTransaction(); currentTx != nil {
panic("cannot calculate root with running transactions")
}
return t.state.Hash()
}

Expand Down Expand Up @@ -181,7 +184,7 @@ func (t *TrieState) NextKey(key []byte) []byte {

for _, k := range keys {
if k > string(key) && !currentTx.deletes[k] {
return allEntries[k]
return []byte(k)
}
}
}
Expand Down Expand Up @@ -444,6 +447,7 @@ func (t *TrieState) GetChildNextKey(keyToChild, key []byte) ([]byte, error) {

if currentTx := t.getCurrentTransaction(); currentTx != nil {
// If we are going to delete this child we return error

if currentTx.deletes[string(keyToChild)] {
return nil, trie.ErrChildTrieDoesNotExist
}
Expand All @@ -466,7 +470,7 @@ func (t *TrieState) GetChildNextKey(keyToChild, key []byte) ([]byte, error) {

for _, k := range keys {
if k > string(key) && !childChanges.deletes[k] {
return allEntries[k], nil
return []byte(k), nil
}
}
return nil, nil
Expand Down Expand Up @@ -514,7 +518,7 @@ func (t *TrieState) GetKeysWithPrefixFromChild(keyToChild, prefix []byte) ([][]b

for _, k := range keys {
if bytes.HasPrefix([]byte(k), prefix) {
values = append(values, allEntries[k])
values = append(values, []byte(k))
}
}

Expand Down
70 changes: 44 additions & 26 deletions lib/runtime/storage/trie_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,24 @@ func TestTrieState_WithAndWithoutTransactions(t *testing.T) {
[]byte("key3"),
}

sortedValues := [][]byte{
[]byte("val1"),
[]byte("val2"),
[]byte("val3"),
[]byte("val4"),
[]byte("val5"),
[]byte("val6"),
}

sortedKeyToChild := [][]byte{
[]byte("ktc1"),
[]byte("ktc2"),
[]byte("ktc3"),
[]byte("ktc4"),
[]byte("ktc5"),
[]byte("ktc6"),
}

keyToChild := []byte("keytochild")

cases := map[string]struct {
Expand All @@ -44,46 +62,46 @@ func TestTrieState_WithAndWithoutTransactions(t *testing.T) {
}{
"set_get": {
changes: func(t *testing.T, ts *TrieState) {
for _, tc := range testCases {
err := ts.Put([]byte(tc), []byte(tc))
for i, tc := range testCases {
err := ts.Put([]byte(tc), sortedValues[i])
require.NoError(t, err)
}
},
checks: func(t *testing.T, ts *TrieState, _ bool) {
for _, tc := range testCases {
for i, tc := range testCases {
res := ts.Get([]byte(tc))
require.Equal(t, []byte(tc), res)
require.Equal(t, sortedValues[i], res)
}
},
},
"set_child_storage": {
changes: func(t *testing.T, ts *TrieState) {
for _, tc := range testCases {
err := ts.SetChildStorage([]byte(tc), []byte(tc), []byte(tc))
for i, tc := range testCases {
err := ts.SetChildStorage(sortedKeyToChild[i], []byte(tc), sortedValues[i])
require.NoError(t, err)
}
},
checks: func(t *testing.T, ts *TrieState, _ bool) {
for _, tc := range testCases {
res, err := ts.GetChildStorage([]byte(tc), []byte(tc))
for i, tc := range testCases {
res, err := ts.GetChildStorage(sortedKeyToChild[i], []byte(tc))
require.NoError(t, err)
require.Equal(t, []byte(tc), res)
require.Equal(t, sortedValues[i], res)
}
},
},
"set_and_clear_from_child": {
changes: func(t *testing.T, ts *TrieState) {
for _, tc := range testCases {
err := ts.SetChildStorage([]byte(tc), []byte(tc), []byte(tc))
for i, tc := range testCases {
err := ts.SetChildStorage(sortedKeyToChild[i], []byte(tc), sortedValues[i])
require.NoError(t, err)
}
},
checks: func(t *testing.T, ts *TrieState, isTransactionRunning bool) {
for _, tc := range testCases {
err := ts.ClearChildStorage([]byte(tc), []byte(tc))
for i, tc := range testCases {
err := ts.ClearChildStorage(sortedKeyToChild[i], []byte(tc))
require.NoError(t, err)

val, err := ts.GetChildStorage([]byte(tc), []byte(tc))
val, err := ts.GetChildStorage(sortedKeyToChild[i], []byte(tc))

require.Nil(t, val)

Expand All @@ -97,8 +115,8 @@ func TestTrieState_WithAndWithoutTransactions(t *testing.T) {
},
"delete": {
changes: func(t *testing.T, ts *TrieState) {
for _, tc := range testCases {
ts.Put([]byte(tc), []byte(tc))
for i, tc := range testCases {
ts.Put([]byte(tc), sortedValues[i])
}
},
checks: func(t *testing.T, ts *TrieState, _ bool) {
Expand All @@ -109,8 +127,8 @@ func TestTrieState_WithAndWithoutTransactions(t *testing.T) {
},
"delete_child": {
changes: func(t *testing.T, ts *TrieState) {
for _, tc := range prefixedKeys {
ts.SetChildStorage(keyToChild, tc, tc)
for i, tc := range prefixedKeys {
ts.SetChildStorage(keyToChild, tc, sortedValues[i])
}
},
checks: func(t *testing.T, ts *TrieState, _ bool) {
Expand Down Expand Up @@ -268,8 +286,8 @@ func TestTrieState_WithAndWithoutTransactions(t *testing.T) {
},
"next_key": {
changes: func(t *testing.T, ts *TrieState) {
for _, tc := range sortedKeys {
err := ts.Put(tc, tc)
for i, tc := range sortedKeys {
err := ts.Put(tc, sortedValues[i])
require.Nil(t, err)
}
},
Expand All @@ -286,8 +304,8 @@ func TestTrieState_WithAndWithoutTransactions(t *testing.T) {
},
"child_next_key": {
changes: func(t *testing.T, ts *TrieState) {
for _, tc := range sortedKeys {
err := ts.SetChildStorage(keyToChild, tc, tc)
for i, tc := range sortedKeys {
err := ts.SetChildStorage(keyToChild, tc, sortedValues[i])
require.Nil(t, err)
}
},
Expand All @@ -306,8 +324,8 @@ func TestTrieState_WithAndWithoutTransactions(t *testing.T) {
},
"entries": {
changes: func(t *testing.T, ts *TrieState) {
for _, tc := range testCases {
err := ts.Put([]byte(tc), []byte(tc))
for i, tc := range testCases {
err := ts.Put([]byte(tc), sortedValues[i])
require.Nil(t, err)
}
},
Expand All @@ -322,8 +340,8 @@ func TestTrieState_WithAndWithoutTransactions(t *testing.T) {
},
"get_keys_with_prefix_from_child": {
changes: func(t *testing.T, ts *TrieState) {
for _, tc := range prefixedKeys {
err := ts.SetChildStorage(keyToChild, tc, tc)
for i, tc := range prefixedKeys {
err := ts.SetChildStorage(keyToChild, tc, sortedValues[i])
require.Nil(t, err)
}
},
Expand Down
1 change: 1 addition & 0 deletions lib/runtime/test_data/paseo/block1008648.out

Large diffs are not rendered by default.

67 changes: 65 additions & 2 deletions lib/runtime/wazero/instance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@
package wazero_runtime

import (
_ "embed"

"bytes"
"encoding/json"
"math/big"
"os"
"path/filepath"
"testing"

"github.com/ChainSafe/gossamer/dot/network"
"github.com/ChainSafe/gossamer/dot/types"
"github.com/ChainSafe/gossamer/internal/database"
"github.com/ChainSafe/gossamer/internal/log"
"github.com/ChainSafe/gossamer/lib/common"
"github.com/ChainSafe/gossamer/lib/crypto/ed25519"
Expand Down Expand Up @@ -52,6 +52,28 @@ func genesisFromRawJSON(t *testing.T, jsonFilepath string) (gen genesis.Genesis)
return gen
}

func newTrieFromKeyValueList(t *testing.T, filename string) trie.Trie {
data, err := os.ReadFile(filename)
require.NoError(t, err)

stateTrieResponse := make([]string, 0)
err = json.Unmarshal(data, &stateTrieResponse)
require.NoError(t, err)

entries := make(map[string]string)
for _, encEntry := range stateTrieResponse {
entry := new(trie.Entry)
err := scale.Unmarshal(common.MustHexToBytes(encEntry), entry)
require.NoError(t, err)

entries[common.BytesToHex(entry.Key)] = common.BytesToHex(entry.Value)
}

tr, err := inmemory_trie.LoadFromMap(entries, trie.V0)
require.NoError(t, err)
return tr
}

// this is generated by printing key ownership proof while running `test_generate_equivocation_report_blob`
// https://github.com/paritytech/substrate/blob/ded44948e2d5a398abcb4e342b0513cb690961bb/frame/grandpa/src/benchmarking.rs#L85
var testKeyOwnershipProof types.OpaqueKeyOwnershipProof = types.OpaqueKeyOwnershipProof([]byte{64, 138, 252, 29, 127, 102, 189, 129, 207, 47, 157, 60, 17, 138, 194, 121, 139, 92, 176, 175, 224, 16, 185, 93, 175, 251, 224, 81, 209, 61, 0, 71}) //nolint:lll
Expand Down Expand Up @@ -954,6 +976,47 @@ func TestInstance_ExecuteBlock_KusamaRuntime_KusamaBlock1482003(t *testing.T) {
require.NoError(t, err)
}

func TestInstance_ExecuteBlock_PaseoRuntime_PaseoBlock1008649(t *testing.T) {
paseoTrie := newTrieFromKeyValueList(t, "../test_data/paseo/block1008648.out")
expectedRoot := common.MustHexToHash("0x4fbffb5810aca70ff9e3d6560f496ca3b3beef098ab76d18e48283ec7c8ffa9b")
require.Equal(t, expectedRoot, trie.V0.MustHash(paseoTrie))

// set state to genesis state
state := storage.NewTrieState(paseoTrie)

db, err := database.NewPebble("", true)
require.NoError(t, err)

cfg := Config{
Storage: state,
LogLvl: log.Critical,
NodeStorage: runtime.NodeStorage{
LocalStorage: db,
PersistentStorage: db,
BaseDB: db,
},
}

instance, err := NewInstanceFromTrie(paseoTrie, cfg)
require.NoError(t, err)

blockResponse := &network.BlockResponseMessage{}
err = blockResponse.Decode(common.MustHexToBytes("0x0ab81d0a202dcc0a3c3d4953da49e0006da24c7617a55e2e169dcf59aacc5fbd5856b14fb312c702c154fd69bab5b5676a746c32868b053980a53bb0aaae89fc47d5408056883eb826903d00c75b6a15438acb997f925a09714092fc463af3ba44ab93654c89b775c44dfe13dd89f50472e232a4cff46ce80cfa07fa8777a7acfc2c40a52e7f23b481a2a1b60c0642414245b501030c000000e02c081100000000a4acb383795247df8efcf162903c35af1d09774e83def72e149382695048073868402c77fbb9b7c54c842910999327853c735df23083fd26bd3d2a4a3c934607a875840cddf647d6ab39189d11b07a0e423645e80e0c681ebffc54926a2d0f0a0442454546840318e81c2a62d4f79164052d2969cff6ddfcc35c6fd5bbac6bd5af7e66c64d7d32054241424501017a1cf0a7fc757c3e6cebf7b937ec6bceb42239be3cab62c2a6fec237d00f380a73bcc611b3b07533378e79e47a8b9553f438763e39358f45b09247bbf949f1881a0b280403000b00c29b2f8f011abc1ae934043600ac24e5010000000042f2d3629bcee331ba3e797da9bc8f7b114c56cf5879916f0a5ccaacbbcfcd33c0d66483f41f0aee1c18722313e9e634bd6f443dd420c63e7c8c0fa02a2a4e8824e70101000000d63256338b66d3d86dce4341858efc26160dc11f40885d7ba574b2cbdbb05e3cb80d46137b91740b84a8989e792c276101d6821b8258b50c28f53209db9d588424e50102000000d886d2c0a6a7b7d91d31cd79d6395a9314ce484ef1aab5a77a91683ba0611919551c2dbc369ba092e28b84d2d54e88ae2dd060fec60a9c0051adba0233df348224e70103000000aa4acbcf105d03e73ab393aa6227c5bba1c65c880be240b98175c21d71934d0e6b2763ec6af35d403764a56a4ce6cad623978141cd065fb9f89f3c4e2913fd8c24e70104000000ec43f63380097da3ffd0453684f3da6a3c26d6dd09ec76c06ec16d109f0c2b3b7d78226c2ef6279ffaa1bba644a82575830156804e425a36817e16bcc453888024e50105000000a63a6b5385a3b5991f259872608e5adbe41aa41835c72c200457cb61919d393c222e41187cda30be8d7f8661ea9ae1d6310b2f19bcbda51815bae6b12b088b8c24e501060000006a80b176d88acb02140385f788e128e4a67f97848074dde691d674d8e8cb191d1b2994d37d17d4bee2bcc523832b98fc3f4227e1ddc54cf4dcfdd41ade07e38824e50107000000020532162b48a2be92028352a84523d07036e767430e1dde4aec92efa64e155e74f7fbbb92ec95db26f115950a877efab4a1a30df074cf59c7cc244d950f1b8e24e501080000006c1864b2125754ffddfe8d6f60df4f4f342fda159a9b7ac407b34dfd17c9a532287ff9011bd9d587d98bb0543bb8b92ea854f1385672c76c733c54cea0bb498924e50109000000ec340ccf10147706d756441b1648ddb8b94b119e10f2996682c27d612df5d547d7506c11490121ae784f9306655425b6e81f4cba0fb8e43b2892b2cf4588a68f24e5010a000000f4d3ecd0f7c4b61d01ecbeb4bb06df50e483d57dfd7eeced80baf6d8ce9d2b0c594457e8b2c29a55558091bdb5632b535def00b9d3171b77ce4348037866b08724e7010b000000ce2cba80c37ae22e7b18bad4955949ab3bf897a56b10962c03cd13374a09f15342b14e8f9f89610326f4591ad8cd85d26b31128f8a5fb734991242ecb24f728824e5010c000000ec431a72f9684c632c21eba1798c25a7f50a20323a82288e3b8b70fcec65941acc3dcf8794966948aa288929f2857d1c6bb6ace040d76428f1aeed86992c688124e5010d000000383d447003f376f260094e480901c952f9e6f7898963f0ea9f2c182bcf970b38807310db0bb96bda55bbf2aa308e39c40f9216a01661ee7f285143ccf9692f8824e5010e0000007a7d6c9dca3c7fa7ae893defbccd66b4667c6bbfc343c9d28a82107df2b4774ecd1825a31f4ebd10c1ef99be9bd3a2ea1e82c9d5498ed9d74cf48dffe388b98324e7010f00000024aa634d5fe7eeee763b99bd866ebebe200276324725e0fd4805ef8c8ea12e0dfd31821e04853e387ad5e0d0784dbb8fceabda017c18708cad4b1deacd39a58b24e701100000006edb6f3c9d893bbb12ef1d96cbb6b2c0056c85efcc2209b946a6493c875b671db57fa876570554e9e2125aaddfb78329c5904c3a395f50c93172cb6439529c8424e501110000004a0a3ba9408d28223b760c0c45858d07c40a73b9d6d571ab556c03687404c330c812e899eb55aaafab0f433caa8b1672a654aad12547f515096362b62b01ed8b24e501120000003cb4aebfd1885088d9b8fa5fffc03d7c0534ad854be79c4b79c312c64f8c5b57ca31bcb5d187074357c1141b860d76a6b8265234b48cb1f35fbcb3907bcb6b8b24e701130000006caab829a264c4111a3d5c25adf7a453beaf10ec1808e9e56781666fb4a8947c3dddebe5546c2659136d9b54cb7effb86e7fb674c6ec6ea1b0be58e424426a8e24e701140000006acbb3e3747dd3cc8da8b799825ac9acdda50f06a50683a78d98fa0974fc0c7d9466a23685dfb7e4beaa5f3d3ca823d59a418f69a576df97dc3daf7eb1186c8624e70115000000f4676fdc3ef1fd8e9335ee39037e259bfe47d3c44c281f1e65e24c8754f99433735a16bbf461f1caddefb2936217785db25f41ef0d2434e18ee24025a315058224e70116000000d00612c21d6b56d069bba06f93c4abba858ee451b77c54c97be96b425a82eb31813e55c7dbca3194189ade96148964a90c2da6a3c8bc6790bbaaa4e6e351098824e701170000009c6b8cc879a2392e7d355c1fe36e7e935ca34bdef19c8260cb0a05585ed13b6ea02c8a8309807dbdaeccc722d4857000f9d0dd924d02562a7ab116ea34fe4b8a24e50118000000e2cab7cd19724ca5dd6d8451e2029422b7182ab865a41a7424b92058602cac45fe1b3b683cd904f66bb3a30f0cda65c28848dffbb861b4734e6195a25d15ba8724e70119000000ccb1fc1afac473206a141c1ae6b396009b1c9b18e958dbd2ff3ecdcf49de800a2b49d32e21f4b48ee612bea0e2c79a4033832bfbac8301e47b7466d0ce8d6c8724e7011a000000c29e7059a38a50559fc8b788eb39b4222427fc8505bce5e9ec909b32317801015e6603dc610cd4980d53682b44954dc882ec1b2410e6ee62396f08f320b3468c24e7011b00000028ecbd92be9f51dcfeaea071e13972e510b78dfecdc378e381be8ffb9ff0697e3b8487a9aa611d251cfc8135b4bcfe97c62870a887ec0532356b869953020a8e24e5011c000000623b8905d098e1be6b3e8a27f7c9d166d57276981447829f76b827a3ce092e560f459ace810b0e5fb56b67c3081afd36fc30d4d1f5a01a3319f6333ea7593e8e24e5011d00000074a390dec4d88662d690e7ac19e109c6bfc05d48462918e60f31f07415925d4987e9760e07e29b726365ccacb5ddbb226e8817b85b298d39c84c79db67df6c8124e7011e00000046111b603d728bd7ccc2d9f61268c0b24b7f709001fd2dc5389a74797021b240d2c71165f106fc0168be101c05f594d73b7f621bbf201c514d042ead4477b28624e7011f000000d0c54ec78326b880088b86fc2be9f189299d1a8a7b2ea7ec08d5ef7c57ba83074f1342900dc808b7f63223d1274d018e1c5ca2c1ec1a5d0402c6d0a8f7f3148324e70120000000947bb9cc6c1ed2c88ba722fcc863e4e5858adefe47eead172883ba23b46e4d031c1e0ccf6ee8417d93e1ec4897c34efc4a5f4f5e5a0858e7176c284a0f915e8c24e5012100000026637a5c715a6b9811b6fe0ed40ad9c682a67c6331c4ce64fb3433c976b80c3365aa283ba85e5789a010617da58b28d52378bde135506250fde6b35f2a6efe8724e7012200000084c8c3bc09469d8ce5ff553cdaeea7d5872c5b8ba2f0e60f17e8e072a2545e5ecda680e8c268284def421a166b1b3523450e69206662891c0dbf11c9443e0c8b24e70123000000c044e668ce3522a731adaecce85ebfcbe529801f8ce1a4076ddf27e0cf732d6ace24d0d4375bb3d3516069a6e347e33ea81fad337928f7f06d03bacb1c08688624e7012400000080f81db560c9ea46e6659f7f539c73e1be80e635f5fc79854525f6f6883c600198447d9b235746003dfcf0f2b683a377b91eeb4343b992c487796e846b727f8324e701250000009008cfae49b4a3157c30bbff6e32e1efa473d71bb6d101fb1e0edcde618e3063b07a0aedd3f7624fd71cec63f81de3d0d837f9d72edf6282cf6e234875dda18024e701260000007806a8eade36a4f7bb824a8ed3ee19e64411caf315fbcdeb4c709afaf81a106d504e713b4356c7dff25d00d07f65c7847c12e9843876c24033a5d23e6c40c480240200270000008a9428f41d3fdd6413602758a5b8658e4f40a2b26fa296ef7c38514b305f337aeca2686aff49e656c2f3dd676a9b2c0b5ff65c5101ab8209ed18e4d9c2d7348b24e50128000000d8e58bf175f49e98da8e49d3dfa1539fbb1a0cea98c47a40d949095385504623e20a60aeef90fce21e6428e48b481cf452b4e47acc1d7bd9d42ffdb885133e8924e50129000000ce093b859e143eb44826888c17c6dc8456543668b0637958ae6f962d1638a02093039cff3950172663f645f6c1db5e503579cc0905c4e924c6a35fbb26a0d08b24e7012a0000005e31978a3e353144f41818892d2b664d9fc2454d4b9db3923281228ceee87c453d9b3239488681c038e60356a30d8d558c0254561f3843c5b492aabc6623bc870000bc2924e71e51b4ebc3657e523f90ce68cce6d0d56cb50ff350e9e3a40762c9e822903d004fbffb5810aca70ff9e3d6560f496ca3b3beef098ab76d18e48283ec7c8ffa9b340eeeb98f740c3ab0241a51890e0e178ebc5633dd547ab1e7c89130a269507a0c0642414245b5010319000000df2c0811000000005ef4b3ef782dcf55f6908627668e7765b501877fc237cffe5b7fa4e7b1f16323662e712d38fe8b4cde838be86103551014cd55fcc201a580836de920e2afaa0b095f61f4b7a25e7a0b0f78f437b97ab00117c6baa0abccd353a6260be0637a030442454546840388660a70d906e4ff831f89296abeb9efa4bfc4b794a87c4ca2ecf65b6f52b97805424142450101d4748ed493c413ccfe6c4a08ef0dfbab6bb763ceeb6d552e72213fefe0ee9538ce2bd597a112c32b88d908ac07e38951996033344989bd8808d2406e9454318d")) //nolint:lll
require.NoError(t, err)

block := &types.Block{
Header: *blockResponse.BlockData[0].Header,
Body: *blockResponse.BlockData[0].Body,
}

_, err = instance.ExecuteBlock(block)
require.NoError(t, err)

expectedRootNew := common.MustHexToHash("0xc75b6a15438acb997f925a09714092fc463af3ba44ab93654c89b775c44dfe13")
require.Equal(t, expectedRootNew, state.MustRoot())

}

func TestInstance_ExecuteBlock_PolkadotBlock1089328(t *testing.T) {
dotTrie := newTrieFromPairs(t, "../test_data/polkadot/block1089327.json")
expectedRoot := common.MustHexToHash("0x87ed9ebe7fb645d3b5b0255cc16e78ed022d9fbb52486105436e15a74557535b")
Expand Down

0 comments on commit 30d7f27

Please sign in to comment.