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

feat(trie): Add trie v1 new headers support #3295

Merged
merged 29 commits into from
Jul 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
5ecc13a
feat(trie): Add trie v1 new headers support
dimartiro May 24, 2023
b2b3944
Refa: rename variants following substrate names
dimartiro Jun 1, 2023
f4ef2a9
feat(trie): Decode nodes with hashed values
dimartiro Jun 7, 2023
7f72f43
Add todo reminders
dimartiro Jun 7, 2023
894d9b7
feat(trie): Encode new node headers
dimartiro Jun 7, 2023
4ff225a
feat(trie): Add encode node with hashed values impl
dimartiro Jun 7, 2023
43f4f41
fix(trie): Decode hashed value
dimartiro Jun 7, 2023
e0f7571
feat(trie): Add short hashed values checks
dimartiro Jun 12, 2023
819792b
feat(trie): Add encode tests
dimartiro Jun 14, 2023
e7b4360
feat(trie): One more test to cover all lines
dimartiro Jun 14, 2023
7dc48b7
fix(trie): Improve error messages
dimartiro Jun 21, 2023
813413f
fix(trie/proof): Get value from hashed node
dimartiro Jul 3, 2023
d415a7e
fix tests
dimartiro Jul 4, 2023
ada5ad2
fix linting
dimartiro Jul 4, 2023
b69428d
feat(trie/proof): Use predifined nodes DB
dimartiro Jul 4, 2023
ce19d2c
feat(trie/proof): Add copyright
dimartiro Jul 4, 2023
9b17564
fix(trie/proof): tests
dimartiro Jul 4, 2023
628aa4e
fix(trie/proof): Fix verify proof
dimartiro Jul 4, 2023
78106c5
fix(trie/proof): Add mock
dimartiro Jul 4, 2023
9376bf7
Fix mock and add copyright
dimartiro Jul 4, 2023
2f0bbd4
Adds copyright
dimartiro Jul 4, 2023
c285da9
Fix type error
dimartiro Jul 4, 2023
6ce3e01
Remove use of experiment libs
dimartiro Jul 6, 2023
d66ff06
PR comments
dimartiro Jul 6, 2023
85f7523
Idiomatic comments
dimartiro Jul 6, 2023
d1441b7
Use empty variant bits instead plain bits
dimartiro Jul 16, 2023
9fb4d9c
Mock trie db
dimartiro Jul 16, 2023
98a87cf
fix(trie): Fix node copy & improve test coverage
dimartiro Jul 17, 2023
62c72b6
Merge branch 'development' into diego/trieV1/new-headers
dimartiro Jul 19, 2023
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
49 changes: 49 additions & 0 deletions dot/state/db_getter_mocks_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions dot/state/mocks_generate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ package state
//go:generate mockgen -destination=mocks_runtime_test.go -package $GOPACKAGE github.com/ChainSafe/gossamer/lib/runtime Instance
//go:generate mockgen -destination=mock_gauge_test.go -package $GOPACKAGE github.com/prometheus/client_golang/prometheus Gauge
//go:generate mockgen -destination=mock_counter_test.go -package $GOPACKAGE github.com/prometheus/client_golang/prometheus Counter
//go:generate mockgen -destination=db_getter_mocks_test.go -package=$GOPACKAGE github.com/ChainSafe/gossamer/lib/trie DBGetter
4 changes: 3 additions & 1 deletion dot/state/storage_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,13 +178,15 @@ func TestGetStorageChildAndGetStorageFromChild(t *testing.T) {
genHeader.Hash(),
"0",
))
dbGetter := NewMockDBGetter(ctrl)
dbGetter.EXPECT().Get(gomock.Any()).Times(0)

trieRoot := &node.Node{
PartialKey: []byte{1, 2},
StorageValue: []byte{3, 4},
Dirty: true,
}
testChildTrie := trie.NewTrie(trieRoot)
testChildTrie := trie.NewTrie(trieRoot, dbGetter)

testChildTrie.Put([]byte("keyInsidechild"), []byte("voila"))

