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 support #3276

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all 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
17 changes: 7 additions & 10 deletions internal/trie/node/decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,10 @@ import (
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")
// 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")
)

Expand All @@ -39,21 +35,22 @@ func Decode(reader io.Reader) (n *Node, err error) {
}

switch variant {
case leafVariant.bits:
case emptyVariant:
return nil, nil //nolint:nilnil
case leafVariant:
n, err = decodeLeaf(reader, partialKeyLength)
if err != nil {
return nil, fmt.Errorf("cannot decode leaf: %w", err)
}
return n, nil
case branchVariant.bits, branchWithValueVariant.bits:
case branchVariant, branchWithValueVariant:
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 +60,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,7 +79,7 @@ func decodeBranch(reader io.Reader, variant byte, partialKeyLength uint16) (

sd := scale.NewDecoder(reader)

if variant == branchWithValueVariant.bits {
if variant == branchWithValueVariant {
err := sd.Decode(&node.StorageValue)
if err != nil {
return nil, fmt.Errorf("%w: %s", ErrDecodeStorageValue, err)
Expand Down
72 changes: 34 additions & 38 deletions internal/trie/node/decode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,13 @@ func Test_Decode(t *testing.T) {
errMessage: "decoding header: reading header byte: EOF",
},
"unknown_node_variant": {
reader: bytes.NewReader([]byte{0}),
reader: bytes.NewReader([]byte{0b0000_1000}),
errWrapped: ErrVariantUnknown,
errMessage: "decoding header: decoding header byte: node variant is unknown: for header byte 00000000",
errMessage: "decoding header: decoding header byte: node variant is unknown: for header byte 00001000",
},
"empty_node": {
reader: bytes.NewReader([]byte{emptyVariant.bits}),
n: nil,
},
"leaf_decoding_error": {
reader: bytes.NewReader([]byte{
Expand All @@ -64,15 +68,11 @@ func Test_Decode(t *testing.T) {
"reading from reader: EOF",
},
"leaf_success": {
reader: bytes.NewReader(
append(
[]byte{
leafVariant.bits | 1, // key length 1
9, // key data
},
scaleEncodeBytes(t, 1, 2, 3)...,
),
),
reader: bytes.NewReader(concatByteSlices([][]byte{
{leafVariant.bits | 1}, // partial key length 1
{9}, // key data
scaleEncodeBytes(t, 1, 2, 3),
})),
n: &Node{
PartialKey: []byte{9},
StorageValue: []byte{1, 2, 3},
Expand All @@ -88,13 +88,11 @@ func Test_Decode(t *testing.T) {
"reading from reader: EOF",
},
"branch_success": {
reader: bytes.NewReader(
[]byte{
branchVariant.bits | 1, // key length 1
9, // key data
0, 0, // no children bitmap
},
),
reader: bytes.NewReader(concatByteSlices([][]byte{
{branchVariant.bits | 1}, // partial key length 1
{9}, // key data
{0b0000_0000, 0b0000_0000}, // no children bitmap
})),
n: &Node{
PartialKey: []byte{9},
Children: make([]*Node, ChildrenCapacity),
Expand Down Expand Up @@ -130,7 +128,7 @@ func Test_decodeBranch(t *testing.T) {

testCases := map[string]struct {
reader io.Reader
variant byte
nodeVariant variant
partialKeyLength uint16
branch *Node
errWrapped error
Expand All @@ -140,7 +138,7 @@ func Test_decodeBranch(t *testing.T) {
reader: bytes.NewBuffer([]byte{
// missing key data byte
}),
variant: branchVariant.bits,
nodeVariant: branchVariant,
partialKeyLength: 1,
errWrapped: io.EOF,
errMessage: "cannot decode key: reading from reader: EOF",
Expand All @@ -150,7 +148,7 @@ func Test_decodeBranch(t *testing.T) {
9, // key data
// missing children bitmap 2 bytes
}),
variant: branchVariant.bits,
nodeVariant: branchVariant,
partialKeyLength: 1,
errWrapped: ErrReadChildrenBitmap,
errMessage: "cannot read children bitmap: EOF",
Expand All @@ -161,7 +159,7 @@ func Test_decodeBranch(t *testing.T) {
0, 4, // children bitmap
// missing children scale encoded data
}),
variant: branchVariant.bits,
nodeVariant: branchVariant,
partialKeyLength: 1,
errWrapped: ErrDecodeChildHash,
errMessage: "cannot decode child hash: at index 10: reading byte: EOF",
Expand All @@ -174,7 +172,7 @@ func Test_decodeBranch(t *testing.T) {
scaleEncodedChildHash,
}),
),
variant: branchVariant.bits,
nodeVariant: branchVariant,
partialKeyLength: 1,
branch: &Node{
PartialKey: []byte{9},
Expand All @@ -196,7 +194,7 @@ func Test_decodeBranch(t *testing.T) {
// missing encoded branch storage value
}),
),
variant: branchWithValueVariant.bits,
nodeVariant: branchWithValueVariant,
partialKeyLength: 1,
errWrapped: ErrDecodeStorageValue,
errMessage: "cannot decode storage value: reading byte: EOF",
Expand All @@ -208,7 +206,7 @@ func Test_decodeBranch(t *testing.T) {
scaleEncodeBytes(t, 7, 8, 9), // branch storage value
scaleEncodedChildHash,
})),
variant: branchWithValueVariant.bits,
nodeVariant: branchWithValueVariant,
partialKeyLength: 1,
branch: &Node{
PartialKey: []byte{9},
Expand All @@ -230,7 +228,7 @@ func Test_decodeBranch(t *testing.T) {
scaleEncodeBytes(t, 1), // branch storage value
{0}, // garbage inlined node
})),
variant: branchWithValueVariant.bits,
nodeVariant: branchWithValueVariant,
partialKeyLength: 1,
errWrapped: io.EOF,
errMessage: "decoding inlined child at index 0: " +
Expand Down Expand Up @@ -260,7 +258,7 @@ func Test_decodeBranch(t *testing.T) {
})),
})),
})),
variant: branchVariant.bits,
nodeVariant: branchVariant,
partialKeyLength: 1,
branch: &Node{
PartialKey: []byte{1},
Expand All @@ -286,7 +284,7 @@ func Test_decodeBranch(t *testing.T) {
t.Parallel()

branch, err := decodeBranch(testCase.reader,
testCase.variant, testCase.partialKeyLength)
testCase.nodeVariant, testCase.partialKeyLength)

assert.ErrorIs(t, err, testCase.errWrapped)
if err != nil {
Expand Down Expand Up @@ -318,10 +316,10 @@ func Test_decodeLeaf(t *testing.T) {
errMessage: "cannot decode key: reading from reader: EOF",
},
"value_decoding_error": {
reader: bytes.NewBuffer([]byte{
9, // key data
255, 255, // bad storage value data
}),
reader: bytes.NewBuffer(concatByteSlices([][]byte{
{9}, // key data
{255, 255}, // bad storage value data
})),
variant: leafVariant.bits,
partialKeyLength: 1,
errWrapped: ErrDecodeStorageValue,
Expand Down Expand Up @@ -350,12 +348,10 @@ func Test_decodeLeaf(t *testing.T) {
},
},
"success": {
reader: bytes.NewBuffer(
concatByteSlices([][]byte{
{9}, // key data
scaleEncodeBytes(t, 1, 2, 3, 4, 5), // storage value data
}),
),
reader: bytes.NewBuffer(concatByteSlices([][]byte{
{9}, // key data
scaleEncodeBytes(t, 1, 2, 3, 4, 5), // storage value data
})),
variant: leafVariant.bits,
partialKeyLength: 1,
leaf: &Node{
Expand Down
5 changes: 5 additions & 0 deletions internal/trie/node/encode.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ func (n *Node) Encode(buffer Buffer) (err error) {
return fmt.Errorf("cannot encode header: %w", err)
}

if n == nil {
// only encode the empty variant byte header
return nil
}

keyLE := codec.NibblesToKeyLE(n.PartialKey)
_, err = buffer.Write(keyLE)
if err != nil {
Expand Down
4 changes: 2 additions & 2 deletions internal/trie/node/encode_decode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,10 +121,10 @@ func Test_Branch_Encode_Decode(t *testing.T) {
err := testCase.branchToEncode.Encode(buffer)
require.NoError(t, err)

variant, partialKeyLength, err := decodeHeader(buffer)
nodeVariant, partialKeyLength, err := decodeHeader(buffer)
require.NoError(t, err)

resultBranch, err := decodeBranch(buffer, variant, partialKeyLength)
resultBranch, err := decodeBranch(buffer, nodeVariant, partialKeyLength)
require.NoError(t, err)

assert.Equal(t, testCase.branchDecoded, resultBranch)
Expand Down
9 changes: 9 additions & 0 deletions internal/trie/node/encode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,15 @@ func Test_Node_Encode(t *testing.T) {
wrappedErr error
errMessage string
}{
"nil_node": {
node: nil,
writes: []writeCall{
{
written: []byte{emptyVariant.bits},
},
},
expectedEncoding: []byte{emptyVariant.bits},
},
"leaf_header_encoding_error": {
node: &Node{
PartialKey: make([]byte, 1),
Expand Down
Loading