Skip to content

Commit 0cc4006

Browse files
task(storage/child-trie): implement store/load of child trie from DB (#2122)
Fixes #1490
1 parent 790dfb5 commit 0cc4006

File tree

4 files changed

+87
-5
lines changed

4 files changed

+87
-5
lines changed

dot/state/storage_test.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,11 @@ import (
1111

1212
"github.com/ChainSafe/gossamer/dot/state/pruner"
1313
"github.com/ChainSafe/gossamer/dot/types"
14+
"github.com/ChainSafe/gossamer/lib/common"
15+
"github.com/ChainSafe/gossamer/lib/genesis"
1416
runtime "github.com/ChainSafe/gossamer/lib/runtime/storage"
1517
"github.com/ChainSafe/gossamer/lib/trie"
18+
"github.com/ChainSafe/gossamer/lib/utils"
1619

1720
"github.com/stretchr/testify/require"
1821
)
@@ -183,3 +186,51 @@ func TestStorage_StoreTrie_NotSyncing(t *testing.T) {
183186
require.NoError(t, err)
184187
require.Equal(t, 2, syncMapLen(storage.tries))
185188
}
189+
190+
func TestGetStorageChildAndGetStorageFromChild(t *testing.T) {
191+
// initialise database using data directory
192+
basepath := t.TempDir()
193+
db, err := utils.SetupDatabase(basepath, false)
194+
require.NoError(t, err)
195+
196+
_, genTrie, genHeader := genesis.NewTestGenesisWithTrieAndHeader(t)
197+
198+
blockState, err := NewBlockStateFromGenesis(db, genHeader)
199+
require.NoError(t, err)
200+
201+
testChildTrie := trie.NewEmptyTrie()
202+
testChildTrie.Put([]byte("keyInsidechild"), []byte("voila"))
203+
204+
err = genTrie.PutChild([]byte("keyToChild"), testChildTrie)
205+
require.NoError(t, err)
206+
207+
storage, err := NewStorageState(db, blockState, genTrie, pruner.Config{})
208+
require.NoError(t, err)
209+
210+
trieState, err := runtime.NewTrieState(genTrie)
211+
require.NoError(t, err)
212+
213+
header, err := types.NewHeader(blockState.GenesisHash(), trieState.MustRoot(),
214+
common.Hash{}, big.NewInt(1), types.NewDigest())
215+
require.NoError(t, err)
216+
217+
err = storage.StoreTrie(trieState, header)
218+
require.NoError(t, err)
219+
220+
rootHash, err := genTrie.Hash()
221+
require.NoError(t, err)
222+
223+
_, err = storage.GetStorageChild(&rootHash, []byte("keyToChild"))
224+
require.NoError(t, err)
225+
226+
// Clear trie from cache and fetch data from disk.
227+
storage.tries.Delete(rootHash)
228+
229+
_, err = storage.GetStorageChild(&rootHash, []byte("keyToChild"))
230+
require.NoError(t, err)
231+
232+
value, err := storage.GetStorageFromChild(&rootHash, []byte("keyToChild"), []byte("keyInsidechild"))
233+
require.NoError(t, err)
234+
235+
require.Equal(t, []byte("voila"), value)
236+
}

lib/runtime/storage/trie_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package storage
66
import (
77
"bytes"
88
"encoding/binary"
9+
"fmt"
910
"sort"
1011
"testing"
1112

@@ -221,7 +222,7 @@ func TestTrieState_DeleteChildLimit(t *testing.T) {
221222
limit: optLimit2,
222223
expectedDeleted: 0,
223224
expectedDelAll: false,
224-
errMsg: "child trie does not exist at key :child_storage:default:fakekey",
225+
errMsg: fmt.Sprintf("child trie does not exist at key 0x%x", ":child_storage:default:fakekey"),
225226
},
226227
{key: []byte("keytochild"), limit: optLimit2, expectedDeleted: 2, expectedDelAll: false},
227228
{key: []byte("keytochild"), limit: nil, expectedDeleted: 1, expectedDelAll: true},

lib/trie/child_storage.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
package trie
55

66
import (
7+
"errors"
78
"fmt"
89

910
"github.com/ChainSafe/gossamer/lib/common"
@@ -12,6 +13,8 @@ import (
1213
// ChildStorageKeyPrefix is the prefix for all child storage keys
1314
var ChildStorageKeyPrefix = []byte(":child_storage:default:")
1415

16+
var ErrChildTrieDoesNotExist = errors.New("child trie does not exist")
17+
1518
// PutChild inserts a child trie into the main trie at key :child_storage:[keyToChild]
1619
func (t *Trie) PutChild(keyToChild []byte, child *Trie) error {
1720
childHash, err := child.Hash()
@@ -32,7 +35,7 @@ func (t *Trie) GetChild(keyToChild []byte) (*Trie, error) {
3235
key := append(ChildStorageKeyPrefix, keyToChild...)
3336
childHash := t.Get(key)
3437
if childHash == nil {
35-
return nil, fmt.Errorf("child trie does not exist at key %s%s", ChildStorageKeyPrefix, keyToChild)
38+
return nil, fmt.Errorf("%w at key 0x%x%x", ErrChildTrieDoesNotExist, ChildStorageKeyPrefix, keyToChild)
3639
}
3740

3841
hash := [32]byte{}
@@ -58,7 +61,7 @@ func (t *Trie) PutIntoChild(keyToChild, key, value []byte) error {
5861
return err
5962
}
6063

61-
t.childTries[origChildHash] = nil
64+
delete(t.childTries, origChildHash)
6265
t.childTries[childHash] = child
6366

6467
return t.PutChild(keyToChild, child)
@@ -73,7 +76,7 @@ func (t *Trie) GetFromChild(keyToChild, key []byte) ([]byte, error) {
7376
}
7477

7578
if child == nil {
76-
return nil, fmt.Errorf("child trie does not exist at key %s%s", ChildStorageKeyPrefix, keyToChild)
79+
return nil, fmt.Errorf("%w at key 0x%x%x", ErrChildTrieDoesNotExist, ChildStorageKeyPrefix, keyToChild)
7780
}
7881

7982
val := child.Get(key)
@@ -93,7 +96,7 @@ func (t *Trie) ClearFromChild(keyToChild, key []byte) error {
9396
return err
9497
}
9598
if child == nil {
96-
return fmt.Errorf("child trie does not exist at key %s%s", ChildStorageKeyPrefix, keyToChild)
99+
return fmt.Errorf("%w at key 0x%x%x", ErrChildTrieDoesNotExist, ChildStorageKeyPrefix, keyToChild)
97100
}
98101
child.Delete(key)
99102
return nil

lib/trie/database.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@ var (
2525
// and the value is the encoded node.
2626
// Generally, this will only be used for the genesis trie.
2727
func (t *Trie) Store(db chaindb.Database) error {
28+
for _, v := range t.childTries {
29+
if err := v.Store(db); err != nil {
30+
return fmt.Errorf("failed to store child trie with root hash=0x%x in the db: %w", v.root.GetHash(), err)
31+
}
32+
}
33+
2834
batch := db.NewBatch()
2935
err := t.store(batch, t.root)
3036
if err != nil {
@@ -201,6 +207,21 @@ func (t *Trie) load(db chaindb.Database, n Node) error {
201207
}
202208
}
203209

210+
for _, key := range t.GetKeysWithPrefix(ChildStorageKeyPrefix) {
211+
childTrie := NewEmptyTrie()
212+
value := t.Get(key)
213+
err := childTrie.Load(db, common.NewHash(value))
214+
if err != nil {
215+
return fmt.Errorf("failed to load child trie with root hash=0x%x: %w", value, err)
216+
}
217+
218+
err = t.PutChild(value, childTrie)
219+
if err != nil {
220+
return fmt.Errorf("failed to insert child trie with root hash=0x%x into main trie: %w",
221+
childTrie.root.GetHash(), err)
222+
}
223+
}
224+
204225
return nil
205226
}
206227

@@ -395,6 +416,12 @@ func (t *Trie) writeDirty(db chaindb.Batch, n Node) error {
395416
}
396417
}
397418

419+
for _, childTrie := range t.childTries {
420+
if err := childTrie.writeDirty(db, childTrie.root); err != nil {
421+
return fmt.Errorf("failed to write dirty node=0x%x to database: %w", childTrie.root.GetHash(), err)
422+
}
423+
}
424+
398425
branch.SetDirty(false)
399426

400427
return nil

0 commit comments

Comments
 (0)