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

task(storage/child-trie): implement store/load of child trie from DB #2122

Merged
merged 14 commits into from
Jan 11, 2022
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions dot/state/storage_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,19 @@
package state

import (
"fmt"
"math/big"
"sync"
"testing"
"time"

"github.com/ChainSafe/gossamer/dot/state/pruner"
"github.com/ChainSafe/gossamer/dot/types"
"github.com/ChainSafe/gossamer/lib/common"
"github.com/ChainSafe/gossamer/lib/genesis"
runtime "github.com/ChainSafe/gossamer/lib/runtime/storage"
"github.com/ChainSafe/gossamer/lib/trie"
"github.com/ChainSafe/gossamer/lib/utils"

"github.com/stretchr/testify/require"
)
Expand Down Expand Up @@ -183,3 +187,56 @@ func TestStorage_StoreTrie_NotSyncing(t *testing.T) {
require.NoError(t, err)
require.Equal(t, 2, syncMapLen(storage.tries))
}

func TestGetStorageChildAndGetStorageFromChild(t *testing.T) {
// initialise database using data directory
basepath := t.TempDir()
db, err := utils.SetupDatabase(basepath, false)
require.NoError(t, err)

_, genTrie, genHeader := genesis.NewTestGenesisWithTrieAndHeader(t)

blockState, err := NewBlockStateFromGenesis(db, genHeader)
require.NoError(t, err)

testChildTrie := trie.NewEmptyTrie()
testChildTrie.Put([]byte("keyInsidechild"), []byte("voila"))

err = genTrie.PutChild([]byte("keyToChild"), testChildTrie)
require.NoError(t, err)

storage, err := NewStorageState(db, blockState, genTrie, pruner.Config{})
require.NoError(t, err)

ts, err := runtime.NewTrieState(genTrie)
kishansagathiya marked this conversation as resolved.
Show resolved Hide resolved
require.NoError(t, err)

header, err := types.NewHeader(blockState.GenesisHash(), ts.MustRoot(),
common.Hash{}, big.NewInt(1), types.NewDigest())
require.NoError(t, err)

err = storage.StoreTrie(ts, header)
require.NoError(t, err)

root, err := genTrie.Hash()
kishansagathiya marked this conversation as resolved.
Show resolved Hide resolved
require.NoError(t, err)

_, err = storage.GetStorageChild(&root, []byte("keyToChild"))
require.NoError(t, err)

// Clear trie from cache and fetch data from disk.
storage.tries.Delete(root)
time.Sleep(time.Millisecond * 100)
kishansagathiya marked this conversation as resolved.
Show resolved Hide resolved

// Should these databases really be different?
fmt.Println(storage.db == db)
kishansagathiya marked this conversation as resolved.
Show resolved Hide resolved

_, err = storage.GetStorageChild(&root, []byte("keyToChild"))
require.NoError(t, err)

value, err := storage.GetStorageFromChild(&root, []byte("keyToChild"), []byte("keyInsidechild"))
require.NoError(t, err)

require.Equal(t, []byte("voila"), value)

kishansagathiya marked this conversation as resolved.
Show resolved Hide resolved
}
3 changes: 2 additions & 1 deletion lib/runtime/storage/trie_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package storage
import (
"bytes"
"encoding/binary"
"fmt"
"sort"
"testing"

Expand Down Expand Up @@ -221,7 +222,7 @@ func TestTrieState_DeleteChildLimit(t *testing.T) {
limit: optLimit2,
expectedDeleted: 0,
expectedDelAll: false,
errMsg: "child trie does not exist at key :child_storage:default:fakekey",
errMsg: fmt.Sprintf("child trie does not exist at key 0x%x", ":child_storage:default:fakekey"),
},
{key: []byte("keytochild"), limit: optLimit2, expectedDeleted: 2, expectedDelAll: false},
{key: []byte("keytochild"), limit: nil, expectedDeleted: 1, expectedDelAll: true},
Expand Down
11 changes: 7 additions & 4 deletions lib/trie/child_storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package trie

import (
"errors"
"fmt"

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

var ErrChildTrieDoesNotExist = errors.New("child trie does not exist")

// PutChild inserts a child trie into the main trie at key :child_storage:[keyToChild]
func (t *Trie) PutChild(keyToChild []byte, child *Trie) error {
childHash, err := child.Hash()
Expand All @@ -32,7 +35,7 @@ func (t *Trie) GetChild(keyToChild []byte) (*Trie, error) {
key := append(ChildStorageKeyPrefix, keyToChild...)
childHash := t.Get(key)
if childHash == nil {
return nil, fmt.Errorf("child trie does not exist at key %s%s", ChildStorageKeyPrefix, keyToChild)
return nil, fmt.Errorf("%w at key 0x%x%x", ErrChildTrieDoesNotExist, ChildStorageKeyPrefix, keyToChild)
}

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

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

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

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

val := child.Get(key)
Expand All @@ -93,7 +96,7 @@ func (t *Trie) ClearFromChild(keyToChild, key []byte) error {
return err
}
if child == nil {
return fmt.Errorf("child trie does not exist at key %s%s", ChildStorageKeyPrefix, keyToChild)
return fmt.Errorf("%w at key 0x%x%x", ErrChildTrieDoesNotExist, ChildStorageKeyPrefix, keyToChild)
}
child.Delete(key)
return nil
Expand Down
27 changes: 27 additions & 0 deletions lib/trie/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ var ErrEmptyProof = errors.New("proof slice empty")
// and the value is the encoded node.
// Generally, this will only be used for the genesis trie.
func (t *Trie) Store(db chaindb.Database) error {
for _, v := range t.childTries {
if err := v.Store(db); err != nil {
return fmt.Errorf("failed to store child trie with root hash=0x%x in the db: %w", v.root.GetHash(), err)
}
}

batch := db.NewBatch()
err := t.store(batch, t.root)
if err != nil {
Expand Down Expand Up @@ -179,6 +185,21 @@ func (t *Trie) load(db chaindb.Database, curr Node) error {
}
}

for _, key := range t.GetKeysWithPrefix(ChildStorageKeyPrefix) {
childTrie := NewEmptyTrie()
value := t.Get(key)
err := childTrie.Load(db, common.NewHash(value))
if err != nil {
return fmt.Errorf("failed to load child trie with root hash=0x%x: %w", value, err)
}

err = t.PutChild(value, childTrie)
if err != nil {
return fmt.Errorf("failed to insert child trie with root hash=0x%x into main trie: %w",
childTrie.root.GetHash(), err)
}
}
kishansagathiya marked this conversation as resolved.
Show resolved Hide resolved

return nil
}

Expand Down Expand Up @@ -348,6 +369,12 @@ func (t *Trie) writeDirty(db chaindb.Batch, curr Node) error {
}
}

for _, childTrie := range t.childTries {
if err := childTrie.writeDirty(db, childTrie.root); err != nil {
return fmt.Errorf("failed to write dirty node=0x%x to database: %w", childTrie.root.GetHash(), err)
}
}

curr.SetDirty(false)
return nil
}
Expand Down