Skip to content

Commit 7cd4118

Browse files
fix(lib/trie): Make sure writing and reading a trie to disk gives the same trie and cover more store/load child trie related test cases (#2302)
* test TestGetStorageChildAndGetStorageFromChild with non-empty trie * test store and load of a trie that hash child tries * tackle the case when encoding and hash is same, i.e., encoding is less than 32 bits * don't put childTrie again inside the trie
1 parent e6098ea commit 7cd4118

File tree

8 files changed

+106
-45
lines changed

8 files changed

+106
-45
lines changed

dot/state/storage.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ func (s *StorageState) loadTrie(root *common.Hash) (*trie.Trie, error) {
167167

168168
tr, err := s.LoadFromDB(*root)
169169
if err != nil {
170-
return nil, errTrieDoesNotExist(*root)
170+
return nil, fmt.Errorf("trie does not exist at root %s: %w", *root, err)
171171
}
172172

173173
return tr, nil

dot/state/storage_test.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"github.com/ChainSafe/gossamer/dot/state/pruner"
1111
"github.com/ChainSafe/gossamer/dot/telemetry"
1212
"github.com/ChainSafe/gossamer/dot/types"
13+
"github.com/ChainSafe/gossamer/internal/trie/node"
1314
"github.com/ChainSafe/gossamer/lib/common"
1415
"github.com/ChainSafe/gossamer/lib/genesis"
1516
runtime "github.com/ChainSafe/gossamer/lib/runtime/storage"
@@ -179,7 +180,8 @@ func TestGetStorageChildAndGetStorageFromChild(t *testing.T) {
179180
"0",
180181
))
181182

182-
testChildTrie := trie.NewEmptyTrie()
183+
testChildTrie := trie.NewTrie(node.NewLeaf([]byte{1, 2}, []byte{3, 4}, true, 0))
184+
183185
testChildTrie.Put([]byte("keyInsidechild"), []byte("voila"))
184186

185187
err = genTrie.PutChild([]byte("keyToChild"), testChildTrie)

internal/trie/node/leaf.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ type Leaf struct {
1616
// Partial key bytes in nibbles (0 to f in hexadecimal)
1717
Key []byte
1818
Value []byte
19-
// Dirty is true when the branch differs
19+
// Dirty is true when the leaf differs
2020
// from the node stored in the database.
2121
Dirty bool
2222
HashDigest []byte

lib/trie/child_storage.go

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,16 @@ var ChildStorageKeyPrefix = []byte(":child_storage:default:")
1616
var ErrChildTrieDoesNotExist = errors.New("child trie does not exist")
1717

1818
// PutChild inserts a child trie into the main trie at key :child_storage:[keyToChild]
19+
// A child trie is added as a node (K, V) in the main trie. K is the child storage key
20+
// associated to the child trie, and V is the root hash of the child trie.
1921
func (t *Trie) PutChild(keyToChild []byte, child *Trie) error {
2022
childHash, err := child.Hash()
2123
if err != nil {
2224
return err
2325
}
24-
2526
key := append(ChildStorageKeyPrefix, keyToChild...)
26-
value := [32]byte(childHash)
2727

28-
t.Put(key, value[:])
28+
t.Put(key, childHash.ToBytes())
2929
t.childTries[childHash] = child
3030
return nil
3131
}
@@ -38,9 +38,7 @@ func (t *Trie) GetChild(keyToChild []byte) (*Trie, error) {
3838
return nil, fmt.Errorf("%w at key 0x%x%x", ErrChildTrieDoesNotExist, ChildStorageKeyPrefix, keyToChild)
3939
}
4040

41-
hash := [32]byte{}
42-
copy(hash[:], childHash)
43-
return t.childTries[common.Hash(hash)], nil
41+
return t.childTries[common.BytesToHash(childHash)], nil
4442
}
4543

4644
// PutIntoChild puts a key-value pair into the child trie located in the main trie at key :child_storage:[keyToChild]

lib/trie/database.go

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -150,8 +150,7 @@ func (t *Trie) Load(db chaindb.Database, rootHash common.Hash) error {
150150
t.root = nil
151151
return nil
152152
}
153-
154-
rootHashBytes := rootHash[:]
153+
rootHashBytes := rootHash.ToBytes()
155154

156155
encodedNode, err := db.Get(rootHashBytes)
157156
if err != nil {
@@ -163,6 +162,7 @@ func (t *Trie) Load(db chaindb.Database, rootHash common.Hash) error {
163162
if err != nil {
164163
return fmt.Errorf("cannot decode root node: %w", err)
165164
}
165+
166166
t.root = root
167167
t.root.SetDirty(false)
168168
t.root.SetEncodingAndHash(encodedNode, rootHashBytes)
@@ -185,6 +185,7 @@ func (t *Trie) load(db chaindb.Database, n Node) error {
185185
}
186186

187187
hash := child.GetHash()
188+
188189
encodedNode, err := db.Get(hash)
189190
if err != nil {
190191
return fmt.Errorf("cannot find child node key 0x%x in database: %w", hash, err)
@@ -209,16 +210,17 @@ func (t *Trie) load(db chaindb.Database, n Node) error {
209210
for _, key := range t.GetKeysWithPrefix(ChildStorageKeyPrefix) {
210211
childTrie := NewEmptyTrie()
211212
value := t.Get(key)
212-
err := childTrie.Load(db, common.NewHash(value))
213+
rootHash := common.BytesToHash(value)
214+
err := childTrie.Load(db, rootHash)
213215
if err != nil {
214-
return fmt.Errorf("failed to load child trie with root hash=0x%x: %w", value, err)
216+
return fmt.Errorf("failed to load child trie with root hash=%s: %w", rootHash, err)
215217
}
216218

217-
err = t.PutChild(value, childTrie)
219+
hash, err := childTrie.Hash()
218220
if err != nil {
219-
return fmt.Errorf("failed to insert child trie with root hash=0x%x into main trie: %w",
220-
childTrie.root.GetHash(), err)
221+
return fmt.Errorf("cannot hash chilld trie at key 0x%x: %w", key, err)
221222
}
223+
t.childTries[hash] = childTrie
222224
}
223225

224226
return nil

lib/trie/database_test.go

Lines changed: 66 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"testing"
1010

1111
"github.com/ChainSafe/chaindb"
12+
"github.com/ChainSafe/gossamer/internal/trie/node"
1213
"github.com/ChainSafe/gossamer/lib/utils"
1314
"github.com/stretchr/testify/require"
1415
)
@@ -21,7 +22,7 @@ func newTestDB(t *testing.T) chaindb.Database {
2122
}
2223

2324
func TestTrie_DatabaseStoreAndLoad(t *testing.T) {
24-
cases := [][]Test{
25+
cases := [][]keyValues{
2526
{
2627
{key: []byte{0x01, 0x35}, value: []byte("pen")},
2728
{key: []byte{0x01, 0x35, 0x79}, value: []byte("penguin")},
@@ -65,7 +66,7 @@ func TestTrie_DatabaseStoreAndLoad(t *testing.T) {
6566
res := NewEmptyTrie()
6667
err = res.Load(db, trie.MustHash())
6768
require.NoError(t, err)
68-
require.Equal(t, trie.MustHash(), res.MustHash())
69+
require.Equal(t, trie.String(), res.String())
6970

7071
for _, test := range testCase {
7172
val, err := GetFromDB(db, trie.MustHash(), test.key)
@@ -76,7 +77,7 @@ func TestTrie_DatabaseStoreAndLoad(t *testing.T) {
7677
}
7778

7879
func TestTrie_WriteDirty_Put(t *testing.T) {
79-
cases := [][]Test{
80+
cases := [][]keyValues{
8081
{
8182
{key: []byte{0x01, 0x35}, value: []byte("pen")},
8283
{key: []byte{0x01, 0x35, 0x79}, value: []byte("penguin")},
@@ -154,7 +155,7 @@ func TestTrie_WriteDirty_Put(t *testing.T) {
154155
}
155156

156157
func TestTrie_WriteDirty_PutReplace(t *testing.T) {
157-
cases := [][]Test{
158+
cases := [][]keyValues{
158159
{
159160
{key: []byte{0x01, 0x35}, value: []byte("pen")},
160161
{key: []byte{0x01, 0x35, 0x79}, value: []byte("penguin")},
@@ -217,7 +218,7 @@ func TestTrie_WriteDirty_PutReplace(t *testing.T) {
217218
}
218219

219220
func TestTrie_WriteDirty_Delete(t *testing.T) {
220-
cases := [][]Test{
221+
cases := [][]keyValues{
221222
{
222223
{key: []byte{0x01, 0x35}, value: []byte("pen")},
223224
{key: []byte{0x01, 0x35, 0x79}, value: []byte("penguin")},
@@ -284,7 +285,7 @@ func TestTrie_WriteDirty_Delete(t *testing.T) {
284285
}
285286

286287
func TestTrie_WriteDirty_ClearPrefix(t *testing.T) {
287-
cases := [][]Test{
288+
cases := [][]keyValues{
288289
{
289290
{key: []byte{0x01, 0x35}, value: []byte("pen")},
290291
{key: []byte{0x01, 0x35, 0x79}, value: []byte("penguin")},
@@ -337,7 +338,7 @@ func TestTrie_WriteDirty_ClearPrefix(t *testing.T) {
337338
}
338339

339340
func TestTrie_GetFromDB(t *testing.T) {
340-
cases := [][]Test{
341+
cases := [][]keyValues{
341342
{
342343
{key: []byte{0x01, 0x35}, value: []byte("pen")},
343344
{key: []byte{0x01, 0x35, 0x79}, value: []byte("penguin")},
@@ -387,3 +388,61 @@ func TestTrie_GetFromDB(t *testing.T) {
387388
}
388389
}
389390
}
391+
392+
func TestStoreAndLoadWithChildTries(t *testing.T) {
393+
keyValue := []keyValues{
394+
{key: []byte{0xf2, 0x3}, value: []byte("f")},
395+
{key: []byte{0x09, 0xd3}, value: []byte("noot")},
396+
{key: []byte{0x07}, value: []byte("ramen")},
397+
{key: []byte{0}, value: nil},
398+
{
399+
key: []byte("The boxed moved. That was a problem."),
400+
value: []byte("The question now was whether or not Peter was going to open it up and look inside to see why it had moved."), // nolint
401+
},
402+
}
403+
404+
key := []byte{1, 2}
405+
value := []byte{3, 4}
406+
const dirty = true
407+
const generation = 0
408+
409+
t.Run("happy path, tries being loaded are same as trie being read", func(t *testing.T) {
410+
t.Parallel()
411+
412+
// hash could be different for keys smaller than 32 and larger than 32 bits.
413+
// thus, testing with keys of different sizes.
414+
keysToTest := [][]byte{
415+
[]byte("This handout will help you understand how paragraphs are formed, how to develop stronger paragraphs."),
416+
[]byte("This handout"),
417+
[]byte("test"),
418+
}
419+
420+
for _, keyToChild := range keysToTest {
421+
trie := NewEmptyTrie()
422+
423+
for _, test := range keyValue {
424+
trie.Put(test.key, test.value)
425+
}
426+
427+
db := newTestDB(t)
428+
429+
sampleChildTrie := NewTrie(node.NewLeaf(key, value, dirty, generation))
430+
431+
err := trie.PutChild(keyToChild, sampleChildTrie)
432+
require.NoError(t, err)
433+
434+
err = trie.Store(db)
435+
require.NoError(t, err)
436+
437+
res := NewEmptyTrie()
438+
439+
err = res.Load(db, trie.MustHash())
440+
require.NoError(t, err)
441+
442+
require.Equal(t, trie.childTries, res.childTries)
443+
require.Equal(t, trie.String(), res.String())
444+
}
445+
446+
})
447+
448+
}

lib/trie/helpers_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ type writeCall struct {
2020

2121
var errTest = errors.New("test error")
2222

23-
type Test struct {
23+
type keyValues struct {
2424
key []byte
2525
value []byte
2626
op int

0 commit comments

Comments
 (0)