diff --git a/internal/trie/node/decode.go b/internal/trie/node/decode.go index 8653559ba3..c1c0642fca 100644 --- a/internal/trie/node/decode.go +++ b/internal/trie/node/decode.go @@ -138,6 +138,10 @@ func decodeLeaf(reader io.Reader, partialKeyLength uint16) (node *Node, err erro return nil, fmt.Errorf("%w: %s", ErrDecodeValue, err) } + // The scale decoding above can encounter either io.EOF, + // leaving the value byte slice as `[]byte(nil)`, or + // `[]byte{0}` which decodes to `[]byte{}`. + // This implementation forces empty node values to `[]byte(nil)`. if len(value) > 0 { node.SubValue = value } diff --git a/internal/trie/node/encode.go b/internal/trie/node/encode.go index 4affcff077..c0a8656b53 100644 --- a/internal/trie/node/encode.go +++ b/internal/trie/node/encode.go @@ -27,8 +27,7 @@ func (n *Node) Encode(buffer Buffer) (err error) { return fmt.Errorf("cannot write LE key to buffer: %w", err) } - kind := n.Kind() - nodeIsBranch := kind == Branch + nodeIsBranch := n.Kind() == Branch if nodeIsBranch { childrenBitmap := common.Uint16ToBytes(n.ChildrenBitmap()) _, err = buffer.Write(childrenBitmap) @@ -37,9 +36,10 @@ func (n *Node) Encode(buffer Buffer) (err error) { } } - // Only encode node value if the node is a leaf or - // the node is a branch with a non empty value. - if !nodeIsBranch || (nodeIsBranch && n.SubValue != nil) { + // Only encode node value if it's not empty. + // See https://spec.polkadot.network/#defn-node-subvalue + // See https://github.com/paritytech/substrate/blob/a7ba55d3c54b9957c242f659e02f5b5a0f47b3ff/primitives/trie/src/node_codec.rs#L123 + if n.SubValue != nil { encoder := scale.NewEncoder(buffer) err = encoder.Encode(n.SubValue) if err != nil { diff --git a/internal/trie/node/encode_test.go b/internal/trie/node/encode_test.go index f8e32b93bb..fe2b0fda87 100644 --- a/internal/trie/node/encode_test.go +++ b/internal/trie/node/encode_test.go @@ -4,9 +4,11 @@ package node import ( + "bytes" "errors" "testing" + "github.com/ChainSafe/gossamer/pkg/scale" "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -102,8 +104,6 @@ func Test_Node_Encode(t *testing.T) { writes: []writeCall{ {written: []byte{leafVariant.bits | 3}}, // partial key length 3 {written: []byte{0x01, 0x23}}, // partial key - {written: []byte{0}}, // node value encoded length - {written: nil}, // node value }, expectedEncoding: []byte{1, 2, 3}, }, @@ -310,3 +310,20 @@ func Test_Node_Encode(t *testing.T) { }) } } + +func Test_scaleEncodeEmptyByteSlices(t *testing.T) { + t.Parallel() + + buffer := bytes.NewBuffer(nil) + encoder := scale.NewEncoder(buffer) + err := encoder.Encode([]byte(nil)) + require.NoError(t, err) + assert.Equal(t, []byte{0}, buffer.Bytes()) + + buffer.Reset() + + encoder = scale.NewEncoder(buffer) + err = encoder.Encode([]byte{}) + require.NoError(t, err) + assert.Equal(t, []byte{0}, buffer.Bytes()) +}