Expand Down
12 changes: 9 additions & 3 deletions dot/state/tries_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,11 @@ func Test_Tries_SetEmptyTrie(t *testing.T) {

func Test_Tries_SetTrie(t *testing.T) {
t.Parallel()
ctrl := gomock.NewController(t)
dbGetter := NewMockDBGetter(ctrl)
dbGetter.EXPECT().Get(gomock.Any()).Times(0)

tr := trie.NewTrie(&node.Node{PartialKey: []byte{1}})
tr := trie.NewTrie(&node.Node{PartialKey: []byte{1}}, dbGetter)

tries := NewTries()
tries.SetTrie(tr)
Expand Down Expand Up @@ -188,6 +191,9 @@ func Test_Tries_delete(t *testing.T) {
}
func Test_Tries_get(t *testing.T) {
t.Parallel()
ctrl := gomock.NewController(t)
dbGetter := NewMockDBGetter(ctrl)
dbGetter.EXPECT().Get(gomock.Any()).Times(0)

testCases := map[string]struct {
tries *Tries
Expand All @@ -200,14 +206,14 @@ func Test_Tries_get(t *testing.T) {
{1, 2, 3}: trie.NewTrie(&node.Node{
PartialKey: []byte{1, 2, 3},
StorageValue: []byte{1},
}),
}, dbGetter),
},
},
root: common.Hash{1, 2, 3},
trie: trie.NewTrie(&node.Node{
PartialKey: []byte{1, 2, 3},
StorageValue: []byte{1},
}),
}, dbGetter),
},
"not_found_in_map": {
// similar to not found in database
Expand Down
1 change: 1 addition & 0 deletions internal/trie/node/copy.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ func (n *Node) Copy(settings CopySettings) *Node {
if settings.CopyStorageValue && n.StorageValue != nil {
cpy.StorageValue = make([]byte, len(n.StorageValue))
copy(cpy.StorageValue, n.StorageValue)
cpy.HashedValue = n.HashedValue
}

if settings.CopyMerkleValue {
Expand Down
48 changes: 48 additions & 0 deletions internal/trie/node/copy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,37 @@ func Test_Node_Copy(t *testing.T) {
MerkleValue: []byte{5},
},
},
"deep_copy_branch_with_hashed_values": {
node: &Node{
PartialKey: []byte{1, 2},
StorageValue: []byte{3, 4},
HashedValue: true,
Children: padRightChildren([]*Node{
nil, nil, {
PartialKey: []byte{9},
StorageValue: []byte{1},
HashedValue: true,
},
}),
Dirty: true,
MerkleValue: []byte{5},
},
settings: DeepCopySettings,
expectedNode: &Node{
PartialKey: []byte{1, 2},
StorageValue: []byte{3, 4},
HashedValue: true,
Children: padRightChildren([]*Node{
nil, nil, {
PartialKey: []byte{9},
StorageValue: []byte{1},
HashedValue: true,
},
}),
Dirty: true,
MerkleValue: []byte{5},
},
},
"non_empty_leaf": {
node: &Node{
PartialKey: []byte{1, 2},
Expand Down Expand Up @@ -139,6 +170,23 @@ func Test_Node_Copy(t *testing.T) {
MerkleValue: []byte{5},
},
},
"deep_copy_leaf_with_hashed_value": {
node: &Node{
PartialKey: []byte{1, 2},
StorageValue: []byte{3, 4},
HashedValue: true,
Dirty: true,
MerkleValue: []byte{5},
},
settings: DeepCopySettings,
expectedNode: &Node{
PartialKey: []byte{1, 2},
StorageValue: []byte{3, 4},
HashedValue: true,
Dirty: true,
MerkleValue: []byte{5},
},
},
}

for name, testCase := range testCases {
Expand Down
65 changes: 50 additions & 15 deletions internal/trie/node/decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,24 @@ import (
"fmt"
"io"

"github.com/ChainSafe/gossamer/lib/common"
"github.com/ChainSafe/gossamer/pkg/scale"
)

var (
// ErrDecodeStorageValue is defined since no sentinel error is defined
// in the scale package.
// TODO remove once the following issue is done:
// https://github.com/ChainSafe/gossamer/issues/2631 .
ErrDecodeStorageValue = errors.New("cannot decode storage value")
ErrReadChildrenBitmap = errors.New("cannot read children bitmap")
ErrDecodeStorageValue = errors.New("cannot decode storage value")
ErrDecodeHashedStorageValue = errors.New("cannot decode hashed storage value")
ErrDecodeHashedValueTooShort = errors.New("hashed storage value too short")
ErrReadChildrenBitmap = errors.New("cannot read children bitmap")
// ErrDecodeChildHash is defined since no sentinel error is defined
// in the scale package.
// TODO remove once the following issue is done:
// https://github.com/ChainSafe/gossamer/issues/2631 .
ErrDecodeChildHash = errors.New("cannot decode child hash")
)

const hashLength = common.HashLength

// Decode decodes a node from a reader.
// The encoding format is documented in the README.md
// of this package, and specified in the Polkadot spec at
Expand All @@ -39,21 +40,22 @@ func Decode(reader io.Reader) (n *Node, err error) {
}

switch variant {
case leafVariant.bits:
n, err = decodeLeaf(reader, partialKeyLength)
case emptyVariant:
return nil, nil //nolint:nilnil
case leafVariant, leafWithHashedValueVariant:
n, err = decodeLeaf(reader, variant, partialKeyLength)
if err != nil {
return nil, fmt.Errorf("cannot decode leaf: %w", err)
}
return n, nil
case branchVariant.bits, branchWithValueVariant.bits:
case branchVariant, branchWithValueVariant, branchWithHashedValueVariant:
n, err = decodeBranch(reader, variant, partialKeyLength)
if err != nil {
return nil, fmt.Errorf("cannot decode branch: %w", err)
}
return n, nil
default:
// this is a programming error, an unknown node variant
// should be caught by decodeHeader.
// this is a programming error, an unknown node variant should be caught by decodeHeader.
panic(fmt.Sprintf("not implemented for node variant %08b", variant))
}
}
Expand All @@ -63,7 +65,7 @@ func Decode(reader io.Reader) (n *Node, err error) {
// reconstructing the child nodes from the encoding. This function instead stubs where the
// children are known to be with an empty leaf. The children nodes hashes are then used to
// find other storage values using the persistent database.
func decodeBranch(reader io.Reader, variant byte, partialKeyLength uint16) (
func decodeBranch(reader io.Reader, variant variant, partialKeyLength uint16) (
node *Node, err error) {
node = &Node{
Children: make([]*Node, ChildrenCapacity),
Expand All @@ -82,11 +84,21 @@ func decodeBranch(reader io.Reader, variant byte, partialKeyLength uint16) (

sd := scale.NewDecoder(reader)

if variant == branchWithValueVariant.bits {
switch variant {
case branchWithValueVariant:
err := sd.Decode(&node.StorageValue)
if err != nil {
return nil, fmt.Errorf("%w: %s", ErrDecodeStorageValue, err)
}
case branchWithHashedValueVariant:
hashedValue, err := decodeHashedValue(reader)
if err != nil {
return nil, err
}
node.StorageValue = hashedValue
node.HashedValue = true
default:
// Ignored
}

for i := 0; i < ChildrenCapacity; i++ {
Expand All @@ -101,7 +113,6 @@ func decodeBranch(reader io.Reader, variant byte, partialKeyLength uint16) (
ErrDecodeChildHash, i, err)
}

const hashLength = 32
childNode := &Node{
MerkleValue: hash,
}
Expand All @@ -123,7 +134,7 @@ func decodeBranch(reader io.Reader, variant byte, partialKeyLength uint16) (
}

// decodeLeaf reads from a reader and decodes to a leaf node.
func decodeLeaf(reader io.Reader, partialKeyLength uint16) (node *Node, err error) {
func decodeLeaf(reader io.Reader, variant variant, partialKeyLength uint16) (node *Node, err error) {
node = &Node{}

node.PartialKey, err = decodeKey(reader, partialKeyLength)
Expand All @@ -132,10 +143,34 @@ func decodeLeaf(reader io.Reader, partialKeyLength uint16) (node *Node, err erro
}

sd := scale.NewDecoder(reader)

if variant == leafWithHashedValueVariant {
hashedValue, err := decodeHashedValue(reader)
if err != nil {
return nil, err
}
node.StorageValue = hashedValue
node.HashedValue = true
return node, nil
}

err = sd.Decode(&node.StorageValue)
if err != nil {
return nil, fmt.Errorf("%w: %s", ErrDecodeStorageValue, err)
}

return node, nil
}

func decodeHashedValue(reader io.Reader) ([]byte, error) {
buffer := make([]byte, hashLength)
n, err := reader.Read(buffer)
if err != nil {
return nil, fmt.Errorf("%w: %s", ErrDecodeStorageValue, err)
}
if n < hashLength {
return nil, fmt.Errorf("%w: expected %d, got: %d", ErrDecodeHashedValueTooShort, hashLength, n)
}

return buffer, nil
}
Loading