Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
qdm12 committed May 11, 2022
1 parent 56069ef commit bfb210f
Show file tree
Hide file tree
Showing 11 changed files with 517 additions and 410 deletions.
75 changes: 32 additions & 43 deletions internal/trie/node/decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,10 @@ import (
"fmt"
"io"

"github.com/ChainSafe/gossamer/internal/trie/pools"
"github.com/ChainSafe/gossamer/pkg/scale"
)

var (
ErrReadHeaderByte = errors.New("cannot read header byte")
ErrUnknownNodeType = errors.New("unknown node type")
ErrDecodeValue = errors.New("cannot decode value")
ErrReadChildrenBitmap = errors.New("cannot read children bitmap")
ErrDecodeChildHash = errors.New("cannot decode child hash")
Expand All @@ -25,31 +22,28 @@ var (
// For branch decoding, see the comments on decodeBranch.
// For leaf decoding, see the comments on decodeLeaf.
func Decode(reader io.Reader) (n *Node, err error) {
buffer := pools.SingleByteBuffers.Get().(*bytes.Buffer)
defer pools.SingleByteBuffers.Put(buffer)
oneByteBuf := buffer.Bytes()
_, err = reader.Read(oneByteBuf)
variant, partialKeyLength, err := decodeHeader(reader)
if err != nil {
return nil, fmt.Errorf("%w: %s", ErrReadHeaderByte, err)
return nil, fmt.Errorf("cannot decode header: %w", err)
}
header := oneByteBuf[0]

nodeTypeHeaderByte := header >> 6
switch nodeTypeHeaderByte {
case leafHeader:
n, err = decodeLeaf(reader, header)
switch variant {
case leafVariant.bits:
n, err = decodeLeaf(reader, partialKeyLength)
if err != nil {
return nil, fmt.Errorf("cannot decode leaf: %w", err)
}
return n, nil
case branchHeader, branchWithValueHeader:
n, err = decodeBranch(reader, header)
case branchVariant.bits, branchWithValueVariant.bits:
n, err = decodeBranch(reader, variant, partialKeyLength)
if err != nil {
return nil, fmt.Errorf("cannot decode branch: %w", err)
}
return n, nil
default:
return nil, fmt.Errorf("%w: %d", ErrUnknownNodeType, nodeTypeHeaderByte)
// 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 @@ -58,13 +52,14 @@ 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 values using the persistent database.
func decodeBranch(reader io.Reader, header byte) (node *Node, err error) {
func decodeBranch(reader io.Reader, variant byte, partialKeyLength uint16) (
node *Node, err error) {
node = &Node{
Dirty: true,
Children: make([]*Node, ChildrenCapacity),
}

keyLen := header & keyLenOffset
node.Key, err = decodeKey(reader, keyLen)
node.Key, err = decodeKey(reader, partialKeyLength)
if err != nil {
return nil, fmt.Errorf("cannot decode key: %w", err)
}
Expand All @@ -77,15 +72,11 @@ func decodeBranch(reader io.Reader, header byte) (node *Node, err error) {

sd := scale.NewDecoder(reader)

nodeType := header >> 6
if nodeType == branchWithValueHeader {
var value []byte
// branch w/ value
err := sd.Decode(&value)
if variant == branchWithValueVariant.bits {
err := sd.Decode(&node.Value)
if err != nil {
return nil, fmt.Errorf("%w: %s", ErrDecodeValue, err)
}
node.Value = value
}

for i := 0; i < 16; i++ {
Expand All @@ -100,39 +91,37 @@ func decodeBranch(reader io.Reader, header byte) (node *Node, err error) {
ErrDecodeChildHash, i, err)
}

// Handle inlined leaf nodes.
const hashLength = 32
nodeTypeHeaderByte := hash[0] >> 6
if nodeTypeHeaderByte == leafHeader && len(hash) < hashLength {
leaf, err := decodeLeaf(bytes.NewReader(hash[1:]), hash[0])
if err != nil {
return nil, fmt.Errorf("%w: at index %d: %s",
ErrDecodeValue, i, err)
childNode := &Node{
HashDigest: hash,
}
if len(hash) < hashLength {
// Handle inlined nodes
reader = bytes.NewReader(hash)
variant, partialKeyLength, err := decodeHeader(reader)
if err == nil && variant == leafVariant.bits {
childNode, err = decodeLeaf(reader, partialKeyLength)
if err != nil {
return nil, fmt.Errorf("%w: at index %d: %s",
ErrDecodeValue, i, err)
}
}
node.Descendants++
node.Children[i] = leaf
continue
}

node.Descendants++
node.Children[i] = &Node{
HashDigest: hash,
}
node.Children[i] = childNode
}

node.Dirty = true

return node, nil
}

// decodeLeaf reads and decodes from a reader with the encoding specified in lib/trie/node/encode_doc.go.
func decodeLeaf(reader io.Reader, header byte) (node *Node, err error) {
func decodeLeaf(reader io.Reader, partialKeyLength uint16) (node *Node, err error) {
node = &Node{
Dirty: true,
}

keyLen := header & keyLenOffset
node.Key, err = decodeKey(reader, keyLen)
node.Key, err = decodeKey(reader, partialKeyLength)
if err != nil {
return nil, fmt.Errorf("cannot decode key: %w", err)
}
Expand Down
102 changes: 59 additions & 43 deletions internal/trie/node/decode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,21 +42,22 @@ func Test_Decode(t *testing.T) {
}{
"no data": {
reader: bytes.NewReader(nil),
errWrapped: ErrReadHeaderByte,
errMessage: "cannot read header byte: EOF",
errWrapped: io.EOF,
errMessage: "cannot decode header: cannot read header byte: EOF",
},
"unknown node type": {
"unknown node variant": {
reader: bytes.NewReader([]byte{0}),
errWrapped: ErrUnknownNodeType,
errMessage: "unknown node type: 0",
errWrapped: ErrVariantUnknown,
errMessage: "cannot decode header: cannot parse header byte: node variant is unknown: for header byte 00000000",
},
"leaf decoding error": {
reader: bytes.NewReader([]byte{
65, // node type 1 (leaf) and key length 1
// missing key data byte
}),
errWrapped: ErrReadKeyData,
errMessage: "cannot decode leaf: cannot decode key: cannot read key data: EOF",
errWrapped: io.EOF,
errMessage: "cannot decode leaf: cannot decode key: " +
"cannot read from reader: EOF",
},
"leaf success": {
reader: bytes.NewReader(
Expand All @@ -79,8 +80,9 @@ func Test_Decode(t *testing.T) {
129, // node type 2 (branch without value) and key length 1
// missing key data byte
}),
errWrapped: ErrReadKeyData,
errMessage: "cannot decode branch: cannot decode key: cannot read key data: EOF",
errWrapped: io.EOF,
errMessage: "cannot decode branch: cannot decode key: " +
"cannot read from reader: EOF",
},
"branch success": {
reader: bytes.NewReader(
Expand Down Expand Up @@ -178,38 +180,42 @@ func Test_decodeBranch(t *testing.T) {
t.Parallel()

testCases := map[string]struct {
reader io.Reader
header byte
branch *Node
errWrapped error
errMessage string
reader io.Reader
variant byte
partialKeyLength uint16
branch *Node
errWrapped error
errMessage string
}{
"key decoding error": {
reader: bytes.NewBuffer([]byte{
// missing key data byte
}),
header: 129, // node type 2 (branch without value) and key length 1
errWrapped: ErrReadKeyData,
errMessage: "cannot decode key: cannot read key data: EOF",
variant: branchVariant.bits,
partialKeyLength: 1,
errWrapped: io.EOF,
errMessage: "cannot decode key: cannot read from reader: EOF",
},
"children bitmap read error": {
reader: bytes.NewBuffer([]byte{
9, // key data
// missing children bitmap 2 bytes
}),
header: 129, // node type 2 (branch without value) and key length 1
errWrapped: ErrReadChildrenBitmap,
errMessage: "cannot read children bitmap: EOF",
variant: branchVariant.bits,
partialKeyLength: 1,
errWrapped: ErrReadChildrenBitmap,
errMessage: "cannot read children bitmap: EOF",
},
"children decoding error": {
reader: bytes.NewBuffer([]byte{
9, // key data
0, 4, // children bitmap
// missing children scale encoded data
}),
header: 129, // node type 2 (branch without value) and key length 1
errWrapped: ErrDecodeChildHash,
errMessage: "cannot decode child hash: at index 10: EOF",
variant: branchVariant.bits,
partialKeyLength: 1,
errWrapped: ErrDecodeChildHash,
errMessage: "cannot decode child hash: at index 10: EOF",
},
"success node type 2": {
reader: bytes.NewBuffer(
Expand All @@ -221,7 +227,8 @@ func Test_decodeBranch(t *testing.T) {
scaleEncodeBytes(t, 1, 2, 3, 4, 5), // child hash
}),
),
header: 129, // node type 2 (branch without value) and key length 1
variant: branchVariant.bits,
partialKeyLength: 1,
branch: &Node{
Key: []byte{9},
Children: padRightChildren([]*Node{
Expand All @@ -243,9 +250,10 @@ func Test_decodeBranch(t *testing.T) {
// missing encoded branch value
}),
),
header: 193, // node type 3 (branch with value) and key length 1
errWrapped: ErrDecodeValue,
errMessage: "cannot decode value: EOF",
variant: branchWithValueVariant.bits,
partialKeyLength: 1,
errWrapped: ErrDecodeValue,
errMessage: "cannot decode value: EOF",
},
"success node type 3": {
reader: bytes.NewBuffer(
Expand All @@ -256,7 +264,8 @@ func Test_decodeBranch(t *testing.T) {
scaleEncodeBytes(t, 1, 2, 3, 4, 5), // child hash
}),
),
header: 193, // node type 3 (branch with value) and key length 1
variant: branchWithValueVariant.bits,
partialKeyLength: 1,
branch: &Node{
Key: []byte{9},
Value: []byte{7, 8, 9},
Expand All @@ -278,7 +287,8 @@ func Test_decodeBranch(t *testing.T) {
t.Run(name, func(t *testing.T) {
t.Parallel()

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

assert.ErrorIs(t, err, testCase.errWrapped)
if err != nil {
Expand All @@ -293,35 +303,39 @@ func Test_decodeLeaf(t *testing.T) {
t.Parallel()

testCases := map[string]struct {
reader io.Reader
header byte
leaf *Node
errWrapped error
errMessage string
reader io.Reader
variant byte
partialKeyLength uint16
leaf *Node
errWrapped error
errMessage string
}{
"key decoding error": {
reader: bytes.NewBuffer([]byte{
// missing key data byte
}),
header: 65, // node type 1 (leaf) and key length 1
errWrapped: ErrReadKeyData,
errMessage: "cannot decode key: cannot read key data: EOF",
variant: leafVariant.bits,
partialKeyLength: 1,
errWrapped: io.EOF,
errMessage: "cannot decode key: cannot read from reader: EOF",
},
"value decoding error": {
reader: bytes.NewBuffer([]byte{
9, // key data
255, 255, // bad value data
}),
header: 65, // node type 1 (leaf) and key length 1
errWrapped: ErrDecodeValue,
errMessage: "cannot decode value: could not decode invalid integer",
variant: leafVariant.bits,
partialKeyLength: 1,
errWrapped: ErrDecodeValue,
errMessage: "cannot decode value: could not decode invalid integer",
},
"zero value": {
reader: bytes.NewBuffer([]byte{
9, // key data
// missing value data
}),
header: 65, // node type 1 (leaf) and key length 1
variant: leafVariant.bits,
partialKeyLength: 1,
leaf: &Node{
Key: []byte{9},
Dirty: true,
Expand All @@ -334,7 +348,8 @@ func Test_decodeLeaf(t *testing.T) {
scaleEncodeBytes(t, 1, 2, 3, 4, 5), // value data
}),
),
header: 65, // node type 1 (leaf) and key length 1
variant: leafVariant.bits,
partialKeyLength: 1,
leaf: &Node{
Key: []byte{9},
Value: []byte{1, 2, 3, 4, 5},
Expand All @@ -348,7 +363,8 @@ func Test_decodeLeaf(t *testing.T) {
t.Run(name, func(t *testing.T) {
t.Parallel()

leaf, err := decodeLeaf(testCase.reader, testCase.header)
leaf, err := decodeLeaf(testCase.reader,
testCase.partialKeyLength)

assert.ErrorIs(t, err, testCase.errWrapped)
if err != nil {
Expand Down
6 changes: 2 additions & 4 deletions internal/trie/node/encode_decode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,12 +127,10 @@ func Test_Branch_Encode_Decode(t *testing.T) {
err := testCase.branchToEncode.Encode(buffer)
require.NoError(t, err)

oneBuffer := make([]byte, 1)
_, err = buffer.Read(oneBuffer)
variant, partialKeyLength, err := decodeHeader(buffer)
require.NoError(t, err)
header := oneBuffer[0]

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

assert.Equal(t, testCase.branchDecoded, resultBranch)
Expand Down
Loading

0 comments on commit bfb210f

Please sign in to comment.