From 6d22c23f1bba0eba062086d612a4a43f8b32f6af Mon Sep 17 00:00:00 2001 From: Quentin McGaw Date: Mon, 6 Jun 2022 17:15:20 -0400 Subject: [PATCH] chore(trie): merge leaf and branch in single node struct (#2504) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Removed type assertions and `Node` interface - Leaf and branch structs merged in one common `Node` struct - Removed trivial methods that were used to avoid type switches such as `GetHash()` - Change `Children` field from an array to a slice so it can be nil for leaves - Deduct node type from children slice (not nil is for a leaf) Co-authored-by: Eclésio Junior --- dot/state/storage_test.go | 7 +- dot/state/tries_test.go | 4 +- go.mod | 9 +- internal/trie/node/branch.go | 84 - internal/trie/node/branch_encode.go | 194 +-- internal/trie/node/branch_encode_test.go | 479 ++---- internal/trie/node/children.go | 23 +- internal/trie/node/children_test.go | 66 +- internal/trie/node/copy.go | 97 +- internal/trie/node/copy_test.go | 143 +- internal/trie/node/decode.go | 75 +- internal/trie/node/decode_test.go | 55 +- internal/trie/node/dirty.go | 32 +- internal/trie/node/dirty_test.go | 146 +- internal/trie/node/encode.go | 75 + internal/trie/node/encode_decode_test.go | 71 +- internal/trie/node/encode_test.go | 322 +++- internal/trie/node/generation.go | 24 - internal/trie/node/generation_test.go | 50 - internal/trie/node/hash.go | 103 +- internal/trie/node/hash_test.go | 280 ++-- internal/trie/node/header.go | 59 +- internal/trie/node/header_test.go | 137 +- internal/trie/node/helpers_test.go | 10 + internal/trie/node/key.go | 28 - internal/trie/node/key_test.go | 40 - internal/trie/node/leaf.go | 71 - internal/trie/node/leaf_encode.go | 112 -- internal/trie/node/leaf_encode_test.go | 296 ---- internal/trie/node/leaf_test.go | 103 -- internal/trie/node/node.go | 99 +- .../node/{branch_test.go => node_test.go} | 150 +- internal/trie/node/stats.go | 19 - internal/trie/node/stats_test.go | 60 - internal/trie/node/types.go | 28 +- internal/trie/node/value.go | 18 - internal/trie/node/value_test.go | 30 - lib/trie/database.go | 113 +- lib/trie/helpers_test.go | 7 + lib/trie/lookup.go | 17 +- lib/trie/node.go | 6 +- lib/trie/print_test.go | 11 +- lib/trie/proof_test.go | 2 +- lib/trie/trie.go | 242 ++- lib/trie/trie_endtoend_test.go | 26 +- lib/trie/trie_test.go | 1456 ++++++++--------- 46 files changed, 2188 insertions(+), 3291 deletions(-) delete mode 100644 internal/trie/node/branch.go create mode 100644 internal/trie/node/encode.go delete mode 100644 internal/trie/node/generation.go delete mode 100644 internal/trie/node/generation_test.go create mode 100644 internal/trie/node/helpers_test.go delete mode 100644 internal/trie/node/leaf.go delete mode 100644 internal/trie/node/leaf_encode.go delete mode 100644 internal/trie/node/leaf_encode_test.go delete mode 100644 internal/trie/node/leaf_test.go rename internal/trie/node/{branch_test.go => node_test.go} (61%) delete mode 100644 internal/trie/node/stats.go delete mode 100644 internal/trie/node/stats_test.go delete mode 100644 internal/trie/node/value.go delete mode 100644 internal/trie/node/value_test.go diff --git a/dot/state/storage_test.go b/dot/state/storage_test.go index b060cf2c3e..ea6cee81a9 100644 --- a/dot/state/storage_test.go +++ b/dot/state/storage_test.go @@ -180,7 +180,12 @@ func TestGetStorageChildAndGetStorageFromChild(t *testing.T) { "0", )) - testChildTrie := trie.NewTrie(node.NewLeaf([]byte{1, 2}, []byte{3, 4}, true, 0)) + trieRoot := &node.Node{ + Key: []byte{1, 2}, + Value: []byte{3, 4}, + Dirty: true, + } + testChildTrie := trie.NewTrie(trieRoot) testChildTrie.Put([]byte("keyInsidechild"), []byte("voila")) diff --git a/dot/state/tries_test.go b/dot/state/tries_test.go index 765f43a30f..388a689106 100644 --- a/dot/state/tries_test.go +++ b/dot/state/tries_test.go @@ -168,13 +168,13 @@ func Test_Tries_get(t *testing.T) { "found in map": { tries: &Tries{ rootToTrie: map[common.Hash]*trie.Trie{ - {1, 2, 3}: trie.NewTrie(&node.Leaf{ + {1, 2, 3}: trie.NewTrie(&node.Node{ Key: []byte{1, 2, 3}, }), }, }, root: common.Hash{1, 2, 3}, - trie: trie.NewTrie(&node.Leaf{ + trie: trie.NewTrie(&node.Node{ Key: []byte{1, 2, 3}, }), }, diff --git a/go.mod b/go.mod index 80aa16bae5..1d7e6ba5b4 100644 --- a/go.mod +++ b/go.mod @@ -37,20 +37,18 @@ require ( github.com/nanobox-io/golang-scribble v0.0.0-20190309225732-aa3e7c118975 github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416 github.com/perlin-network/life v0.0.0-20191203030451-05c0e0f7eaea + github.com/prometheus/client_golang v1.12.2 + github.com/prometheus/client_model v0.2.0 github.com/qdm12/gotree v0.2.0 github.com/stretchr/testify v1.7.1 github.com/urfave/cli v1.22.9 github.com/wasmerio/go-ext-wasm v0.3.2-0.20200326095750-0a32be6068ec golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 + golang.org/x/text v0.3.7 google.golang.org/protobuf v1.28.0 ) -require ( - github.com/prometheus/client_golang v1.12.2 - github.com/prometheus/client_model v0.2.0 -) - require ( github.com/ChainSafe/log15 v1.0.0 // indirect github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 // indirect @@ -181,7 +179,6 @@ require ( golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 // indirect golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 // indirect - golang.org/x/text v0.3.7 // indirect google.golang.org/appengine v1.6.6 // indirect gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect diff --git a/internal/trie/node/branch.go b/internal/trie/node/branch.go deleted file mode 100644 index 0bbf055444..0000000000 --- a/internal/trie/node/branch.go +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright 2021 ChainSafe Systems (ON) -// SPDX-License-Identifier: LGPL-3.0-only - -package node - -import ( - "github.com/qdm12/gotree" -) - -var _ Node = (*Branch)(nil) - -const ( - // ChildrenCapacity is the maximum number of children in a branch node. - ChildrenCapacity = 16 -) - -// Branch is a branch in the trie. -type Branch struct { - // Partial key bytes in nibbles (0 to f in hexadecimal) - Key []byte - Children [16]Node - Value []byte - // Dirty is true when the branch differs - // from the node stored in the database. - Dirty bool - HashDigest []byte - Encoding []byte - // Generation is incremented on every trie Snapshot() call. - // Each node also contain a certain Generation number, - // which is updated to match the trie Generation once they are - // inserted, moved or iterated over. - Generation uint64 - - // Statistics - - // Descendants is the number of descendant nodes for - // this particular node. - Descendants uint32 -} - -// NewBranch creates a new branch using the arguments given. -func NewBranch(key, value []byte, dirty bool, generation uint64) *Branch { - return &Branch{ - Key: key, - Value: value, - Dirty: dirty, - Generation: generation, - } -} - -// Type returns BranchType if the branch value -// is nil, and BranchWithValueType otherwise. -func (b *Branch) Type() Type { - if b.Value == nil { - return BranchType - } - return BranchWithValueType -} - -func (b *Branch) String() string { - return b.StringNode().String() -} - -// StringNode returns a gotree compatible node for String methods. -func (b *Branch) StringNode() (stringNode *gotree.Node) { - stringNode = gotree.New("Branch") - stringNode.Appendf("Generation: %d", b.Generation) - stringNode.Appendf("Dirty: %t", b.Dirty) - stringNode.Appendf("Key: " + bytesToString(b.Key)) - stringNode.Appendf("Value: " + bytesToString(b.Value)) - stringNode.Appendf("Descendants: %d", b.Descendants) - stringNode.Appendf("Calculated encoding: " + bytesToString(b.Encoding)) - stringNode.Appendf("Calculated digest: " + bytesToString(b.HashDigest)) - - for i, child := range b.Children { - if child == nil { - continue - } - childNode := stringNode.Appendf("Child %d", i) - childNode.AppendNode(child.StringNode()) - } - - return stringNode -} diff --git a/internal/trie/node/branch_encode.go b/internal/trie/node/branch_encode.go index 7408482a3e..58f8acd16d 100644 --- a/internal/trie/node/branch_encode.go +++ b/internal/trie/node/branch_encode.go @@ -10,125 +10,17 @@ import ( "io" "runtime" - "github.com/ChainSafe/gossamer/internal/trie/codec" "github.com/ChainSafe/gossamer/internal/trie/pools" - "github.com/ChainSafe/gossamer/lib/common" "github.com/ChainSafe/gossamer/pkg/scale" ) -// ScaleEncodeHash hashes the node (blake2b sum on encoded value) -// and then SCALE encodes it. This is used to encode children -// nodes of branches. -func (b *Branch) ScaleEncodeHash() (encoding []byte, err error) { - buffer := pools.DigestBuffers.Get().(*bytes.Buffer) - buffer.Reset() - defer pools.DigestBuffers.Put(buffer) - - err = b.hash(buffer) - if err != nil { - return nil, fmt.Errorf("cannot hash branch: %w", err) - } - - encoding, err = scale.Marshal(buffer.Bytes()) - if err != nil { - return nil, fmt.Errorf("cannot scale encode hashed branch: %w", err) - } - - return encoding, nil -} - -func (b *Branch) hash(digestBuffer io.Writer) (err error) { - encodingBuffer := pools.EncodingBuffers.Get().(*bytes.Buffer) - encodingBuffer.Reset() - defer pools.EncodingBuffers.Put(encodingBuffer) - - err = b.Encode(encodingBuffer) - if err != nil { - return fmt.Errorf("cannot encode leaf: %w", err) - } - - // if length of encoded branch is less than 32 bytes, do not hash - if encodingBuffer.Len() < 32 { - _, err = digestBuffer.Write(encodingBuffer.Bytes()) - if err != nil { - return fmt.Errorf("cannot write encoded branch to buffer: %w", err) - } - return nil - } - - // otherwise, hash encoded node - hasher := pools.Hashers.Get().(hash.Hash) - hasher.Reset() - defer pools.Hashers.Put(hasher) - - // Note: using the sync.Pool's buffer is useful here. - _, err = hasher.Write(encodingBuffer.Bytes()) - if err != nil { - return fmt.Errorf("cannot hash encoded node: %w", err) - } - - _, err = digestBuffer.Write(hasher.Sum(nil)) - if err != nil { - return fmt.Errorf("cannot write hash sum of branch to buffer: %w", err) - } - return nil -} - -// Encode encodes a branch with the encoding specified at the top of this package -// to the buffer given. -func (b *Branch) Encode(buffer Buffer) (err error) { - if !b.Dirty && b.Encoding != nil { - _, err = buffer.Write(b.Encoding) - if err != nil { - return fmt.Errorf("cannot write stored encoding to buffer: %w", err) - } - return nil - } - - err = b.encodeHeader(buffer) - if err != nil { - return fmt.Errorf("cannot encode header: %w", err) - } - - keyLE := codec.NibblesToKeyLE(b.Key) - _, err = buffer.Write(keyLE) - if err != nil { - return fmt.Errorf("cannot write encoded key to buffer: %w", err) - } - - childrenBitmap := common.Uint16ToBytes(b.ChildrenBitmap()) - _, err = buffer.Write(childrenBitmap) - if err != nil { - return fmt.Errorf("cannot write children bitmap to buffer: %w", err) - } - - if b.Value != nil { - bytes, err := scale.Marshal(b.Value) - if err != nil { - return fmt.Errorf("cannot scale encode value: %w", err) - } - - _, err = buffer.Write(bytes) - if err != nil { - return fmt.Errorf("cannot write encoded value to buffer: %w", err) - } - } - - err = encodeChildrenOpportunisticParallel(b.Children, buffer) - if err != nil { - return fmt.Errorf("cannot encode children of branch: %w", err) - } - - return nil -} - type encodingAsyncResult struct { index int buffer *bytes.Buffer err error } -func runEncodeChild(child Node, index int, +func runEncodeChild(child *Node, index int, results chan<- encodingAsyncResult, rateLimit <-chan struct{}) { buffer := pools.EncodingBuffers.Get().(*bytes.Buffer) buffer.Reset() @@ -158,13 +50,13 @@ var parallelEncodingRateLimit = make(chan struct{}, parallelLimit) // goroutines IF they are less than the parallelLimit number of goroutines already // running. This is designed to limit the total number of goroutines in order to // avoid using too much memory on the stack. -func encodeChildrenOpportunisticParallel(children [16]Node, buffer io.Writer) (err error) { +func encodeChildrenOpportunisticParallel(children []*Node, buffer io.Writer) (err error) { // Buffered channels since children might be encoded in this // goroutine or another one. resultsCh := make(chan encodingAsyncResult, ChildrenCapacity) for i, child := range children { - if isNodeNil(child) || child.Type() == LeafType { + if child == nil || child.Type() == Leaf { runEncodeChild(child, i, resultsCh, nil) continue } @@ -223,7 +115,7 @@ func encodeChildrenOpportunisticParallel(children [16]Node, buffer io.Writer) (e return err } -func encodeChildrenSequentially(children [16]Node, buffer io.Writer) (err error) { +func encodeChildrenSequentially(children []*Node, buffer io.Writer) (err error) { for i, child := range children { err = encodeChild(child, buffer) if err != nil { @@ -233,32 +125,78 @@ func encodeChildrenSequentially(children [16]Node, buffer io.Writer) (err error) return nil } -func isNodeNil(n Node) (isNil bool) { - switch impl := n.(type) { - case *Branch: - isNil = impl == nil - case *Leaf: - isNil = impl == nil - default: - isNil = n == nil - } - return isNil -} - -func encodeChild(child Node, buffer io.Writer) (err error) { - if isNodeNil(child) { +func encodeChild(child *Node, buffer io.Writer) (err error) { + if child == nil { return nil } - scaleEncodedChild, err := child.ScaleEncodeHash() + scaleEncodedChildHash, err := scaleEncodeHash(child) if err != nil { return fmt.Errorf("failed to hash and scale encode child: %w", err) } - _, err = buffer.Write(scaleEncodedChild) + _, err = buffer.Write(scaleEncodedChildHash) if err != nil { return fmt.Errorf("failed to write child to buffer: %w", err) } return nil } + +// scaleEncodeHash hashes the node (blake2b sum on encoded value) +// and then SCALE encodes it. This is used to encode children +// nodes of branches. +func scaleEncodeHash(node *Node) (encoding []byte, err error) { + buffer := pools.DigestBuffers.Get().(*bytes.Buffer) + buffer.Reset() + defer pools.DigestBuffers.Put(buffer) + + err = hashNode(node, buffer) + if err != nil { + return nil, fmt.Errorf("cannot hash %s: %w", node.Type(), err) + } + + encoding, err = scale.Marshal(buffer.Bytes()) + if err != nil { + return nil, fmt.Errorf("cannot scale encode hashed %s: %w", node.Type(), err) + } + + return encoding, nil +} + +func hashNode(node *Node, digestWriter io.Writer) (err error) { + encodingBuffer := pools.EncodingBuffers.Get().(*bytes.Buffer) + encodingBuffer.Reset() + defer pools.EncodingBuffers.Put(encodingBuffer) + + err = node.Encode(encodingBuffer) + if err != nil { + return fmt.Errorf("cannot encode %s: %w", node.Type(), err) + } + + // if length of encoded leaf is less than 32 bytes, do not hash + if encodingBuffer.Len() < 32 { + _, err = digestWriter.Write(encodingBuffer.Bytes()) + if err != nil { + return fmt.Errorf("cannot write encoded %s to buffer: %w", node.Type(), err) + } + return nil + } + + // otherwise, hash encoded node + hasher := pools.Hashers.Get().(hash.Hash) + hasher.Reset() + defer pools.Hashers.Put(hasher) + + // Note: using the sync.Pool's buffer is useful here. + _, err = hasher.Write(encodingBuffer.Bytes()) + if err != nil { + return fmt.Errorf("cannot hash encoding of %s: %w", node.Type(), err) + } + + _, err = digestWriter.Write(hasher.Sum(nil)) + if err != nil { + return fmt.Errorf("cannot write hash sum of %s to buffer: %w", node.Type(), err) + } + return nil +} diff --git a/internal/trie/node/branch_encode_test.go b/internal/trie/node/branch_encode_test.go index c06551497b..88efb2e752 100644 --- a/internal/trie/node/branch_encode_test.go +++ b/internal/trie/node/branch_encode_test.go @@ -13,75 +13,98 @@ import ( "github.com/stretchr/testify/require" ) -func Test_Branch_ScaleEncodeHash(t *testing.T) { +func Test_hashNode(t *testing.T) { t.Parallel() testCases := map[string]struct { - branch *Branch - encoding []byte - wrappedErr error + node *Node + write writeCall + errWrapped error errMessage string }{ - "empty branch": { - branch: &Branch{}, - encoding: []byte{0xc, 0x80, 0x0, 0x0}, + "small leaf buffer write error": { + node: &Node{ + Encoding: []byte{1, 2, 3}, + }, + write: writeCall{ + written: []byte{1, 2, 3}, + err: errTest, + }, + errWrapped: errTest, + errMessage: "cannot write encoded leaf to buffer: " + + "test error", }, - "non empty branch": { - branch: &Branch{ - Key: []byte{1, 2}, - Value: []byte{3, 4}, - Children: [16]Node{ - nil, nil, &Leaf{Key: []byte{9}}, + "small leaf success": { + node: &Node{ + Encoding: []byte{1, 2, 3}, + }, + write: writeCall{ + written: []byte{1, 2, 3}, + }, + }, + "leaf hash sum buffer write error": { + node: &Node{ + Encoding: []byte{ + 1, 2, 3, 4, 5, 6, 7, 8, + 1, 2, 3, 4, 5, 6, 7, 8, + 1, 2, 3, 4, 5, 6, 7, 8, + 1, 2, 3, 4, 5, 6, 7, 8, + 1, 2, 3, 4, 5, 6, 7, 8, }, }, - encoding: []byte{0x2c, 0xc2, 0x12, 0x4, 0x0, 0x8, 0x3, 0x4, 0xc, 0x41, 0x9, 0x0}, + write: writeCall{ + written: []byte{ + 107, 105, 154, 175, 253, 170, 232, + 135, 240, 21, 207, 148, 82, 117, + 249, 230, 80, 197, 254, 17, 149, + 108, 50, 7, 80, 56, 114, 176, + 84, 114, 125, 234}, + err: errTest, + }, + errWrapped: errTest, + errMessage: "cannot write hash sum of leaf to buffer: " + + "test error", + }, + "leaf hash sum success": { + node: &Node{ + Encoding: []byte{ + 1, 2, 3, 4, 5, 6, 7, 8, + 1, 2, 3, 4, 5, 6, 7, 8, + 1, 2, 3, 4, 5, 6, 7, 8, + 1, 2, 3, 4, 5, 6, 7, 8, + 1, 2, 3, 4, 5, 6, 7, 8, + }, + }, + write: writeCall{ + written: []byte{ + 107, 105, 154, 175, 253, 170, 232, + 135, 240, 21, 207, 148, 82, 117, + 249, 230, 80, 197, 254, 17, 149, + 108, 50, 7, 80, 56, 114, 176, + 84, 114, 125, 234}, + }, }, - } - - for name, testCase := range testCases { - testCase := testCase - t.Run(name, func(t *testing.T) { - t.Parallel() - - encoding, err := testCase.branch.ScaleEncodeHash() - - if testCase.wrappedErr != nil { - assert.ErrorIs(t, err, testCase.wrappedErr) - assert.EqualError(t, err, testCase.errMessage) - } else { - require.NoError(t, err) - } - assert.Equal(t, testCase.encoding, encoding) - }) - } -} - -func Test_Branch_hash(t *testing.T) { - t.Parallel() - - testCases := map[string]struct { - branch *Branch - write writeCall - errWrapped error - errMessage string - }{ "empty branch": { - branch: &Branch{}, + node: &Node{ + Children: make([]*Node, ChildrenCapacity), + }, write: writeCall{ written: []byte{128, 0, 0}, }, }, "less than 32 bytes encoding": { - branch: &Branch{ - Key: []byte{1, 2}, + node: &Node{ + Children: make([]*Node, ChildrenCapacity), + Key: []byte{1, 2}, }, write: writeCall{ written: []byte{130, 18, 0, 0}, }, }, "less than 32 bytes encoding write error": { - branch: &Branch{ - Key: []byte{1, 2}, + node: &Node{ + Children: make([]*Node, ChildrenCapacity), + Key: []byte{1, 2}, }, write: writeCall{ written: []byte{130, 18, 0, 0}, @@ -91,8 +114,9 @@ func Test_Branch_hash(t *testing.T) { errMessage: "cannot write encoded branch to buffer: test error", }, "more than 32 bytes encoding": { - branch: &Branch{ - Key: repeatBytes(100, 1), + node: &Node{ + Children: make([]*Node, ChildrenCapacity), + Key: repeatBytes(100, 1), }, write: writeCall{ written: []byte{ @@ -103,8 +127,9 @@ func Test_Branch_hash(t *testing.T) { }, }, "more than 32 bytes encoding write error": { - branch: &Branch{ - Key: repeatBytes(100, 1), + node: &Node{ + Children: make([]*Node, ChildrenCapacity), + Key: repeatBytes(100, 1), }, write: writeCall{ written: []byte{ @@ -129,7 +154,7 @@ func Test_Branch_hash(t *testing.T) { digestBuffer.EXPECT().Write(testCase.write.written). Return(testCase.write.n, testCase.write.err) - err := testCase.branch.hash(digestBuffer) + err := hashNode(testCase.node, digestBuffer) if testCase.errWrapped != nil { assert.ErrorIs(t, err, testCase.errWrapped) @@ -141,213 +166,6 @@ func Test_Branch_hash(t *testing.T) { } } -func Test_Branch_Encode(t *testing.T) { - t.Parallel() - - testCases := map[string]struct { - branch *Branch - writes []writeCall - wrappedErr error - errMessage string - }{ - "clean branch with encoding": { - branch: &Branch{ - Encoding: []byte{1, 2, 3}, - }, - writes: []writeCall{ - { // stored encoding - written: []byte{1, 2, 3}, - }, - }, - }, - "write error for clean branch with encoding": { - branch: &Branch{ - Encoding: []byte{1, 2, 3}, - }, - writes: []writeCall{ - { // stored encoding - written: []byte{1, 2, 3}, - err: errTest, - }, - }, - wrappedErr: errTest, - errMessage: "cannot write stored encoding to buffer: test error", - }, - "header encoding error": { - branch: &Branch{ - Key: make([]byte, 63+(1<<16)), - }, - writes: []writeCall{ - { // header - written: []byte{191}, - }, - }, - wrappedErr: ErrPartialKeyTooBig, - errMessage: "cannot encode header: partial key length cannot be larger than or equal to 2^16: 65536", - }, - "buffer write error for encoded key": { - branch: &Branch{ - Key: []byte{1, 2, 3}, - Value: []byte{100}, - }, - writes: []writeCall{ - { // header - written: []byte{195}, - }, - { // key LE - written: []byte{1, 35}, - err: errTest, - }, - }, - wrappedErr: errTest, - errMessage: "cannot write encoded key to buffer: test error", - }, - "buffer write error for children bitmap": { - branch: &Branch{ - Key: []byte{1, 2, 3}, - Value: []byte{100}, - Children: [16]Node{ - nil, nil, nil, &Leaf{Key: []byte{9}}, - nil, nil, nil, &Leaf{Key: []byte{11}}, - }, - }, - writes: []writeCall{ - { // header - written: []byte{195}, - }, - { // key LE - written: []byte{1, 35}, - }, - { // children bitmap - written: []byte{136, 0}, - err: errTest, - }, - }, - wrappedErr: errTest, - errMessage: "cannot write children bitmap to buffer: test error", - }, - "buffer write error for value": { - branch: &Branch{ - Key: []byte{1, 2, 3}, - Value: []byte{100}, - Children: [16]Node{ - nil, nil, nil, &Leaf{Key: []byte{9}}, - nil, nil, nil, &Leaf{Key: []byte{11}}, - }, - }, - writes: []writeCall{ - { // header - written: []byte{195}, - }, - { // key LE - written: []byte{1, 35}, - }, - { // children bitmap - written: []byte{136, 0}, - }, - { // value - written: []byte{4, 100}, - err: errTest, - }, - }, - wrappedErr: errTest, - errMessage: "cannot write encoded value to buffer: test error", - }, - "buffer write error for children encoding": { - branch: &Branch{ - Key: []byte{1, 2, 3}, - Value: []byte{100}, - Children: [16]Node{ - nil, nil, nil, &Leaf{Key: []byte{9}}, - nil, nil, nil, &Leaf{Key: []byte{11}}, - }, - }, - writes: []writeCall{ - { // header - written: []byte{195}, - }, - { // key LE - written: []byte{1, 35}, - }, - { // children bitmap - written: []byte{136, 0}, - }, - { // value - written: []byte{4, 100}, - }, - { // children - written: []byte{12, 65, 9, 0}, - err: errTest, - }, - }, - wrappedErr: errTest, - errMessage: "cannot encode children of branch: " + - "cannot write encoding of child at index 3: " + - "test error", - }, - "success with children encoding": { - branch: &Branch{ - Key: []byte{1, 2, 3}, - Value: []byte{100}, - Children: [16]Node{ - nil, nil, nil, &Leaf{Key: []byte{9}}, - nil, nil, nil, &Leaf{Key: []byte{11}}, - }, - }, - writes: []writeCall{ - { // header - written: []byte{195}, - }, - { // key LE - written: []byte{1, 35}, - }, - { // children bitmap - written: []byte{136, 0}, - }, - { // value - written: []byte{4, 100}, - }, - { // first children - written: []byte{12, 65, 9, 0}, - }, - { // second children - written: []byte{12, 65, 11, 0}, - }, - }, - }, - } - - for name, testCase := range testCases { - testCase := testCase - t.Run(name, func(t *testing.T) { - t.Parallel() - ctrl := gomock.NewController(t) - - buffer := NewMockBuffer(ctrl) - var previousCall *gomock.Call - for _, write := range testCase.writes { - call := buffer.EXPECT(). - Write(write.written). - Return(write.n, write.err) - - if previousCall != nil { - call.After(previousCall) - } - previousCall = call - } - - err := testCase.branch.Encode(buffer) - - if testCase.wrappedErr != nil { - assert.ErrorIs(t, err, testCase.wrappedErr) - assert.EqualError(t, err, testCase.errMessage) - } else { - require.NoError(t, err) - } - }) - } -} - // Opportunistic parallel: 13781602 ns/op 14419488 B/op 323575 allocs/op // Sequentially: 24269268 ns/op 20126525 B/op 327668 allocs/op func Benchmark_encodeChildrenOpportunisticParallel(b *testing.B) { @@ -363,12 +181,13 @@ func Benchmark_encodeChildrenOpportunisticParallel(b *testing.B) { }) } -func populateChildren(valueSize, depth int) (children [16]Node) { +func populateChildren(valueSize, depth int) (children []*Node) { someValue := make([]byte, valueSize) + children = make([]*Node, ChildrenCapacity) if depth == 0 { for i := range children { - children[i] = &Leaf{ + children[i] = &Node{ Key: someValue, Value: someValue, } @@ -377,7 +196,7 @@ func populateChildren(valueSize, depth int) (children [16]Node) { } for i := range children { - children[i] = &Branch{ + children[i] = &Node{ Key: someValue, Value: someValue, Children: populateChildren(valueSize, depth-1), @@ -391,15 +210,15 @@ func Test_encodeChildrenOpportunisticParallel(t *testing.T) { t.Parallel() testCases := map[string]struct { - children [16]Node + children []*Node writes []writeCall wrappedErr error errMessage string }{ "no children": {}, "first child not nil": { - children: [16]Node{ - &Leaf{Key: []byte{1}}, + children: []*Node{ + {Key: []byte{1}}, }, writes: []writeCall{ { @@ -408,11 +227,11 @@ func Test_encodeChildrenOpportunisticParallel(t *testing.T) { }, }, "last child not nil": { - children: [16]Node{ + children: []*Node{ nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - &Leaf{Key: []byte{1}}, + {Key: []byte{1}}, }, writes: []writeCall{ { @@ -421,9 +240,9 @@ func Test_encodeChildrenOpportunisticParallel(t *testing.T) { }, }, "first two children not nil": { - children: [16]Node{ - &Leaf{Key: []byte{1}}, - &Leaf{Key: []byte{2}}, + children: []*Node{ + {Key: []byte{1}}, + {Key: []byte{2}}, }, writes: []writeCall{ { @@ -435,13 +254,11 @@ func Test_encodeChildrenOpportunisticParallel(t *testing.T) { }, }, "leaf encoding error": { - children: [16]Node{ + children: []*Node{ nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - &Leaf{ - Key: []byte{1}, - }, + {Key: []byte{1}}, nil, nil, nil, nil, }, writes: []writeCall{ @@ -457,11 +274,11 @@ func Test_encodeChildrenOpportunisticParallel(t *testing.T) { "branch encoding": { // Note this may run in parallel or not depending on other tests // running in parallel. - children: [16]Node{ - &Branch{ + children: []*Node{ + { Key: []byte{1}, - Children: [16]Node{ - &Leaf{Key: []byte{1}}, + Children: []*Node{ + {Key: []byte{1}}, }, }, }, @@ -506,9 +323,11 @@ func Test_encodeChildrenOpportunisticParallel(t *testing.T) { t.Run("opportunist parallel branch encoding", func(t *testing.T) { t.Parallel() - var children [16]Node + children := make([]*Node, ChildrenCapacity) for i := range children { - children[i] = &Branch{} + children[i] = &Node{ + Children: make([]*Node, ChildrenCapacity), + } } buffer := bytes.NewBuffer(nil) @@ -533,15 +352,15 @@ func Test_encodeChildrenSequentially(t *testing.T) { t.Parallel() testCases := map[string]struct { - children [16]Node + children []*Node writes []writeCall wrappedErr error errMessage string }{ "no children": {}, "first child not nil": { - children: [16]Node{ - &Leaf{Key: []byte{1}}, + children: []*Node{ + {Key: []byte{1}}, }, writes: []writeCall{ { @@ -550,11 +369,11 @@ func Test_encodeChildrenSequentially(t *testing.T) { }, }, "last child not nil": { - children: [16]Node{ + children: []*Node{ nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - &Leaf{Key: []byte{1}}, + {Key: []byte{1}}, }, writes: []writeCall{ { @@ -563,9 +382,9 @@ func Test_encodeChildrenSequentially(t *testing.T) { }, }, "first two children not nil": { - children: [16]Node{ - &Leaf{Key: []byte{1}}, - &Leaf{Key: []byte{2}}, + children: []*Node{ + {Key: []byte{1}}, + {Key: []byte{2}}, }, writes: []writeCall{ { @@ -577,13 +396,11 @@ func Test_encodeChildrenSequentially(t *testing.T) { }, }, "encoding error": { - children: [16]Node{ + children: []*Node{ nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - &Leaf{ - Key: []byte{1}, - }, + {Key: []byte{1}}, nil, nil, nil, nil, }, writes: []writeCall{ @@ -633,35 +450,33 @@ func Test_encodeChild(t *testing.T) { t.Parallel() testCases := map[string]struct { - child Node + child *Node writeCall bool write writeCall wrappedErr error errMessage string }{ "nil node": {}, - "nil leaf": { - child: (*Leaf)(nil), - }, - "nil branch": { - child: (*Branch)(nil), - }, "empty leaf child": { - child: &Leaf{}, + child: &Node{}, writeCall: true, write: writeCall{ written: []byte{8, 64, 0}, }, }, "empty branch child": { - child: &Branch{}, + child: &Node{ + Children: make([]*Node, ChildrenCapacity), + }, writeCall: true, write: writeCall{ written: []byte{12, 128, 0, 0}, }, }, "buffer write error": { - child: &Branch{}, + child: &Node{ + Children: make([]*Node, ChildrenCapacity), + }, writeCall: true, write: writeCall{ written: []byte{12, 128, 0, 0}, @@ -671,7 +486,7 @@ func Test_encodeChild(t *testing.T) { errMessage: "failed to write child to buffer: test error", }, "leaf child": { - child: &Leaf{ + child: &Node{ Key: []byte{1}, Value: []byte{2}, }, @@ -681,12 +496,11 @@ func Test_encodeChild(t *testing.T) { }, }, "branch child": { - child: &Branch{ + child: &Node{ Key: []byte{1}, Value: []byte{2}, - Children: [16]Node{ - nil, nil, &Leaf{ - Key: []byte{5}, + Children: []*Node{ + nil, nil, {Key: []byte{5}, Value: []byte{6}, }, }, @@ -723,3 +537,52 @@ func Test_encodeChild(t *testing.T) { }) } } + +func Test_scaleEncodeHash(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + node *Node + encoding []byte + wrappedErr error + errMessage string + }{ + "empty leaf": { + node: &Node{}, + encoding: []byte{0x8, 0x40, 0}, + }, + "empty branch": { + node: &Node{ + Children: make([]*Node, ChildrenCapacity), + }, + encoding: []byte{0xc, 0x80, 0x0, 0x0}, + }, + "non empty branch": { + node: &Node{ + Key: []byte{1, 2}, + Value: []byte{3, 4}, + Children: []*Node{ + nil, nil, {Key: []byte{9}}, + }, + }, + encoding: []byte{0x2c, 0xc2, 0x12, 0x4, 0x0, 0x8, 0x3, 0x4, 0xc, 0x41, 0x9, 0x0}, + }, + } + + for name, testCase := range testCases { + testCase := testCase + t.Run(name, func(t *testing.T) { + t.Parallel() + + encoding, err := scaleEncodeHash(testCase.node) + + if testCase.wrappedErr != nil { + assert.ErrorIs(t, err, testCase.wrappedErr) + assert.EqualError(t, err, testCase.errMessage) + } else { + require.NoError(t, err) + } + assert.Equal(t, testCase.encoding, encoding) + }) + } +} diff --git a/internal/trie/node/children.go b/internal/trie/node/children.go index be4f9e47ea..b08c711c9e 100644 --- a/internal/trie/node/children.go +++ b/internal/trie/node/children.go @@ -3,23 +3,28 @@ package node +const ( + // ChildrenCapacity is the maximum number of children in a branch node. + ChildrenCapacity = 16 +) + // ChildrenBitmap returns the 16 bit bitmap -// of the children in the branch. -func (b *Branch) ChildrenBitmap() (bitmap uint16) { - for i := uint(0); i < 16; i++ { - if b.Children[i] == nil { +// of the children in the branch node. +func (n *Node) ChildrenBitmap() (bitmap uint16) { + for i := range n.Children { + if n.Children[i] == nil { continue } - bitmap |= 1 << i + bitmap |= 1 << uint(i) } return bitmap } // NumChildren returns the total number of children -// in the branch. -func (b *Branch) NumChildren() (count int) { - for i := 0; i < 16; i++ { - if b.Children[i] != nil { +// in the branch node. +func (n *Node) NumChildren() (count int) { + for i := range n.Children { + if n.Children[i] != nil { count++ } } diff --git a/internal/trie/node/children_test.go b/internal/trie/node/children_test.go index 4b60039656..66a2603009 100644 --- a/internal/trie/node/children_test.go +++ b/internal/trie/node/children_test.go @@ -9,43 +9,43 @@ import ( "github.com/stretchr/testify/assert" ) -func Test_Branch_ChildrenBitmap(t *testing.T) { +func Test_Node_ChildrenBitmap(t *testing.T) { t.Parallel() testCases := map[string]struct { - branch *Branch + node Node bitmap uint16 }{ "no children": { - branch: &Branch{}, + node: Node{}, }, "index 0": { - branch: &Branch{ - Children: [16]Node{ - &Leaf{}, + node: Node{ + Children: []*Node{ + {}, }, }, bitmap: 1, }, "index 0 and 4": { - branch: &Branch{ - Children: [16]Node{ - &Leaf{}, + node: Node{ + Children: []*Node{ + {}, nil, nil, nil, - &Leaf{}, + {}, }, }, bitmap: 1<<4 + 1, }, "index 0, 4 and 15": { - branch: &Branch{ - Children: [16]Node{ - &Leaf{}, + node: Node{ + Children: []*Node{ + {}, nil, nil, nil, - &Leaf{}, + {}, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - &Leaf{}, + {}, }, }, bitmap: 1<<15 + 1<<4 + 1, @@ -57,50 +57,50 @@ func Test_Branch_ChildrenBitmap(t *testing.T) { t.Run(name, func(t *testing.T) { t.Parallel() - bitmap := testCase.branch.ChildrenBitmap() + bitmap := testCase.node.ChildrenBitmap() assert.Equal(t, testCase.bitmap, bitmap) }) } } -func Test_Branch_NumChildren(t *testing.T) { +func Test_Node_NumChildren(t *testing.T) { t.Parallel() testCases := map[string]struct { - branch *Branch - count int + node Node + count int }{ "zero": { - branch: &Branch{}, + node: Node{}, }, "one": { - branch: &Branch{ - Children: [16]Node{ - &Leaf{}, + node: Node{ + Children: []*Node{ + {}, }, }, count: 1, }, "two": { - branch: &Branch{ - Children: [16]Node{ - &Leaf{}, + node: Node{ + Children: []*Node{ + {}, nil, nil, nil, - &Leaf{}, + {}, }, }, count: 2, }, "three": { - branch: &Branch{ - Children: [16]Node{ - &Leaf{}, + node: Node{ + Children: []*Node{ + {}, nil, nil, nil, - &Leaf{}, + {}, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - &Leaf{}, + {}, }, }, count: 3, @@ -112,7 +112,7 @@ func Test_Branch_NumChildren(t *testing.T) { t.Run(name, func(t *testing.T) { t.Parallel() - count := testCase.branch.NumChildren() + count := testCase.node.NumChildren() assert.Equal(t, testCase.count, count) }) diff --git a/internal/trie/node/copy.go b/internal/trie/node/copy.go index 9e51343b5c..ec2e5c0796 100644 --- a/internal/trie/node/copy.go +++ b/internal/trie/node/copy.go @@ -52,85 +52,56 @@ type CopySettings struct { CopyValue bool } -// Copy deep copies the branch. +// Copy deep copies the node. // Setting copyChildren to true will deep copy // children as well. -func (b *Branch) Copy(settings CopySettings) Node { - cpy := &Branch{ - Dirty: b.Dirty, - Generation: b.Generation, - Descendants: b.GetDescendants(), +func (n *Node) Copy(settings CopySettings) *Node { + cpy := &Node{ + Dirty: n.Dirty, + Generation: n.Generation, + Descendants: n.Descendants, } - if settings.CopyChildren { - // Copy all fields of children if we deep copy children - childSettings := settings - childSettings.CopyKey = true - childSettings.CopyValue = true - childSettings.CopyCached = true - for i, child := range b.Children { - if child == nil { - continue + if n.Type() == Branch { + if settings.CopyChildren { + // Copy all fields of children if we deep copy children + childSettings := settings + childSettings.CopyKey = true + childSettings.CopyValue = true + childSettings.CopyCached = true + cpy.Children = make([]*Node, ChildrenCapacity) + for i, child := range n.Children { + if child == nil { + continue + } + cpy.Children[i] = child.Copy(childSettings) } - cpy.Children[i] = child.Copy(childSettings) + } else { + cpy.Children = make([]*Node, ChildrenCapacity) + copy(cpy.Children, n.Children) // copy node pointers only } - } else { - cpy.Children = b.Children // copy interface pointers only } - if settings.CopyKey && b.Key != nil { - cpy.Key = make([]byte, len(b.Key)) - copy(cpy.Key, b.Key) + if settings.CopyKey && n.Key != nil { + cpy.Key = make([]byte, len(n.Key)) + copy(cpy.Key, n.Key) } // nil and []byte{} are encoded differently, watch out! - if settings.CopyValue && b.Value != nil { - cpy.Value = make([]byte, len(b.Value)) - copy(cpy.Value, b.Value) + if settings.CopyValue && n.Value != nil { + cpy.Value = make([]byte, len(n.Value)) + copy(cpy.Value, n.Value) } if settings.CopyCached { - if b.HashDigest != nil { - cpy.HashDigest = make([]byte, len(b.HashDigest)) - copy(cpy.HashDigest, b.HashDigest) + if n.HashDigest != nil { + cpy.HashDigest = make([]byte, len(n.HashDigest)) + copy(cpy.HashDigest, n.HashDigest) } - if b.Encoding != nil { - cpy.Encoding = make([]byte, len(b.Encoding)) - copy(cpy.Encoding, b.Encoding) - } - } - - return cpy -} - -// Copy deep copies the leaf. -func (l *Leaf) Copy(settings CopySettings) Node { - cpy := &Leaf{ - Dirty: l.Dirty, - Generation: l.Generation, - } - - if settings.CopyKey && l.Key != nil { - cpy.Key = make([]byte, len(l.Key)) - copy(cpy.Key, l.Key) - } - - // nil and []byte{} are encoded differently, watch out! - if settings.CopyValue && l.Value != nil { - cpy.Value = make([]byte, len(l.Value)) - copy(cpy.Value, l.Value) - } - - if settings.CopyCached { - if l.HashDigest != nil { - cpy.HashDigest = make([]byte, len(l.HashDigest)) - copy(cpy.HashDigest, l.HashDigest) - } - - if l.Encoding != nil { - cpy.Encoding = make([]byte, len(l.Encoding)) - copy(cpy.Encoding, l.Encoding) + if n.Encoding != nil { + cpy.Encoding = make([]byte, len(n.Encoding)) + copy(cpy.Encoding, n.Encoding) } } diff --git a/internal/trie/node/copy_test.go b/internal/trie/node/copy_test.go index 215a572af8..ad0f2f0a2a 100644 --- a/internal/trie/node/copy_test.go +++ b/internal/trie/node/copy_test.go @@ -8,7 +8,6 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) func testForSliceModif(t *testing.T, original, copied []byte) { @@ -21,116 +20,100 @@ func testForSliceModif(t *testing.T, original, copied []byte) { assert.NotEqual(t, copied, original) } -func Test_Branch_Copy(t *testing.T) { +func Test_Node_Copy(t *testing.T) { t.Parallel() testCases := map[string]struct { - branch *Branch - settings CopySettings - expectedBranch *Branch + node *Node + settings CopySettings + expectedNode *Node }{ "empty branch": { - branch: &Branch{}, - expectedBranch: &Branch{}, + node: &Node{ + Children: make([]*Node, ChildrenCapacity), + }, + expectedNode: &Node{ + Children: make([]*Node, ChildrenCapacity), + }, }, "non empty branch": { - branch: &Branch{ + node: &Node{ Key: []byte{1, 2}, Value: []byte{3, 4}, - Children: [16]Node{ - nil, nil, &Leaf{Key: []byte{9}}, - }, + Children: padRightChildren([]*Node{ + nil, nil, { + Key: []byte{9}, + }, + }), Dirty: true, HashDigest: []byte{5}, Encoding: []byte{6}, }, settings: DefaultCopySettings, - expectedBranch: &Branch{ + expectedNode: &Node{ Key: []byte{1, 2}, Value: []byte{3, 4}, - Children: [16]Node{ - nil, nil, &Leaf{Key: []byte{9}}, - }, + Children: padRightChildren([]*Node{ + nil, nil, { + Key: []byte{9}, + }, + }), Dirty: true, }, }, "branch with children copied": { - branch: &Branch{ - Children: [16]Node{ - nil, nil, &Leaf{Key: []byte{9}}, - }, + node: &Node{ + Children: padRightChildren([]*Node{ + nil, nil, { + Key: []byte{9}, + }, + }), }, settings: CopySettings{ CopyChildren: true, }, - expectedBranch: &Branch{ - Children: [16]Node{ - nil, nil, &Leaf{Key: []byte{9}}, - }, + expectedNode: &Node{ + Children: padRightChildren([]*Node{ + nil, nil, { + Key: []byte{9}, + }, + }), }, }, - "deep copy": { - branch: &Branch{ + "deep copy branch": { + node: &Node{ Key: []byte{1, 2}, Value: []byte{3, 4}, - Children: [16]Node{ - nil, nil, &Leaf{Key: []byte{9}}, - }, + Children: padRightChildren([]*Node{ + nil, nil, { + Key: []byte{9}, + }, + }), Dirty: true, HashDigest: []byte{5}, Encoding: []byte{6}, }, settings: DeepCopySettings, - expectedBranch: &Branch{ + expectedNode: &Node{ Key: []byte{1, 2}, Value: []byte{3, 4}, - Children: [16]Node{ - nil, nil, &Leaf{Key: []byte{9}}, - }, + Children: padRightChildren([]*Node{ + nil, nil, { + Key: []byte{9}, + }, + }), Dirty: true, HashDigest: []byte{5}, Encoding: []byte{6}, }, }, - } - - for name, testCase := range testCases { - testCase := testCase - t.Run(name, func(t *testing.T) { - t.Parallel() - - nodeCopy := testCase.branch.Copy(testCase.settings) - - branchCopy, ok := nodeCopy.(*Branch) - require.True(t, ok) - - assert.Equal(t, testCase.expectedBranch, branchCopy) - testForSliceModif(t, testCase.branch.Key, branchCopy.Key) - testForSliceModif(t, testCase.branch.Value, branchCopy.Value) - testForSliceModif(t, testCase.branch.HashDigest, branchCopy.HashDigest) - testForSliceModif(t, testCase.branch.Encoding, branchCopy.Encoding) - - testCase.branch.Children[15] = &Leaf{Key: []byte("modified")} - assert.NotEqual(t, branchCopy.Children, testCase.branch.Children) - }) - } -} - -func Test_Leaf_Copy(t *testing.T) { - t.Parallel() - - testCases := map[string]struct { - leaf *Leaf - settings CopySettings - expectedLeaf *Leaf - }{ "empty leaf": { - leaf: &Leaf{}, + node: &Node{}, settings: DefaultCopySettings, - expectedLeaf: &Leaf{}, + expectedNode: &Node{}, }, "non empty leaf": { - leaf: &Leaf{ + node: &Node{ Key: []byte{1, 2}, Value: []byte{3, 4}, Dirty: true, @@ -138,14 +121,14 @@ func Test_Leaf_Copy(t *testing.T) { Encoding: []byte{6}, }, settings: DefaultCopySettings, - expectedLeaf: &Leaf{ + expectedNode: &Node{ Key: []byte{1, 2}, Value: []byte{3, 4}, Dirty: true, }, }, - "deep copy": { - leaf: &Leaf{ + "deep copy leaf": { + node: &Node{ Key: []byte{1, 2}, Value: []byte{3, 4}, Dirty: true, @@ -153,7 +136,7 @@ func Test_Leaf_Copy(t *testing.T) { Encoding: []byte{6}, }, settings: DeepCopySettings, - expectedLeaf: &Leaf{ + expectedNode: &Node{ Key: []byte{1, 2}, Value: []byte{3, 4}, Dirty: true, @@ -168,16 +151,18 @@ func Test_Leaf_Copy(t *testing.T) { t.Run(name, func(t *testing.T) { t.Parallel() - nodeCopy := testCase.leaf.Copy(testCase.settings) + nodeCopy := testCase.node.Copy(testCase.settings) - leafCopy, ok := nodeCopy.(*Leaf) - require.True(t, ok) + assert.Equal(t, testCase.expectedNode, nodeCopy) + testForSliceModif(t, testCase.node.Key, nodeCopy.Key) + testForSliceModif(t, testCase.node.Value, nodeCopy.Value) + testForSliceModif(t, testCase.node.HashDigest, nodeCopy.HashDigest) + testForSliceModif(t, testCase.node.Encoding, nodeCopy.Encoding) - assert.Equal(t, testCase.expectedLeaf, leafCopy) - testForSliceModif(t, testCase.leaf.Key, leafCopy.Key) - testForSliceModif(t, testCase.leaf.Value, leafCopy.Value) - testForSliceModif(t, testCase.leaf.HashDigest, leafCopy.HashDigest) - testForSliceModif(t, testCase.leaf.Encoding, leafCopy.Encoding) + if testCase.node.Type() == Branch { + testCase.node.Children[15] = &Node{Key: []byte("modified")} + assert.NotEqual(t, nodeCopy.Children, testCase.node.Children) + } }) } } diff --git a/internal/trie/node/decode.go b/internal/trie/node/decode.go index 5948fa1724..cb6930bbee 100644 --- a/internal/trie/node/decode.go +++ b/internal/trie/node/decode.go @@ -14,19 +14,17 @@ import ( ) var ( - ErrReadHeaderByte = errors.New("cannot read header byte") - ErrUnknownNodeType = errors.New("unknown node type") - ErrNodeTypeIsNotABranch = errors.New("node type is not a branch") - ErrNodeTypeIsNotALeaf = errors.New("node type is not a leaf") - ErrDecodeValue = errors.New("cannot decode value") - ErrReadChildrenBitmap = errors.New("cannot read children bitmap") - ErrDecodeChildHash = errors.New("cannot decode child hash") + 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") ) // Decode decodes a node from a reader. // 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) { +func Decode(reader io.Reader) (n *Node, err error) { buffer := pools.SingleByteBuffers.Get().(*bytes.Buffer) defer pools.SingleByteBuffers.Put(buffer) oneByteBuf := buffer.Bytes() @@ -36,42 +34,38 @@ func Decode(reader io.Reader) (n Node, err error) { } header := oneByteBuf[0] - nodeType := Type(header >> 6) - switch nodeType { - case LeafType: + nodeTypeHeaderByte := header >> 6 + switch nodeTypeHeaderByte { + case leafHeader: n, err = decodeLeaf(reader, header) if err != nil { return nil, fmt.Errorf("cannot decode leaf: %w", err) } return n, nil - case BranchType, BranchWithValueType: + case branchHeader, branchWithValueHeader: n, err = decodeBranch(reader, header) if err != nil { return nil, fmt.Errorf("cannot decode branch: %w", err) } return n, nil default: - return nil, fmt.Errorf("%w: %d", ErrUnknownNodeType, nodeType) + return nil, fmt.Errorf("%w: %d", ErrUnknownNodeType, nodeTypeHeaderByte) } } -// decodeBranch reads and decodes from a reader with the encoding specified in lib/trie/node/encode_doc.go. +// decodeBranch reads and decodes from a reader with the encoding specified in internal/trie/node/encode_doc.go. // Note that since the encoded branch stores the hash of the children nodes, we are not // 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) (branch *Branch, err error) { - nodeType := Type(header >> 6) - switch nodeType { - case BranchType, BranchWithValueType: - default: - return nil, fmt.Errorf("%w: %d", ErrNodeTypeIsNotABranch, nodeType) +func decodeBranch(reader io.Reader, header byte) (node *Node, err error) { + node = &Node{ + Dirty: true, + Children: make([]*Node, ChildrenCapacity), } - branch = new(Branch) - keyLen := header & keyLenOffset - branch.Key, err = decodeKey(reader, keyLen) + node.Key, err = decodeKey(reader, keyLen) if err != nil { return nil, fmt.Errorf("cannot decode key: %w", err) } @@ -84,14 +78,15 @@ func decodeBranch(reader io.Reader, header byte) (branch *Branch, err error) { sd := scale.NewDecoder(reader) - if nodeType == BranchWithValueType { + nodeType := header >> 6 + if nodeType == branchWithValueHeader { var value []byte // branch w/ value err := sd.Decode(&value) if err != nil { return nil, fmt.Errorf("%w: %s", ErrDecodeValue, err) } - branch.Value = value + node.Value = value } for i := 0; i < 16; i++ { @@ -108,41 +103,35 @@ func decodeBranch(reader io.Reader, header byte) (branch *Branch, err error) { // Handle inlined leaf nodes. const hashLength = 32 - if Type(hash[0]>>6) == LeafType && len(hash) < hashLength { + 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) } - branch.AddDescendants(1) - branch.Children[i] = leaf + node.Descendants++ + node.Children[i] = leaf continue } - branch.AddDescendants(1) - branch.Children[i] = &Leaf{ + node.Descendants++ + node.Children[i] = &Node{ HashDigest: hash, } } - branch.Dirty = true - - return branch, nil + 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) (leaf *Leaf, err error) { - nodeType := Type(header >> 6) - if nodeType != LeafType { - return nil, fmt.Errorf("%w: %d", ErrNodeTypeIsNotALeaf, nodeType) - } - - leaf = &Leaf{ +func decodeLeaf(reader io.Reader, header byte) (node *Node, err error) { + node = &Node{ Dirty: true, } keyLen := header & keyLenOffset - leaf.Key, err = decodeKey(reader, keyLen) + node.Key, err = decodeKey(reader, keyLen) if err != nil { return nil, fmt.Errorf("cannot decode key: %w", err) } @@ -155,8 +144,8 @@ func decodeLeaf(reader io.Reader, header byte) (leaf *Leaf, err error) { } if len(value) > 0 { - leaf.Value = value + node.Value = value } - return leaf, nil + return node, nil } diff --git a/internal/trie/node/decode_test.go b/internal/trie/node/decode_test.go index 46ae084405..6a0a916b81 100644 --- a/internal/trie/node/decode_test.go +++ b/internal/trie/node/decode_test.go @@ -36,7 +36,7 @@ func Test_Decode(t *testing.T) { testCases := map[string]struct { reader io.Reader - n Node + n *Node errWrapped error errMessage string }{ @@ -68,7 +68,7 @@ func Test_Decode(t *testing.T) { scaleEncodeBytes(t, 1, 2, 3)..., ), ), - n: &Leaf{ + n: &Node{ Key: []byte{9}, Value: []byte{1, 2, 3}, Dirty: true, @@ -90,9 +90,10 @@ func Test_Decode(t *testing.T) { 0, 0, // no children bitmap }, ), - n: &Branch{ - Key: []byte{9}, - Dirty: true, + n: &Node{ + Key: []byte{9}, + Children: make([]*Node, ChildrenCapacity), + Dirty: true, }, }, "branch with two inlined children": { @@ -111,7 +112,7 @@ func Test_Decode(t *testing.T) { 187, 32, 134, 92, 74, 43, 127, 1, 0, 0, }, ), - n: &Branch{ + n: &Node{ Key: []byte{ 12, 3, 6, 5, 12, 3, 12, 15, 5, 9, 13, 6, @@ -120,9 +121,9 @@ func Test_Decode(t *testing.T) { 4, 1, 1, 3, 12, 4, }, Descendants: 2, - Children: [16]Node{ + Children: []*Node{ nil, nil, nil, nil, - &Leaf{ + { Key: []byte{ 14, 7, 11, 9, 0, 1, 2, 0, 9, 6, 11, 4, @@ -135,7 +136,7 @@ func Test_Decode(t *testing.T) { Dirty: true, }, nil, nil, nil, nil, - &Leaf{ + { Key: []byte{ 15, 1, 15, 0, 5, 1, 5, 15, 4, 6, 2, 12, @@ -179,16 +180,10 @@ func Test_decodeBranch(t *testing.T) { testCases := map[string]struct { reader io.Reader header byte - branch *Branch + branch *Node errWrapped error errMessage string }{ - "no data with header 1": { - reader: bytes.NewBuffer(nil), - header: 65, - errWrapped: ErrNodeTypeIsNotABranch, - errMessage: "node type is not a branch: 1", - }, "key decoding error": { reader: bytes.NewBuffer([]byte{ // missing key data byte @@ -227,15 +222,15 @@ func Test_decodeBranch(t *testing.T) { }), ), header: 129, // node type 2 (branch without value) and key length 1 - branch: &Branch{ + branch: &Node{ Key: []byte{9}, - Children: [16]Node{ + Children: padRightChildren([]*Node{ nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - &Leaf{ + { HashDigest: []byte{1, 2, 3, 4, 5}, }, - }, + }), Dirty: true, Descendants: 1, }, @@ -262,16 +257,16 @@ func Test_decodeBranch(t *testing.T) { }), ), header: 193, // node type 3 (branch with value) and key length 1 - branch: &Branch{ + branch: &Node{ Key: []byte{9}, Value: []byte{7, 8, 9}, - Children: [16]Node{ + Children: padRightChildren([]*Node{ nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - &Leaf{ + { HashDigest: []byte{1, 2, 3, 4, 5}, }, - }, + }), Dirty: true, Descendants: 1, }, @@ -300,16 +295,10 @@ func Test_decodeLeaf(t *testing.T) { testCases := map[string]struct { reader io.Reader header byte - leaf *Leaf + leaf *Node errWrapped error errMessage string }{ - "no data with header 1": { - reader: bytes.NewBuffer(nil), - header: 1, - errWrapped: ErrNodeTypeIsNotALeaf, - errMessage: "node type is not a leaf: 0", - }, "key decoding error": { reader: bytes.NewBuffer([]byte{ // missing key data byte @@ -333,7 +322,7 @@ func Test_decodeLeaf(t *testing.T) { // missing value data }), header: 65, // node type 1 (leaf) and key length 1 - leaf: &Leaf{ + leaf: &Node{ Key: []byte{9}, Dirty: true, }, @@ -346,7 +335,7 @@ func Test_decodeLeaf(t *testing.T) { }), ), header: 65, // node type 1 (leaf) and key length 1 - leaf: &Leaf{ + leaf: &Node{ Key: []byte{9}, Value: []byte{1, 2, 3, 4, 5}, Dirty: true, diff --git a/internal/trie/node/dirty.go b/internal/trie/node/dirty.go index c4d13f3b61..3c703942b5 100644 --- a/internal/trie/node/dirty.go +++ b/internal/trie/node/dirty.go @@ -3,36 +3,14 @@ package node -// IsDirty returns the dirty status of the branch. -func (b *Branch) IsDirty() bool { - return b.Dirty -} - -// SetDirty sets the dirty status to the branch. -func (b *Branch) SetDirty(dirty bool) { - b.Dirty = dirty - if dirty { - // A node is marked dirty if its key or value is modified. - // This means its cached encoding and hash fields are no longer - // valid. To improve memory usage, we clear these fields. - b.Encoding = nil - b.HashDigest = nil - } -} - -// IsDirty returns the dirty status of the leaf. -func (l *Leaf) IsDirty() bool { - return l.Dirty -} - -// SetDirty sets the dirty status to the leaf. -func (l *Leaf) SetDirty(dirty bool) { - l.Dirty = dirty +// SetDirty sets the dirty status to the node. +func (n *Node) SetDirty(dirty bool) { + n.Dirty = dirty if dirty { // A node is marked dirty if its key or value is modified. // This means its cached encoding and hash fields are no longer // valid. To improve memory usage, we clear these fields. - l.Encoding = nil - l.HashDigest = nil + n.Encoding = nil + n.HashDigest = nil } } diff --git a/internal/trie/node/dirty_test.go b/internal/trie/node/dirty_test.go index 62aedb2616..31d9339447 100644 --- a/internal/trie/node/dirty_test.go +++ b/internal/trie/node/dirty_test.go @@ -9,171 +9,51 @@ import ( "github.com/stretchr/testify/assert" ) -func Test_Branch_IsDirty(t *testing.T) { +func Test_Node_SetDirty(t *testing.T) { t.Parallel() testCases := map[string]struct { - branch *Branch - dirty bool - }{ - "not dirty": { - branch: &Branch{}, - }, - "dirty": { - branch: &Branch{ - Dirty: true, - }, - dirty: true, - }, - } - - for name, testCase := range testCases { - testCase := testCase - t.Run(name, func(t *testing.T) { - t.Parallel() - - dirty := testCase.branch.IsDirty() - - assert.Equal(t, testCase.dirty, dirty) - }) - } -} - -func Test_Branch_SetDirty(t *testing.T) { - t.Parallel() - - testCases := map[string]struct { - branch *Branch - dirty bool - expected *Branch - }{ - "not dirty to not dirty": { - branch: &Branch{ - Encoding: []byte{1}, - HashDigest: []byte{1}, - }, - expected: &Branch{ - Encoding: []byte{1}, - HashDigest: []byte{1}, - }, - }, - "not dirty to dirty": { - branch: &Branch{ - Encoding: []byte{1}, - HashDigest: []byte{1}, - }, - dirty: true, - expected: &Branch{Dirty: true}, - }, - "dirty to not dirty": { - branch: &Branch{ - Encoding: []byte{1}, - HashDigest: []byte{1}, - Dirty: true, - }, - expected: &Branch{ - Encoding: []byte{1}, - HashDigest: []byte{1}, - }, - }, - "dirty to dirty": { - branch: &Branch{ - Encoding: []byte{1}, - HashDigest: []byte{1}, - Dirty: true, - }, - dirty: true, - expected: &Branch{Dirty: true}, - }, - } - - for name, testCase := range testCases { - testCase := testCase - t.Run(name, func(t *testing.T) { - t.Parallel() - - testCase.branch.SetDirty(testCase.dirty) - - assert.Equal(t, testCase.expected, testCase.branch) - }) - } -} - -func Test_Leaf_IsDirty(t *testing.T) { - t.Parallel() - - testCases := map[string]struct { - leaf *Leaf - dirty bool - }{ - "not dirty": { - leaf: &Leaf{}, - }, - "dirty": { - leaf: &Leaf{ - Dirty: true, - }, - dirty: true, - }, - } - - for name, testCase := range testCases { - testCase := testCase - t.Run(name, func(t *testing.T) { - t.Parallel() - - dirty := testCase.leaf.IsDirty() - - assert.Equal(t, testCase.dirty, dirty) - }) - } -} - -func Test_Leaf_SetDirty(t *testing.T) { - t.Parallel() - - testCases := map[string]struct { - leaf *Leaf + node Node dirty bool - expected *Leaf + expected Node }{ "not dirty to not dirty": { - leaf: &Leaf{ + node: Node{ Encoding: []byte{1}, HashDigest: []byte{1}, }, - expected: &Leaf{ + expected: Node{ Encoding: []byte{1}, HashDigest: []byte{1}, }, }, "not dirty to dirty": { - leaf: &Leaf{ + node: Node{ Encoding: []byte{1}, HashDigest: []byte{1}, }, dirty: true, - expected: &Leaf{Dirty: true}, + expected: Node{Dirty: true}, }, "dirty to not dirty": { - leaf: &Leaf{ + node: Node{ Encoding: []byte{1}, HashDigest: []byte{1}, Dirty: true, }, - expected: &Leaf{ + expected: Node{ Encoding: []byte{1}, HashDigest: []byte{1}, }, }, "dirty to dirty": { - leaf: &Leaf{ + node: Node{ Encoding: []byte{1}, HashDigest: []byte{1}, Dirty: true, }, dirty: true, - expected: &Leaf{Dirty: true}, + expected: Node{Dirty: true}, }, } @@ -182,9 +62,9 @@ func Test_Leaf_SetDirty(t *testing.T) { t.Run(name, func(t *testing.T) { t.Parallel() - testCase.leaf.SetDirty(testCase.dirty) + testCase.node.SetDirty(testCase.dirty) - assert.Equal(t, testCase.expected, testCase.leaf) + assert.Equal(t, testCase.expected, testCase.node) }) } } diff --git a/internal/trie/node/encode.go b/internal/trie/node/encode.go new file mode 100644 index 0000000000..c7890e16a8 --- /dev/null +++ b/internal/trie/node/encode.go @@ -0,0 +1,75 @@ +// Copyright 2021 ChainSafe Systems (ON) +// SPDX-License-Identifier: LGPL-3.0-only + +package node + +import ( + "fmt" + + "github.com/ChainSafe/gossamer/internal/trie/codec" + "github.com/ChainSafe/gossamer/lib/common" + "github.com/ChainSafe/gossamer/pkg/scale" +) + +// Encode encodes the node to the buffer given. +// The encoding format is documented in encode_doc.go. +func (n *Node) Encode(buffer Buffer) (err error) { + if !n.Dirty && n.Encoding != nil { + _, err = buffer.Write(n.Encoding) + if err != nil { + return fmt.Errorf("cannot write stored encoding to buffer: %w", err) + } + return nil + } + + err = encodeHeader(n, buffer) + if err != nil { + return fmt.Errorf("cannot encode header: %w", err) + } + + keyLE := codec.NibblesToKeyLE(n.Key) + _, err = buffer.Write(keyLE) + if err != nil { + return fmt.Errorf("cannot write LE key to buffer: %w", err) + } + + if n.Type() == Branch { + childrenBitmap := common.Uint16ToBytes(n.ChildrenBitmap()) + _, err = buffer.Write(childrenBitmap) + if err != nil { + return fmt.Errorf("cannot write children bitmap to buffer: %w", err) + } + } + + // check value is not nil for branch nodes, even though + // leaf nodes always have a non-nil value. + if n.Type() == Leaf || n.Value != nil { + // TODO remove `n.Type() == Leaf` and update tests + encodedValue, err := scale.Marshal(n.Value) // TODO scale encoder to write to buffer + if err != nil { + return fmt.Errorf("cannot scale encode value: %w", err) + } + + _, err = buffer.Write(encodedValue) + if err != nil { + return fmt.Errorf("cannot write scale encoded value to buffer: %w", err) + } + } + + if n.Type() == Branch { + err = encodeChildrenOpportunisticParallel(n.Children, buffer) + if err != nil { + return fmt.Errorf("cannot encode children of branch: %w", err) + } + } + + if n.Type() == Leaf { + // TODO cache this for branches too and update test cases. + // TODO remove this copying since it defeats the purpose of `buffer` + // and the sync.Pool. + n.Encoding = make([]byte, buffer.Len()) + copy(n.Encoding, buffer.Bytes()) + } + + return nil +} diff --git a/internal/trie/node/encode_decode_test.go b/internal/trie/node/encode_decode_test.go index c5e77f8abb..8c6757b4ef 100644 --- a/internal/trie/node/encode_decode_test.go +++ b/internal/trie/node/encode_decode_test.go @@ -15,62 +15,69 @@ func Test_Branch_Encode_Decode(t *testing.T) { t.Parallel() testCases := map[string]struct { - branchToEncode *Branch - branchDecoded *Branch + branchToEncode *Node + branchDecoded *Node }{ "empty branch": { - branchToEncode: new(Branch), - branchDecoded: &Branch{ - Key: []byte{}, - Dirty: true, + branchToEncode: &Node{ + Children: make([]*Node, ChildrenCapacity), + }, + branchDecoded: &Node{ + Key: []byte{}, + Children: make([]*Node, ChildrenCapacity), + Dirty: true, }, }, "branch with key 5": { - branchToEncode: &Branch{ - Key: []byte{5}, + branchToEncode: &Node{ + Children: make([]*Node, ChildrenCapacity), + Key: []byte{5}, }, - branchDecoded: &Branch{ - Key: []byte{5}, - Dirty: true, + branchDecoded: &Node{ + Key: []byte{5}, + Children: make([]*Node, ChildrenCapacity), + Dirty: true, }, }, "branch with two bytes key": { - branchToEncode: &Branch{ - Key: []byte{0xf, 0xa}, // note: each byte cannot be larger than 0xf + branchToEncode: &Node{ + Key: []byte{0xf, 0xa}, // note: each byte cannot be larger than 0xf + Children: make([]*Node, ChildrenCapacity), }, - branchDecoded: &Branch{ - Key: []byte{0xf, 0xa}, - Dirty: true, + branchDecoded: &Node{ + Key: []byte{0xf, 0xa}, + Children: make([]*Node, ChildrenCapacity), + Dirty: true, }, }, "branch with child leaf inline": { - branchToEncode: &Branch{ + branchToEncode: &Node{ Key: []byte{5}, - Children: [16]Node{ - &Leaf{ + Children: padRightChildren([]*Node{ + { Key: []byte{9}, Value: []byte{10}, }, - }, + }), }, - branchDecoded: &Branch{ + branchDecoded: &Node{ Key: []byte{5}, Descendants: 1, - Children: [16]Node{ - &Leaf{ + Children: padRightChildren([]*Node{ + { Key: []byte{9}, Value: []byte{10}, Dirty: true, }, - }, + }), Dirty: true, }, }, "branch with child leaf hash": { - branchToEncode: &Branch{ + branchToEncode: &Node{ Key: []byte{5}, - Children: [16]Node{ - &Leaf{ + Children: padRightChildren([]*Node{ + { Key: []byte{ 10, 11, 12, 13, 14, 15, 16, 17, @@ -87,12 +94,12 @@ func Test_Branch_Encode_Decode(t *testing.T) { 10, 11, 12, 13, }, }, - }, + }), }, - branchDecoded: &Branch{ + branchDecoded: &Node{ Key: []byte{5}, - Children: [16]Node{ - &Leaf{ + Children: padRightChildren([]*Node{ + { HashDigest: []byte{ 2, 18, 48, 30, 98, 133, 244, 78, 70, @@ -103,7 +110,7 @@ func Test_Branch_Encode_Decode(t *testing.T) { 132, 5, 39, 204, }, }, - }, + }), Dirty: true, Descendants: 1, }, diff --git a/internal/trie/node/encode_test.go b/internal/trie/node/encode_test.go index cc72efc06a..e57c13902b 100644 --- a/internal/trie/node/encode_test.go +++ b/internal/trie/node/encode_test.go @@ -3,7 +3,14 @@ package node -import "errors" +import ( + "errors" + "testing" + + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) type writeCall struct { written []byte @@ -12,3 +19,316 @@ type writeCall struct { } var errTest = errors.New("test error") + +func Test_Node_Encode(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + node *Node + writes []writeCall + bufferLenCall bool + bufferBytesCall bool + expectedEncoding []byte + wrappedErr error + errMessage string + }{ + "clean leaf with encoding": { + node: &Node{ + Encoding: []byte{1, 2, 3}, + }, + writes: []writeCall{ + { + written: []byte{1, 2, 3}, + }, + }, + expectedEncoding: []byte{1, 2, 3}, + }, + "write error for clean leaf with encoding": { + node: &Node{ + Encoding: []byte{1, 2, 3}, + }, + writes: []writeCall{ + { + written: []byte{1, 2, 3}, + err: errTest, + }, + }, + expectedEncoding: []byte{1, 2, 3}, + wrappedErr: errTest, + errMessage: "cannot write stored encoding to buffer: test error", + }, + "leaf header encoding error": { + node: &Node{ + Key: make([]byte, 63+(1<<16)), + }, + writes: []writeCall{ + { + written: []byte{127}, + }, + }, + wrappedErr: ErrPartialKeyTooBig, + errMessage: "cannot encode header: partial key length cannot be larger than or equal to 2^16: 65536", + }, + "leaf buffer write error for encoded key": { + node: &Node{ + Key: []byte{1, 2, 3}, + }, + writes: []writeCall{ + { + written: []byte{67}, + }, + { + written: []byte{1, 35}, + err: errTest, + }, + }, + wrappedErr: errTest, + errMessage: "cannot write LE key to buffer: test error", + }, + "leaf buffer write error for encoded value": { + node: &Node{ + Key: []byte{1, 2, 3}, + Value: []byte{4, 5, 6}, + }, + writes: []writeCall{ + { + written: []byte{67}, + }, + { + written: []byte{1, 35}, + }, + { + written: []byte{12, 4, 5, 6}, + err: errTest, + }, + }, + wrappedErr: errTest, + errMessage: "cannot write scale encoded value to buffer: test error", + }, + "leaf success": { + node: &Node{ + Key: []byte{1, 2, 3}, + Value: []byte{4, 5, 6}, + }, + writes: []writeCall{ + { + written: []byte{67}, + }, + { + written: []byte{1, 35}, + }, + { + written: []byte{12, 4, 5, 6}, + }, + }, + bufferLenCall: true, + bufferBytesCall: true, + expectedEncoding: []byte{1, 2, 3}, + }, + "clean branch with encoding": { + node: &Node{ + Children: make([]*Node, ChildrenCapacity), + Encoding: []byte{1, 2, 3}, + }, + writes: []writeCall{ + { // stored encoding + written: []byte{1, 2, 3}, + }, + }, + }, + "write error for clean branch with encoding": { + node: &Node{ + Children: make([]*Node, ChildrenCapacity), + Encoding: []byte{1, 2, 3}, + }, + writes: []writeCall{ + { // stored encoding + written: []byte{1, 2, 3}, + err: errTest, + }, + }, + wrappedErr: errTest, + errMessage: "cannot write stored encoding to buffer: test error", + }, + "branch header encoding error": { + node: &Node{ + Children: make([]*Node, ChildrenCapacity), + Key: make([]byte, 63+(1<<16)), + }, + writes: []writeCall{ + { // header + written: []byte{191}, + }, + }, + wrappedErr: ErrPartialKeyTooBig, + errMessage: "cannot encode header: partial key length cannot be larger than or equal to 2^16: 65536", + }, + "buffer write error for encoded key": { + node: &Node{ + Children: make([]*Node, ChildrenCapacity), + Key: []byte{1, 2, 3}, + Value: []byte{100}, + }, + writes: []writeCall{ + { // header + written: []byte{195}, + }, + { // key LE + written: []byte{1, 35}, + err: errTest, + }, + }, + wrappedErr: errTest, + errMessage: "cannot write LE key to buffer: test error", + }, + "buffer write error for children bitmap": { + node: &Node{ + Key: []byte{1, 2, 3}, + Value: []byte{100}, + Children: []*Node{ + nil, nil, nil, {Key: []byte{9}}, + nil, nil, nil, {Key: []byte{11}}, + }, + }, + writes: []writeCall{ + { // header + written: []byte{195}, + }, + { // key LE + written: []byte{1, 35}, + }, + { // children bitmap + written: []byte{136, 0}, + err: errTest, + }, + }, + wrappedErr: errTest, + errMessage: "cannot write children bitmap to buffer: test error", + }, + "buffer write error for value": { + node: &Node{ + Key: []byte{1, 2, 3}, + Value: []byte{100}, + Children: []*Node{ + nil, nil, nil, {Key: []byte{9}}, + nil, nil, nil, {Key: []byte{11}}, + }, + }, + writes: []writeCall{ + { // header + written: []byte{195}, + }, + { // key LE + written: []byte{1, 35}, + }, + { // children bitmap + written: []byte{136, 0}, + }, + { // value + written: []byte{4, 100}, + err: errTest, + }, + }, + wrappedErr: errTest, + errMessage: "cannot write scale encoded value to buffer: test error", + }, + "buffer write error for children encoding": { + node: &Node{ + Key: []byte{1, 2, 3}, + Value: []byte{100}, + Children: []*Node{ + nil, nil, nil, {Key: []byte{9}}, + nil, nil, nil, {Key: []byte{11}}, + }, + }, + writes: []writeCall{ + { // header + written: []byte{195}, + }, + { // key LE + written: []byte{1, 35}, + }, + { // children bitmap + written: []byte{136, 0}, + }, + { // value + written: []byte{4, 100}, + }, + { // children + written: []byte{12, 65, 9, 0}, + err: errTest, + }, + }, + wrappedErr: errTest, + errMessage: "cannot encode children of branch: " + + "cannot write encoding of child at index 3: " + + "test error", + }, + "success with children encoding": { + node: &Node{ + Key: []byte{1, 2, 3}, + Value: []byte{100}, + Children: []*Node{ + nil, nil, nil, {Key: []byte{9}}, + nil, nil, nil, {Key: []byte{11}}, + }, + }, + writes: []writeCall{ + { // header + written: []byte{195}, + }, + { // key LE + written: []byte{1, 35}, + }, + { // children bitmap + written: []byte{136, 0}, + }, + { // value + written: []byte{4, 100}, + }, + { // first children + written: []byte{12, 65, 9, 0}, + }, + { // second children + written: []byte{12, 65, 11, 0}, + }, + }, + }, + } + + for name, testCase := range testCases { + testCase := testCase + t.Run(name, func(t *testing.T) { + t.Parallel() + ctrl := gomock.NewController(t) + + buffer := NewMockBuffer(ctrl) + var previousCall *gomock.Call + for _, write := range testCase.writes { + call := buffer.EXPECT(). + Write(write.written). + Return(write.n, write.err) + + if previousCall != nil { + call.After(previousCall) + } + previousCall = call + } + if testCase.bufferLenCall { + buffer.EXPECT().Len().Return(len(testCase.expectedEncoding)) + } + if testCase.bufferBytesCall { + buffer.EXPECT().Bytes().Return(testCase.expectedEncoding) + } + + err := testCase.node.Encode(buffer) + + if testCase.wrappedErr != nil { + assert.ErrorIs(t, err, testCase.wrappedErr) + assert.EqualError(t, err, testCase.errMessage) + } else { + require.NoError(t, err) + } + }) + } +} diff --git a/internal/trie/node/generation.go b/internal/trie/node/generation.go deleted file mode 100644 index cdb8d2f9f3..0000000000 --- a/internal/trie/node/generation.go +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2021 ChainSafe Systems (ON) -// SPDX-License-Identifier: LGPL-3.0-only - -package node - -// SetGeneration sets the generation given to the branch. -func (b *Branch) SetGeneration(generation uint64) { - b.Generation = generation -} - -// GetGeneration returns the generation of the branch. -func (b *Branch) GetGeneration() (generation uint64) { - return b.Generation -} - -// SetGeneration sets the generation given to the leaf. -func (l *Leaf) SetGeneration(generation uint64) { - l.Generation = generation -} - -// GetGeneration returns the generation of the leaf. -func (l *Leaf) GetGeneration() (generation uint64) { - return l.Generation -} diff --git a/internal/trie/node/generation_test.go b/internal/trie/node/generation_test.go deleted file mode 100644 index 5961a2eeb2..0000000000 --- a/internal/trie/node/generation_test.go +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2021 ChainSafe Systems (ON) -// SPDX-License-Identifier: LGPL-3.0-only - -package node - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func Test_Branch_SetGeneration(t *testing.T) { - t.Parallel() - - branch := &Branch{ - Generation: 1, - } - branch.SetGeneration(2) - assert.Equal(t, &Branch{Generation: 2}, branch) -} - -func Test_Branch_GetGeneration(t *testing.T) { - t.Parallel() - - const generation uint64 = 1 - branch := &Branch{ - Generation: generation, - } - assert.Equal(t, branch.GetGeneration(), generation) -} - -func Test_Leaf_SetGeneration(t *testing.T) { - t.Parallel() - - leaf := &Leaf{ - Generation: 1, - } - leaf.SetGeneration(2) - assert.Equal(t, &Leaf{Generation: 2}, leaf) -} - -func Test_Leaf_GetGeneration(t *testing.T) { - t.Parallel() - - const generation uint64 = 1 - leaf := &Leaf{ - Generation: generation, - } - assert.Equal(t, leaf.GetGeneration(), generation) -} diff --git a/internal/trie/node/hash.go b/internal/trie/node/hash.go index 85d997e6c7..5c9ef06f1b 100644 --- a/internal/trie/node/hash.go +++ b/internal/trie/node/hash.go @@ -10,92 +10,20 @@ import ( "github.com/ChainSafe/gossamer/lib/common" ) -// SetEncodingAndHash sets the encoding and hash slices -// given to the branch. Note it does not copy them, so beware. -func (b *Branch) SetEncodingAndHash(enc, hash []byte) { - b.Encoding = enc - b.HashDigest = hash -} - -// GetHash returns the hash of the branch. -// Note it does not copy it, so modifying -// the returned hash will modify the hash -// of the branch. -func (b *Branch) GetHash() []byte { - return b.HashDigest -} - -// EncodeAndHash returns the encoding of the branch and -// the blake2b hash digest of the encoding of the branch. -// If the encoding is less than 32 bytes, the hash returned -// is the encoding and not the hash of the encoding. -func (b *Branch) EncodeAndHash(isRoot bool) (encoding, hash []byte, err error) { - if !b.Dirty && b.Encoding != nil && b.HashDigest != nil { - return b.Encoding, b.HashDigest, nil - } - - buffer := pools.EncodingBuffers.Get().(*bytes.Buffer) - buffer.Reset() - defer pools.EncodingBuffers.Put(buffer) - - err = b.Encode(buffer) - if err != nil { - return nil, nil, err - } - - bufferBytes := buffer.Bytes() - - b.Encoding = make([]byte, len(bufferBytes)) - copy(b.Encoding, bufferBytes) - encoding = b.Encoding // no need to copy - - if !isRoot && buffer.Len() < 32 { - b.HashDigest = make([]byte, len(bufferBytes)) - copy(b.HashDigest, bufferBytes) - hash = b.HashDigest // no need to copy - return encoding, hash, nil - } - - // Note: using the sync.Pool's buffer is useful here. - hashArray, err := common.Blake2bHash(buffer.Bytes()) - if err != nil { - return nil, nil, err - } - b.HashDigest = hashArray[:] - hash = b.HashDigest // no need to copy - - return encoding, hash, nil -} - -// SetEncodingAndHash sets the encoding and hash slices -// given to the branch. Note it does not copy them, so beware. -func (l *Leaf) SetEncodingAndHash(enc, hash []byte) { - l.Encoding = enc - l.HashDigest = hash -} - -// GetHash returns the hash of the leaf. -// Note it does not copy it, so modifying -// the returned hash will modify the hash -// of the branch. -func (l *Leaf) GetHash() []byte { - return l.HashDigest -} - -// EncodeAndHash returns the encoding of the leaf and -// the blake2b hash digest of the encoding of the leaf. +// EncodeAndHash returns the encoding of the node and +// the blake2b hash digest of the encoding of the node. // If the encoding is less than 32 bytes, the hash returned // is the encoding and not the hash of the encoding. -func (l *Leaf) EncodeAndHash(isRoot bool) (encoding, hash []byte, err error) { - if !l.IsDirty() && l.Encoding != nil && l.HashDigest != nil { - return l.Encoding, l.HashDigest, nil +func (n *Node) EncodeAndHash(isRoot bool) (encoding, hash []byte, err error) { + if !n.Dirty && n.Encoding != nil && n.HashDigest != nil { + return n.Encoding, n.HashDigest, nil } buffer := pools.EncodingBuffers.Get().(*bytes.Buffer) buffer.Reset() defer pools.EncodingBuffers.Put(buffer) - err = l.Encode(buffer) + err = n.Encode(buffer) if err != nil { return nil, nil, err } @@ -104,14 +32,14 @@ func (l *Leaf) EncodeAndHash(isRoot bool) (encoding, hash []byte, err error) { // TODO remove this copying since it defeats the purpose of `buffer` // and the sync.Pool. - l.Encoding = make([]byte, len(bufferBytes)) - copy(l.Encoding, bufferBytes) - encoding = l.Encoding // no need to copy + n.Encoding = make([]byte, len(bufferBytes)) + copy(n.Encoding, bufferBytes) + encoding = n.Encoding // no need to copy - if !isRoot && len(bufferBytes) < 32 { - l.HashDigest = make([]byte, len(bufferBytes)) - copy(l.HashDigest, bufferBytes) - hash = l.HashDigest // no need to copy + if !isRoot && buffer.Len() < 32 { + n.HashDigest = make([]byte, len(bufferBytes)) + copy(n.HashDigest, bufferBytes) + hash = n.HashDigest // no need to copy return encoding, hash, nil } @@ -120,9 +48,8 @@ func (l *Leaf) EncodeAndHash(isRoot bool) (encoding, hash []byte, err error) { if err != nil { return nil, nil, err } - - l.HashDigest = hashArray[:] - hash = l.HashDigest // no need to copy + n.HashDigest = hashArray[:] + hash = n.HashDigest // no need to copy return encoding, hash, nil } diff --git a/internal/trie/node/hash_test.go b/internal/trie/node/hash_test.go index 93da431b90..b2d785342b 100644 --- a/internal/trie/node/hash_test.go +++ b/internal/trie/node/hash_test.go @@ -9,107 +9,79 @@ import ( "github.com/stretchr/testify/assert" ) -func Test_Branch_SetEncodingAndHash(t *testing.T) { - t.Parallel() - - branch := &Branch{ - Encoding: []byte{2}, - HashDigest: []byte{3}, - } - branch.SetEncodingAndHash([]byte{4}, []byte{5}) - - expectedBranch := &Branch{ - Encoding: []byte{4}, - HashDigest: []byte{5}, - } - assert.Equal(t, expectedBranch, branch) -} - -func Test_Branch_GetHash(t *testing.T) { - t.Parallel() - - branch := &Branch{ - HashDigest: []byte{3}, - } - hash := branch.GetHash() - - expectedHash := []byte{3} - assert.Equal(t, expectedHash, hash) -} - -func Test_Branch_EncodeAndHash(t *testing.T) { +func Test_Node_EncodeAndHash(t *testing.T) { t.Parallel() testCases := map[string]struct { - branch *Branch - expectedBranch *Branch - encoding []byte - hash []byte - isRoot bool - errWrapped error - errMessage string + node Node + expectedNode Node + encoding []byte + hash []byte + isRoot bool + errWrapped error + errMessage string }{ - "empty branch": { - branch: &Branch{}, - expectedBranch: &Branch{ - Encoding: []byte{0x80, 0x0, 0x0}, - HashDigest: []byte{0x80, 0x0, 0x0}, + "empty leaf": { + node: Node{}, + expectedNode: Node{ + Encoding: []byte{0x40, 0x0}, + HashDigest: []byte{0x40, 0x0}, }, - encoding: []byte{0x80, 0x0, 0x0}, - hash: []byte{0x80, 0x0, 0x0}, + encoding: []byte{0x40, 0x0}, + hash: []byte{0x40, 0x0}, isRoot: false, }, - "small branch encoding": { - branch: &Branch{ + "small leaf encoding": { + node: Node{ Key: []byte{1}, Value: []byte{2}, }, - expectedBranch: &Branch{ - Encoding: []byte{0xc1, 0x1, 0x0, 0x0, 0x4, 0x2}, - HashDigest: []byte{0xc1, 0x1, 0x0, 0x0, 0x4, 0x2}, + expectedNode: Node{ + Encoding: []byte{0x41, 0x1, 0x4, 0x2}, + HashDigest: []byte{0x41, 0x1, 0x4, 0x2}, }, - encoding: []byte{0xc1, 0x1, 0x0, 0x0, 0x4, 0x2}, - hash: []byte{0xc1, 0x1, 0x0, 0x0, 0x4, 0x2}, + encoding: []byte{0x41, 0x1, 0x4, 0x2}, + hash: []byte{0x41, 0x1, 0x4, 0x2}, isRoot: false, }, - "small branch encoding for root node": { - branch: &Branch{ + "small leaf encoding for root node": { + node: Node{ Key: []byte{1}, Value: []byte{2}, }, - expectedBranch: &Branch{ - Encoding: []byte{0xc1, 0x1, 0x0, 0x0, 0x4, 0x2}, - HashDigest: []byte{0x48, 0x3c, 0xf6, 0x87, 0xcc, 0x5a, 0x60, 0x42, 0xd3, 0xcf, 0xa6, 0x91, 0xe6, 0x88, 0xfb, 0xdc, 0x1b, 0x38, 0x39, 0x5d, 0x6, 0x0, 0xbf, 0xc3, 0xb, 0x4b, 0x5d, 0x6a, 0x37, 0xd9, 0xc5, 0x1c}, // nolint: lll + expectedNode: Node{ + Encoding: []byte{0x41, 0x1, 0x4, 0x2}, + HashDigest: []byte{0x60, 0x51, 0x6d, 0xb, 0xb6, 0xe1, 0xbb, 0xfb, 0x12, 0x93, 0xf1, 0xb2, 0x76, 0xea, 0x95, 0x5, 0xe9, 0xf4, 0xa4, 0xe7, 0xd9, 0x8f, 0x62, 0xd, 0x5, 0x11, 0x5e, 0xb, 0x85, 0x27, 0x4a, 0xe1}, //nolint: lll }, - encoding: []byte{0xc1, 0x1, 0x0, 0x0, 0x4, 0x2}, - hash: []byte{0x48, 0x3c, 0xf6, 0x87, 0xcc, 0x5a, 0x60, 0x42, 0xd3, 0xcf, 0xa6, 0x91, 0xe6, 0x88, 0xfb, 0xdc, 0x1b, 0x38, 0x39, 0x5d, 0x6, 0x0, 0xbf, 0xc3, 0xb, 0x4b, 0x5d, 0x6a, 0x37, 0xd9, 0xc5, 0x1c}, // nolint: lll + encoding: []byte{0x41, 0x1, 0x4, 0x2}, + hash: []byte{0x60, 0x51, 0x6d, 0xb, 0xb6, 0xe1, 0xbb, 0xfb, 0x12, 0x93, 0xf1, 0xb2, 0x76, 0xea, 0x95, 0x5, 0xe9, 0xf4, 0xa4, 0xe7, 0xd9, 0x8f, 0x62, 0xd, 0x5, 0x11, 0x5e, 0xb, 0x85, 0x27, 0x4a, 0xe1}, // nolint: lll isRoot: true, }, - "branch dirty with precomputed encoding and hash": { - branch: &Branch{ + "leaf dirty with precomputed encoding and hash": { + node: Node{ Key: []byte{1}, Value: []byte{2}, Dirty: true, Encoding: []byte{3}, HashDigest: []byte{4}, }, - expectedBranch: &Branch{ - Encoding: []byte{0xc1, 0x1, 0x0, 0x0, 0x4, 0x2}, - HashDigest: []byte{0xc1, 0x1, 0x0, 0x0, 0x4, 0x2}, + expectedNode: Node{ + Encoding: []byte{0x41, 0x1, 0x4, 0x2}, + HashDigest: []byte{0x41, 0x1, 0x4, 0x2}, }, - encoding: []byte{0xc1, 0x1, 0x0, 0x0, 0x4, 0x2}, - hash: []byte{0xc1, 0x1, 0x0, 0x0, 0x4, 0x2}, + encoding: []byte{0x41, 0x1, 0x4, 0x2}, + hash: []byte{0x41, 0x1, 0x4, 0x2}, isRoot: false, }, - "branch not dirty with precomputed encoding and hash": { - branch: &Branch{ + "leaf not dirty with precomputed encoding and hash": { + node: Node{ Key: []byte{1}, Value: []byte{2}, Dirty: false, Encoding: []byte{3}, HashDigest: []byte{4}, }, - expectedBranch: &Branch{ + expectedNode: Node{ Key: []byte{1}, Value: []byte{2}, Encoding: []byte{3}, @@ -119,138 +91,90 @@ func Test_Branch_EncodeAndHash(t *testing.T) { hash: []byte{4}, isRoot: false, }, - "large branch encoding": { - branch: &Branch{ + "large leaf encoding": { + node: Node{ Key: repeatBytes(65, 7), }, - expectedBranch: &Branch{ - Encoding: []byte{0xbf, 0x2, 0x7, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x0, 0x0}, //nolint:lll - HashDigest: []byte{0x6b, 0xd8, 0xcc, 0xac, 0x71, 0x77, 0x44, 0x17, 0xfe, 0xe0, 0xde, 0xda, 0xd5, 0x97, 0x6e, 0x69, 0xeb, 0xe9, 0xdd, 0x80, 0x1d, 0x4b, 0x51, 0xf1, 0x5b, 0xf3, 0x4a, 0x93, 0x27, 0x32, 0x2c, 0xb0}, //nolint:lll + expectedNode: Node{ + Encoding: []byte{0x7f, 0x2, 0x7, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x0}, //nolint:lll + HashDigest: []byte{0xfb, 0xae, 0x31, 0x4b, 0xef, 0x31, 0x9, 0xc7, 0x62, 0x99, 0x9d, 0x40, 0x9b, 0xd4, 0xdc, 0x64, 0xe7, 0x39, 0x46, 0x8b, 0xd3, 0xaf, 0xe8, 0x63, 0x9d, 0xf9, 0x41, 0x40, 0x76, 0x40, 0x10, 0xa3}, //nolint:lll }, - encoding: []byte{0xbf, 0x2, 0x7, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x0, 0x0}, //nolint:lll - hash: []byte{0x6b, 0xd8, 0xcc, 0xac, 0x71, 0x77, 0x44, 0x17, 0xfe, 0xe0, 0xde, 0xda, 0xd5, 0x97, 0x6e, 0x69, 0xeb, 0xe9, 0xdd, 0x80, 0x1d, 0x4b, 0x51, 0xf1, 0x5b, 0xf3, 0x4a, 0x93, 0x27, 0x32, 0x2c, 0xb0}, //nolint:lll + encoding: []byte{0x7f, 0x2, 0x7, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x0}, //nolint:lll + hash: []byte{0xfb, 0xae, 0x31, 0x4b, 0xef, 0x31, 0x9, 0xc7, 0x62, 0x99, 0x9d, 0x40, 0x9b, 0xd4, 0xdc, 0x64, 0xe7, 0x39, 0x46, 0x8b, 0xd3, 0xaf, 0xe8, 0x63, 0x9d, 0xf9, 0x41, 0x40, 0x76, 0x40, 0x10, 0xa3}, //nolint:lll isRoot: false, }, - } - - for name, testCase := range testCases { - testCase := testCase - t.Run(name, func(t *testing.T) { - t.Parallel() - - encoding, hash, err := testCase.branch.EncodeAndHash(testCase.isRoot) - - assert.ErrorIs(t, err, testCase.errWrapped) - if testCase.errWrapped != nil { - assert.EqualError(t, err, testCase.errMessage) - } - assert.Equal(t, testCase.encoding, encoding) - assert.Equal(t, testCase.hash, hash) - }) - } -} - -func Test_Leaf_SetEncodingAndHash(t *testing.T) { - t.Parallel() - - leaf := &Leaf{ - Encoding: []byte{2}, - HashDigest: []byte{3}, - } - leaf.SetEncodingAndHash([]byte{4}, []byte{5}) - - expectedLeaf := &Leaf{ - Encoding: []byte{4}, - HashDigest: []byte{5}, - } - assert.Equal(t, expectedLeaf, leaf) -} - -func Test_Leaf_GetHash(t *testing.T) { - t.Parallel() - - leaf := &Leaf{ - HashDigest: []byte{3}, - } - hash := leaf.GetHash() - - expectedHash := []byte{3} - assert.Equal(t, expectedHash, hash) -} - -func Test_Leaf_EncodeAndHash(t *testing.T) { - t.Parallel() - - testCases := map[string]struct { - leaf *Leaf - expectedLeaf *Leaf - encoding []byte - hash []byte - isRoot bool - errWrapped error - errMessage string - }{ - "empty leaf": { - leaf: &Leaf{}, - expectedLeaf: &Leaf{ - Encoding: []byte{0x40, 0x0}, - HashDigest: []byte{0x40, 0x0}, + "empty branch": { + node: Node{ + Children: make([]*Node, ChildrenCapacity), }, - encoding: []byte{0x40, 0x0}, - hash: []byte{0x40, 0x0}, + expectedNode: Node{ + Children: make([]*Node, ChildrenCapacity), + Encoding: []byte{0x80, 0x0, 0x0}, + HashDigest: []byte{0x80, 0x0, 0x0}, + }, + encoding: []byte{0x80, 0x0, 0x0}, + hash: []byte{0x80, 0x0, 0x0}, isRoot: false, }, - "small leaf encoding": { - leaf: &Leaf{ - Key: []byte{1}, - Value: []byte{2}, + "small branch encoding": { + node: Node{ + Children: make([]*Node, ChildrenCapacity), + Key: []byte{1}, + Value: []byte{2}, }, - expectedLeaf: &Leaf{ - Encoding: []byte{0x41, 0x1, 0x4, 0x2}, - HashDigest: []byte{0x41, 0x1, 0x4, 0x2}, + expectedNode: Node{ + Children: make([]*Node, ChildrenCapacity), + Encoding: []byte{0xc1, 0x1, 0x0, 0x0, 0x4, 0x2}, + HashDigest: []byte{0xc1, 0x1, 0x0, 0x0, 0x4, 0x2}, }, - encoding: []byte{0x41, 0x1, 0x4, 0x2}, - hash: []byte{0x41, 0x1, 0x4, 0x2}, + encoding: []byte{0xc1, 0x1, 0x0, 0x0, 0x4, 0x2}, + hash: []byte{0xc1, 0x1, 0x0, 0x0, 0x4, 0x2}, isRoot: false, }, - "small leaf encoding for root node": { - leaf: &Leaf{ - Key: []byte{1}, - Value: []byte{2}, + "small branch encoding for root node": { + node: Node{ + Children: make([]*Node, ChildrenCapacity), + Key: []byte{1}, + Value: []byte{2}, }, - expectedLeaf: &Leaf{ - Encoding: []byte{0x41, 0x1, 0x4, 0x2}, - HashDigest: []byte{0x60, 0x51, 0x6d, 0xb, 0xb6, 0xe1, 0xbb, 0xfb, 0x12, 0x93, 0xf1, 0xb2, 0x76, 0xea, 0x95, 0x5, 0xe9, 0xf4, 0xa4, 0xe7, 0xd9, 0x8f, 0x62, 0xd, 0x5, 0x11, 0x5e, 0xb, 0x85, 0x27, 0x4a, 0xe1}, //nolint: lll + expectedNode: Node{ + Children: make([]*Node, ChildrenCapacity), + Encoding: []byte{0xc1, 0x1, 0x0, 0x0, 0x4, 0x2}, + HashDigest: []byte{0x48, 0x3c, 0xf6, 0x87, 0xcc, 0x5a, 0x60, 0x42, 0xd3, 0xcf, 0xa6, 0x91, 0xe6, 0x88, 0xfb, 0xdc, 0x1b, 0x38, 0x39, 0x5d, 0x6, 0x0, 0xbf, 0xc3, 0xb, 0x4b, 0x5d, 0x6a, 0x37, 0xd9, 0xc5, 0x1c}, // nolint: lll }, - encoding: []byte{0x41, 0x1, 0x4, 0x2}, - hash: []byte{0x60, 0x51, 0x6d, 0xb, 0xb6, 0xe1, 0xbb, 0xfb, 0x12, 0x93, 0xf1, 0xb2, 0x76, 0xea, 0x95, 0x5, 0xe9, 0xf4, 0xa4, 0xe7, 0xd9, 0x8f, 0x62, 0xd, 0x5, 0x11, 0x5e, 0xb, 0x85, 0x27, 0x4a, 0xe1}, // nolint: lll + encoding: []byte{0xc1, 0x1, 0x0, 0x0, 0x4, 0x2}, + hash: []byte{0x48, 0x3c, 0xf6, 0x87, 0xcc, 0x5a, 0x60, 0x42, 0xd3, 0xcf, 0xa6, 0x91, 0xe6, 0x88, 0xfb, 0xdc, 0x1b, 0x38, 0x39, 0x5d, 0x6, 0x0, 0xbf, 0xc3, 0xb, 0x4b, 0x5d, 0x6a, 0x37, 0xd9, 0xc5, 0x1c}, // nolint: lll isRoot: true, }, - "leaf dirty with precomputed encoding and hash": { - leaf: &Leaf{ + "branch dirty with precomputed encoding and hash": { + node: Node{ + Children: make([]*Node, ChildrenCapacity), Key: []byte{1}, Value: []byte{2}, Dirty: true, Encoding: []byte{3}, HashDigest: []byte{4}, }, - expectedLeaf: &Leaf{ - Encoding: []byte{0x41, 0x1, 0x4, 0x2}, - HashDigest: []byte{0x41, 0x1, 0x4, 0x2}, + expectedNode: Node{ + Children: make([]*Node, ChildrenCapacity), + Encoding: []byte{0xc1, 0x1, 0x0, 0x0, 0x4, 0x2}, + HashDigest: []byte{0xc1, 0x1, 0x0, 0x0, 0x4, 0x2}, }, - encoding: []byte{0x41, 0x1, 0x4, 0x2}, - hash: []byte{0x41, 0x1, 0x4, 0x2}, + encoding: []byte{0xc1, 0x1, 0x0, 0x0, 0x4, 0x2}, + hash: []byte{0xc1, 0x1, 0x0, 0x0, 0x4, 0x2}, isRoot: false, }, - "leaf not dirty with precomputed encoding and hash": { - leaf: &Leaf{ + "branch not dirty with precomputed encoding and hash": { + node: Node{ + Children: make([]*Node, ChildrenCapacity), Key: []byte{1}, Value: []byte{2}, Dirty: false, Encoding: []byte{3}, HashDigest: []byte{4}, }, - expectedLeaf: &Leaf{ + expectedNode: Node{ + Children: make([]*Node, ChildrenCapacity), Key: []byte{1}, Value: []byte{2}, Encoding: []byte{3}, @@ -260,16 +184,18 @@ func Test_Leaf_EncodeAndHash(t *testing.T) { hash: []byte{4}, isRoot: false, }, - "large leaf encoding": { - leaf: &Leaf{ - Key: repeatBytes(65, 7), + "large branch encoding": { + node: Node{ + Children: make([]*Node, ChildrenCapacity), + Key: repeatBytes(65, 7), }, - expectedLeaf: &Leaf{ - Encoding: []byte{0x7f, 0x2, 0x7, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x0}, //nolint:lll - HashDigest: []byte{0xfb, 0xae, 0x31, 0x4b, 0xef, 0x31, 0x9, 0xc7, 0x62, 0x99, 0x9d, 0x40, 0x9b, 0xd4, 0xdc, 0x64, 0xe7, 0x39, 0x46, 0x8b, 0xd3, 0xaf, 0xe8, 0x63, 0x9d, 0xf9, 0x41, 0x40, 0x76, 0x40, 0x10, 0xa3}, //nolint:lll + expectedNode: Node{ + Children: make([]*Node, ChildrenCapacity), + Encoding: []byte{0xbf, 0x2, 0x7, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x0, 0x0}, //nolint:lll + HashDigest: []byte{0x6b, 0xd8, 0xcc, 0xac, 0x71, 0x77, 0x44, 0x17, 0xfe, 0xe0, 0xde, 0xda, 0xd5, 0x97, 0x6e, 0x69, 0xeb, 0xe9, 0xdd, 0x80, 0x1d, 0x4b, 0x51, 0xf1, 0x5b, 0xf3, 0x4a, 0x93, 0x27, 0x32, 0x2c, 0xb0}, //nolint:lll }, - encoding: []byte{0x7f, 0x2, 0x7, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x0}, //nolint:lll - hash: []byte{0xfb, 0xae, 0x31, 0x4b, 0xef, 0x31, 0x9, 0xc7, 0x62, 0x99, 0x9d, 0x40, 0x9b, 0xd4, 0xdc, 0x64, 0xe7, 0x39, 0x46, 0x8b, 0xd3, 0xaf, 0xe8, 0x63, 0x9d, 0xf9, 0x41, 0x40, 0x76, 0x40, 0x10, 0xa3}, //nolint:lll + encoding: []byte{0xbf, 0x2, 0x7, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x0, 0x0}, //nolint:lll + hash: []byte{0x6b, 0xd8, 0xcc, 0xac, 0x71, 0x77, 0x44, 0x17, 0xfe, 0xe0, 0xde, 0xda, 0xd5, 0x97, 0x6e, 0x69, 0xeb, 0xe9, 0xdd, 0x80, 0x1d, 0x4b, 0x51, 0xf1, 0x5b, 0xf3, 0x4a, 0x93, 0x27, 0x32, 0x2c, 0xb0}, //nolint:lll isRoot: false, }, } @@ -279,7 +205,7 @@ func Test_Leaf_EncodeAndHash(t *testing.T) { t.Run(name, func(t *testing.T) { t.Parallel() - encoding, hash, err := testCase.leaf.EncodeAndHash(testCase.isRoot) + encoding, hash, err := testCase.node.EncodeAndHash(testCase.isRoot) assert.ErrorIs(t, err, testCase.errWrapped) if testCase.errWrapped != nil { diff --git a/internal/trie/node/header.go b/internal/trie/node/header.go index 73b75c872c..5177b6f10c 100644 --- a/internal/trie/node/header.go +++ b/internal/trie/node/header.go @@ -7,62 +7,41 @@ import ( "io" ) +const ( + leafHeader byte = 1 // 01 + branchHeader byte = 2 // 10 + branchWithValueHeader byte = 3 // 11 +) + const ( keyLenOffset = 0x3f nodeHeaderShift = 6 ) -// encodeHeader creates the encoded header for the branch. -func (b *Branch) encodeHeader(writer io.Writer) (err error) { +// encodeHeader writes the encoded header for the node. +func encodeHeader(node *Node, writer io.Writer) (err error) { var header byte - if b.Value == nil { - header = byte(BranchType) << nodeHeaderShift - } else { - header = byte(BranchWithValueType) << nodeHeaderShift - } - - if len(b.Key) >= keyLenOffset { - header = header | keyLenOffset - _, err = writer.Write([]byte{header}) - if err != nil { - return err - } - - err = encodeKeyLength(len(b.Key), writer) - if err != nil { - return err - } + if node.Type() == Leaf { + header = leafHeader + } else if node.Value == nil { + header = branchHeader } else { - header = header | byte(len(b.Key)) - _, err = writer.Write([]byte{header}) - if err != nil { - return err - } + header = branchWithValueHeader } + header <<= nodeHeaderShift - return nil -} - -// encodeHeader creates the encoded header for the leaf. -func (l *Leaf) encodeHeader(writer io.Writer) (err error) { - header := byte(LeafType) << nodeHeaderShift - - if len(l.Key) < 63 { - header |= byte(len(l.Key)) + if len(node.Key) < keyLenOffset { + header |= byte(len(node.Key)) _, err = writer.Write([]byte{header}) return err } - header |= keyLenOffset + header = header | keyLenOffset _, err = writer.Write([]byte{header}) if err != nil { return err } - err = encodeKeyLength(len(l.Key), writer) - if err != nil { - return err - } - - return nil + err = encodeKeyLength(len(node.Key), writer) + return err } diff --git a/internal/trie/node/header_test.go b/internal/trie/node/header_test.go index 78f40344ca..1ed826483a 100644 --- a/internal/trie/node/header_test.go +++ b/internal/trie/node/header_test.go @@ -10,64 +10,72 @@ import ( "github.com/stretchr/testify/assert" ) -func Test_Branch_encodeHeader(t *testing.T) { +func Test_encodeHeader(t *testing.T) { testCases := map[string]struct { - branch *Branch + node *Node writes []writeCall errWrapped error errMessage string }{ - "no key": { - branch: &Branch{}, + "branch with no key": { + node: &Node{ + Children: make([]*Node, ChildrenCapacity), + }, writes: []writeCall{ {written: []byte{0x80}}, }, }, - "with value": { - branch: &Branch{ - Value: []byte{}, + "branch with value": { + node: &Node{ + Value: []byte{}, + Children: make([]*Node, ChildrenCapacity), }, writes: []writeCall{ {written: []byte{0xc0}}, }, }, - "key of length 30": { - branch: &Branch{ - Key: make([]byte, 30), + "branch with key of length 30": { + node: &Node{ + Key: make([]byte, 30), + Children: make([]*Node, ChildrenCapacity), }, writes: []writeCall{ {written: []byte{0x9e}}, }, }, - "key of length 62": { - branch: &Branch{ - Key: make([]byte, 62), + "branch with key of length 62": { + node: &Node{ + Key: make([]byte, 62), + Children: make([]*Node, ChildrenCapacity), }, writes: []writeCall{ {written: []byte{0xbe}}, }, }, - "key of length 63": { - branch: &Branch{ - Key: make([]byte, 63), + "branch with key of length 63": { + node: &Node{ + Key: make([]byte, 63), + Children: make([]*Node, ChildrenCapacity), }, writes: []writeCall{ {written: []byte{0xbf}}, {written: []byte{0x0}}, }, }, - "key of length 64": { - branch: &Branch{ - Key: make([]byte, 64), + "branch with key of length 64": { + node: &Node{ + Key: make([]byte, 64), + Children: make([]*Node, ChildrenCapacity), }, writes: []writeCall{ {written: []byte{0xbf}}, {written: []byte{0x1}}, }, }, - "key too big": { - branch: &Branch{ - Key: make([]byte, 65535+63), + "branch with key too big": { + node: &Node{ + Key: make([]byte, 65535+63), + Children: make([]*Node, ChildrenCapacity), }, writes: []writeCall{ {written: []byte{0xbf}}, @@ -75,8 +83,10 @@ func Test_Branch_encodeHeader(t *testing.T) { errWrapped: ErrPartialKeyTooBig, errMessage: "partial key length cannot be larger than or equal to 2^16: 65535", }, - "small key length write error": { - branch: &Branch{}, + "branch with small key length write error": { + node: &Node{ + Children: make([]*Node, ChildrenCapacity), + }, writes: []writeCall{ { written: []byte{0x80}, @@ -86,9 +96,10 @@ func Test_Branch_encodeHeader(t *testing.T) { errWrapped: errTest, errMessage: "test error", }, - "long key length write error": { - branch: &Branch{ - Key: make([]byte, 64), + "branch with long key length write error": { + node: &Node{ + Key: make([]byte, 64), + Children: make([]*Node, ChildrenCapacity), }, writes: []writeCall{ { @@ -99,60 +110,22 @@ func Test_Branch_encodeHeader(t *testing.T) { errWrapped: errTest, errMessage: "test error", }, - } - - for name, testCase := range testCases { - testCase := testCase - t.Run(name, func(t *testing.T) { - t.Parallel() - ctrl := gomock.NewController(t) - - writer := NewMockWriter(ctrl) - var previousCall *gomock.Call - for _, write := range testCase.writes { - call := writer.EXPECT(). - Write(write.written). - Return(write.n, write.err) - - if previousCall != nil { - call.After(previousCall) - } - previousCall = call - } - - err := testCase.branch.encodeHeader(writer) - - assert.ErrorIs(t, err, testCase.errWrapped) - if testCase.errWrapped != nil { - assert.EqualError(t, err, testCase.errMessage) - } - }) - } -} - -func Test_Leaf_encodeHeader(t *testing.T) { - testCases := map[string]struct { - leaf *Leaf - writes []writeCall - errWrapped error - errMessage string - }{ - "no key": { - leaf: &Leaf{}, + "leaf with no key": { + node: &Node{}, writes: []writeCall{ {written: []byte{0x40}}, }, }, - "key of length 30": { - leaf: &Leaf{ + "leaf with key of length 30": { + node: &Node{ Key: make([]byte, 30), }, writes: []writeCall{ {written: []byte{0x5e}}, }, }, - "short key write error": { - leaf: &Leaf{ + "leaf with short key write error": { + node: &Node{ Key: make([]byte, 30), }, writes: []writeCall{ @@ -164,16 +137,16 @@ func Test_Leaf_encodeHeader(t *testing.T) { errWrapped: errTest, errMessage: errTest.Error(), }, - "key of length 62": { - leaf: &Leaf{ + "leaf with key of length 62": { + node: &Node{ Key: make([]byte, 62), }, writes: []writeCall{ {written: []byte{0x7e}}, }, }, - "key of length 63": { - leaf: &Leaf{ + "leaf with key of length 63": { + node: &Node{ Key: make([]byte, 63), }, writes: []writeCall{ @@ -181,8 +154,8 @@ func Test_Leaf_encodeHeader(t *testing.T) { {written: []byte{0x0}}, }, }, - "key of length 64": { - leaf: &Leaf{ + "leaf with key of length 64": { + node: &Node{ Key: make([]byte, 64), }, writes: []writeCall{ @@ -190,8 +163,8 @@ func Test_Leaf_encodeHeader(t *testing.T) { {written: []byte{0x1}}, }, }, - "long key first byte write error": { - leaf: &Leaf{ + "leaf with long key first byte write error": { + node: &Node{ Key: make([]byte, 63), }, writes: []writeCall{ @@ -203,8 +176,8 @@ func Test_Leaf_encodeHeader(t *testing.T) { errWrapped: errTest, errMessage: errTest.Error(), }, - "key too big": { - leaf: &Leaf{ + "leaf with key too big": { + node: &Node{ Key: make([]byte, 65535+63), }, writes: []writeCall{ @@ -234,7 +207,7 @@ func Test_Leaf_encodeHeader(t *testing.T) { previousCall = call } - err := testCase.leaf.encodeHeader(writer) + err := encodeHeader(testCase.node, writer) assert.ErrorIs(t, err, testCase.errWrapped) if testCase.errWrapped != nil { diff --git a/internal/trie/node/helpers_test.go b/internal/trie/node/helpers_test.go new file mode 100644 index 0000000000..046def3fa6 --- /dev/null +++ b/internal/trie/node/helpers_test.go @@ -0,0 +1,10 @@ +// Copyright 2022 ChainSafe Systems (ON) +// SPDX-License-Identifier: LGPL-3.0-only + +package node + +func padRightChildren(slice []*Node) (paddedSlice []*Node) { + paddedSlice = make([]*Node, ChildrenCapacity) + copy(paddedSlice, slice) + return paddedSlice +} diff --git a/internal/trie/node/key.go b/internal/trie/node/key.go index 3478ef3aa7..3b450513bb 100644 --- a/internal/trie/node/key.go +++ b/internal/trie/node/key.go @@ -13,34 +13,6 @@ import ( "github.com/ChainSafe/gossamer/internal/trie/pools" ) -// GetKey returns the key of the branch. -// Note it does not copy the byte slice so modifying the returned -// byte slice will modify the byte slice of the branch. -func (b *Branch) GetKey() (value []byte) { - return b.Key -} - -// GetKey returns the key of the leaf. -// Note it does not copy the byte slice so modifying the returned -// byte slice will modify the byte slice of the leaf. -func (l *Leaf) GetKey() (value []byte) { - return l.Key -} - -// SetKey sets the key to the branch. -// Note it does not copy it so modifying the passed key -// will modify the key stored in the branch. -func (b *Branch) SetKey(key []byte) { - b.Key = key -} - -// SetKey sets the key to the leaf. -// Note it does not copy it so modifying the passed key -// will modify the key stored in the leaf. -func (l *Leaf) SetKey(key []byte) { - l.Key = key -} - const maxPartialKeySize = ^uint16(0) var ( diff --git a/internal/trie/node/key_test.go b/internal/trie/node/key_test.go index c3413c1628..2e21825cce 100644 --- a/internal/trie/node/key_test.go +++ b/internal/trie/node/key_test.go @@ -13,46 +13,6 @@ import ( "github.com/stretchr/testify/require" ) -func Test_Branch_GetKey(t *testing.T) { - t.Parallel() - - branch := &Branch{ - Key: []byte{2}, - } - key := branch.GetKey() - assert.Equal(t, []byte{2}, key) -} - -func Test_Leaf_GetKey(t *testing.T) { - t.Parallel() - - leaf := &Leaf{ - Key: []byte{2}, - } - key := leaf.GetKey() - assert.Equal(t, []byte{2}, key) -} - -func Test_Branch_SetKey(t *testing.T) { - t.Parallel() - - branch := &Branch{ - Key: []byte{2}, - } - branch.SetKey([]byte{3}) - assert.Equal(t, &Branch{Key: []byte{3}}, branch) -} - -func Test_Leaf_SetKey(t *testing.T) { - t.Parallel() - - leaf := &Leaf{ - Key: []byte{2}, - } - leaf.SetKey([]byte{3}) - assert.Equal(t, &Leaf{Key: []byte{3}}, leaf) -} - func repeatBytes(n int, b byte) (slice []byte) { slice = make([]byte, n) for i := range slice { diff --git a/internal/trie/node/leaf.go b/internal/trie/node/leaf.go deleted file mode 100644 index 129ebce3c1..0000000000 --- a/internal/trie/node/leaf.go +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright 2021 ChainSafe Systems (ON) -// SPDX-License-Identifier: LGPL-3.0-only - -package node - -import ( - "fmt" - - "github.com/qdm12/gotree" -) - -var _ Node = (*Leaf)(nil) - -// Leaf is a leaf in the trie. -type Leaf struct { - // Partial key bytes in nibbles (0 to f in hexadecimal) - Key []byte - Value []byte - // Dirty is true when the leaf differs - // from the node stored in the database. - Dirty bool - HashDigest []byte - Encoding []byte - // Generation is incremented on every trie Snapshot() call. - // Each node also contain a certain Generation number, - // which is updated to match the trie Generation once they are - // inserted, moved or iterated over. - Generation uint64 -} - -// NewLeaf creates a new leaf using the arguments given. -func NewLeaf(key, value []byte, dirty bool, generation uint64) *Leaf { - return &Leaf{ - Key: key, - Value: value, - Dirty: dirty, - Generation: generation, - } -} - -// Type returns LeafType. -func (l *Leaf) Type() Type { - return LeafType -} - -func (l *Leaf) String() string { - return l.StringNode().String() -} - -// StringNode returns a gotree compatible node for String methods. -func (l *Leaf) StringNode() (stringNode *gotree.Node) { - stringNode = gotree.New("Leaf") - stringNode.Appendf("Generation: %d", l.Generation) - stringNode.Appendf("Dirty: %t", l.Dirty) - stringNode.Appendf("Key: " + bytesToString(l.Key)) - stringNode.Appendf("Value: " + bytesToString(l.Value)) - stringNode.Appendf("Calculated encoding: " + bytesToString(l.Encoding)) - stringNode.Appendf("Calculated digest: " + bytesToString(l.HashDigest)) - return stringNode -} - -func bytesToString(b []byte) (s string) { - switch { - case b == nil: - return "nil" - case len(b) <= 20: - return fmt.Sprintf("0x%x", b) - default: - return fmt.Sprintf("0x%x...%x", b[:8], b[len(b)-8:]) - } -} diff --git a/internal/trie/node/leaf_encode.go b/internal/trie/node/leaf_encode.go deleted file mode 100644 index d57d05579e..0000000000 --- a/internal/trie/node/leaf_encode.go +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright 2021 ChainSafe Systems (ON) -// SPDX-License-Identifier: LGPL-3.0-only - -package node - -import ( - "bytes" - "fmt" - "hash" - "io" - - "github.com/ChainSafe/gossamer/internal/trie/codec" - "github.com/ChainSafe/gossamer/internal/trie/pools" - "github.com/ChainSafe/gossamer/pkg/scale" -) - -// Encode encodes a leaf to the buffer given. -// The encoding has the following format: -// NodeHeader | Extra partial key length | Partial Key | Value -func (l *Leaf) Encode(buffer Buffer) (err error) { - if !l.Dirty && l.Encoding != nil { - _, err = buffer.Write(l.Encoding) - if err != nil { - return fmt.Errorf("cannot write stored encoding to buffer: %w", err) - } - return nil - } - - err = l.encodeHeader(buffer) - if err != nil { - return fmt.Errorf("cannot encode header: %w", err) - } - - keyLE := codec.NibblesToKeyLE(l.Key) - _, err = buffer.Write(keyLE) - if err != nil { - return fmt.Errorf("cannot write LE key to buffer: %w", err) - } - - encodedValue, err := scale.Marshal(l.Value) // TODO scale encoder to write to buffer - if err != nil { - return fmt.Errorf("cannot scale marshal value: %w", err) - } - - _, err = buffer.Write(encodedValue) - if err != nil { - return fmt.Errorf("cannot write scale encoded value to buffer: %w", err) - } - - // TODO remove this copying since it defeats the purpose of `buffer` - // and the sync.Pool. - l.Encoding = make([]byte, buffer.Len()) - copy(l.Encoding, buffer.Bytes()) - return nil -} - -// ScaleEncodeHash hashes the node (blake2b sum on encoded value) -// and then SCALE encodes it. This is used to encode children -// nodes of branches. -func (l *Leaf) ScaleEncodeHash() (encoding []byte, err error) { - buffer := pools.DigestBuffers.Get().(*bytes.Buffer) - buffer.Reset() - defer pools.DigestBuffers.Put(buffer) - - err = l.hash(buffer) - if err != nil { - return nil, fmt.Errorf("cannot hash leaf: %w", err) - } - - scEncChild, err := scale.Marshal(buffer.Bytes()) - if err != nil { - return nil, fmt.Errorf("cannot scale encode hashed leaf: %w", err) - } - return scEncChild, nil -} - -func (l *Leaf) hash(writer io.Writer) (err error) { - encodingBuffer := pools.EncodingBuffers.Get().(*bytes.Buffer) - encodingBuffer.Reset() - defer pools.EncodingBuffers.Put(encodingBuffer) - - err = l.Encode(encodingBuffer) - if err != nil { - return fmt.Errorf("cannot encode leaf: %w", err) - } - - // if length of encoded leaf is less than 32 bytes, do not hash - if encodingBuffer.Len() < 32 { - _, err = writer.Write(encodingBuffer.Bytes()) - if err != nil { - return fmt.Errorf("cannot write encoded leaf to buffer: %w", err) - } - return nil - } - - // otherwise, hash encoded node - hasher := pools.Hashers.Get().(hash.Hash) - hasher.Reset() - defer pools.Hashers.Put(hasher) - - // Note: using the sync.Pool's buffer is useful here. - _, err = hasher.Write(encodingBuffer.Bytes()) - if err != nil { - return fmt.Errorf("cannot hash encoded node: %w", err) - } - - _, err = writer.Write(hasher.Sum(nil)) - if err != nil { - return fmt.Errorf("cannot write hash sum of leaf to buffer: %w", err) - } - return nil -} diff --git a/internal/trie/node/leaf_encode_test.go b/internal/trie/node/leaf_encode_test.go deleted file mode 100644 index fdac0713c8..0000000000 --- a/internal/trie/node/leaf_encode_test.go +++ /dev/null @@ -1,296 +0,0 @@ -// Copyright 2021 ChainSafe Systems (ON) -// SPDX-License-Identifier: LGPL-3.0-only - -package node - -import ( - "testing" - - "github.com/golang/mock/gomock" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func Test_Leaf_Encode(t *testing.T) { - t.Parallel() - - testCases := map[string]struct { - leaf *Leaf - writes []writeCall - bufferLenCall bool - bufferBytesCall bool - bufferBytes []byte - expectedEncoding []byte - wrappedErr error - errMessage string - }{ - "clean leaf with encoding": { - leaf: &Leaf{ - Encoding: []byte{1, 2, 3}, - }, - writes: []writeCall{ - { - written: []byte{1, 2, 3}, - }, - }, - expectedEncoding: []byte{1, 2, 3}, - }, - "write error for clean leaf with encoding": { - leaf: &Leaf{ - Encoding: []byte{1, 2, 3}, - }, - writes: []writeCall{ - { - written: []byte{1, 2, 3}, - err: errTest, - }, - }, - expectedEncoding: []byte{1, 2, 3}, - wrappedErr: errTest, - errMessage: "cannot write stored encoding to buffer: test error", - }, - "header encoding error": { - leaf: &Leaf{ - Key: make([]byte, 63+(1<<16)), - }, - writes: []writeCall{ - { - written: []byte{127}, - }, - }, - wrappedErr: ErrPartialKeyTooBig, - errMessage: "cannot encode header: partial key length cannot be larger than or equal to 2^16: 65536", - }, - "buffer write error for encoded key": { - leaf: &Leaf{ - Key: []byte{1, 2, 3}, - }, - writes: []writeCall{ - { - written: []byte{67}, - }, - { - written: []byte{1, 35}, - err: errTest, - }, - }, - wrappedErr: errTest, - errMessage: "cannot write LE key to buffer: test error", - }, - "buffer write error for encoded value": { - leaf: &Leaf{ - Key: []byte{1, 2, 3}, - Value: []byte{4, 5, 6}, - }, - writes: []writeCall{ - { - written: []byte{67}, - }, - { - written: []byte{1, 35}, - }, - { - written: []byte{12, 4, 5, 6}, - err: errTest, - }, - }, - wrappedErr: errTest, - errMessage: "cannot write scale encoded value to buffer: test error", - }, - "success": { - leaf: &Leaf{ - Key: []byte{1, 2, 3}, - Value: []byte{4, 5, 6}, - }, - writes: []writeCall{ - { - written: []byte{67}, - }, - { - written: []byte{1, 35}, - }, - { - written: []byte{12, 4, 5, 6}, - }, - }, - bufferLenCall: true, - bufferBytesCall: true, - bufferBytes: []byte{1, 2, 3}, - expectedEncoding: []byte{1, 2, 3}, - }, - } - - for name, testCase := range testCases { - testCase := testCase - t.Run(name, func(t *testing.T) { - t.Parallel() - ctrl := gomock.NewController(t) - - buffer := NewMockBuffer(ctrl) - var previousCall *gomock.Call - for _, write := range testCase.writes { - call := buffer.EXPECT(). - Write(write.written). - Return(write.n, write.err) - - if previousCall != nil { - call.After(previousCall) - } - previousCall = call - } - if testCase.bufferLenCall { - buffer.EXPECT().Len().Return(len(testCase.bufferBytes)) - } - if testCase.bufferBytesCall { - buffer.EXPECT().Bytes().Return(testCase.bufferBytes) - } - - err := testCase.leaf.Encode(buffer) - - if testCase.wrappedErr != nil { - assert.ErrorIs(t, err, testCase.wrappedErr) - assert.EqualError(t, err, testCase.errMessage) - } else { - require.NoError(t, err) - } - assert.Equal(t, testCase.expectedEncoding, testCase.leaf.Encoding) - }) - } -} - -func Test_Leaf_ScaleEncodeHash(t *testing.T) { - t.Parallel() - - testCases := map[string]struct { - leaf *Leaf - b []byte - wrappedErr error - errMessage string - }{ - "leaf": { - leaf: &Leaf{}, - b: []byte{0x8, 0x40, 0}, - }, - } - - for name, testCase := range testCases { - testCase := testCase - t.Run(name, func(t *testing.T) { - t.Parallel() - - b, err := testCase.leaf.ScaleEncodeHash() - - if testCase.wrappedErr != nil { - assert.ErrorIs(t, err, testCase.wrappedErr) - assert.EqualError(t, err, testCase.errMessage) - } else { - require.NoError(t, err) - } - - assert.Equal(t, testCase.b, b) - }) - } -} - -func Test_Leaf_hash(t *testing.T) { - t.Parallel() - - testCases := map[string]struct { - leaf *Leaf - writeCall bool - write writeCall - wrappedErr error - errMessage string - }{ - "small leaf buffer write error": { - leaf: &Leaf{ - Encoding: []byte{1, 2, 3}, - }, - writeCall: true, - write: writeCall{ - written: []byte{1, 2, 3}, - err: errTest, - }, - wrappedErr: errTest, - errMessage: "cannot write encoded leaf to buffer: " + - "test error", - }, - "small leaf success": { - leaf: &Leaf{ - Encoding: []byte{1, 2, 3}, - }, - writeCall: true, - write: writeCall{ - written: []byte{1, 2, 3}, - }, - }, - "leaf hash sum buffer write error": { - leaf: &Leaf{ - Encoding: []byte{ - 1, 2, 3, 4, 5, 6, 7, 8, - 1, 2, 3, 4, 5, 6, 7, 8, - 1, 2, 3, 4, 5, 6, 7, 8, - 1, 2, 3, 4, 5, 6, 7, 8, - 1, 2, 3, 4, 5, 6, 7, 8, - }, - }, - writeCall: true, - write: writeCall{ - written: []byte{ - 107, 105, 154, 175, 253, 170, 232, - 135, 240, 21, 207, 148, 82, 117, - 249, 230, 80, 197, 254, 17, 149, - 108, 50, 7, 80, 56, 114, 176, - 84, 114, 125, 234}, - err: errTest, - }, - wrappedErr: errTest, - errMessage: "cannot write hash sum of leaf to buffer: " + - "test error", - }, - "leaf hash sum success": { - leaf: &Leaf{ - Encoding: []byte{ - 1, 2, 3, 4, 5, 6, 7, 8, - 1, 2, 3, 4, 5, 6, 7, 8, - 1, 2, 3, 4, 5, 6, 7, 8, - 1, 2, 3, 4, 5, 6, 7, 8, - 1, 2, 3, 4, 5, 6, 7, 8, - }, - }, - writeCall: true, - write: writeCall{ - written: []byte{ - 107, 105, 154, 175, 253, 170, 232, - 135, 240, 21, 207, 148, 82, 117, - 249, 230, 80, 197, 254, 17, 149, - 108, 50, 7, 80, 56, 114, 176, - 84, 114, 125, 234}, - }, - }, - } - - for name, testCase := range testCases { - testCase := testCase - t.Run(name, func(t *testing.T) { - t.Parallel() - ctrl := gomock.NewController(t) - - writer := NewMockWriter(ctrl) - if testCase.writeCall { - writer.EXPECT(). - Write(testCase.write.written). - Return(testCase.write.n, testCase.write.err) - } - - err := testCase.leaf.hash(writer) - - if testCase.wrappedErr != nil { - assert.ErrorIs(t, err, testCase.wrappedErr) - assert.EqualError(t, err, testCase.errMessage) - } else { - require.NoError(t, err) - } - }) - } -} diff --git a/internal/trie/node/leaf_test.go b/internal/trie/node/leaf_test.go deleted file mode 100644 index 17da81b077..0000000000 --- a/internal/trie/node/leaf_test.go +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright 2021 ChainSafe Systems (ON) -// SPDX-License-Identifier: LGPL-3.0-only - -package node - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func Test_NewLeaf(t *testing.T) { - t.Parallel() - - key := []byte{1, 2} - value := []byte{3, 4} - const dirty = true - const generation = 9 - - leaf := NewLeaf(key, value, dirty, generation) - - expectedLeaf := &Leaf{ - Key: key, - Value: value, - Dirty: dirty, - Generation: generation, - } - assert.Equal(t, expectedLeaf, leaf) - - // Check modifying passed slice modifies leaf slices - key[0] = 11 - value[0] = 13 - assert.Equal(t, expectedLeaf, leaf) -} - -func Test_Leaf_Type(t *testing.T) { - t.Parallel() - - leaf := new(Leaf) - - Type := leaf.Type() - - assert.Equal(t, LeafType, Type) -} - -func Test_Leaf_String(t *testing.T) { - t.Parallel() - - testCases := map[string]struct { - leaf *Leaf - s string - }{ - "empty leaf": { - leaf: &Leaf{}, - s: `Leaf -├── Generation: 0 -├── Dirty: false -├── Key: nil -├── Value: nil -├── Calculated encoding: nil -└── Calculated digest: nil`, - }, - "leaf with value smaller than 1024": { - leaf: &Leaf{ - Key: []byte{1, 2}, - Value: []byte{3, 4}, - Dirty: true, - }, - s: `Leaf -├── Generation: 0 -├── Dirty: true -├── Key: 0x0102 -├── Value: 0x0304 -├── Calculated encoding: nil -└── Calculated digest: nil`, - }, - "leaf with value higher than 1024": { - leaf: &Leaf{ - Key: []byte{1, 2}, - Value: make([]byte, 1025), - Dirty: true, - }, - s: `Leaf -├── Generation: 0 -├── Dirty: true -├── Key: 0x0102 -├── Value: 0x0000000000000000...0000000000000000 -├── Calculated encoding: nil -└── Calculated digest: nil`, - }, - } - - for name, testCase := range testCases { - testCase := testCase - t.Run(name, func(t *testing.T) { - t.Parallel() - - s := testCase.leaf.String() - - assert.Equal(t, testCase.s, s) - }) - } -} diff --git a/internal/trie/node/node.go b/internal/trie/node/node.go index eee1dcc96a..493ca1de91 100644 --- a/internal/trie/node/node.go +++ b/internal/trie/node/node.go @@ -3,24 +3,87 @@ package node -import "github.com/qdm12/gotree" +import ( + "fmt" + + "github.com/qdm12/gotree" + "golang.org/x/text/cases" + "golang.org/x/text/language" +) // Node is a node in the trie and can be a leaf or a branch. -type Node interface { - Encode(buffer Buffer) (err error) // TODO change to io.Writer - EncodeAndHash(isRoot bool) (encoding []byte, hash []byte, err error) - ScaleEncodeHash() (encoding []byte, err error) - IsDirty() bool - SetDirty(dirty bool) - SetKey(key []byte) - String() string - SetEncodingAndHash(encoding []byte, hash []byte) - GetHash() (hash []byte) - GetKey() (key []byte) - GetValue() (value []byte) - GetGeneration() (generation uint64) - SetGeneration(generation uint64) - Copy(settings CopySettings) (cpy Node) - Type() Type - StringNode() (stringNode *gotree.Node) +type Node struct { + // Key is the partial key bytes in nibbles (0 to f in hexadecimal) + Key []byte + Value []byte + // Generation is incremented on every trie Snapshot() call. + // Each node also contain a certain Generation number, + // which is updated to match the trie Generation once they are + // inserted, moved or iterated over. + Generation uint64 + // Children is a slice of length 16 for branches. + // It is left to nil for leaves to reduce memory usage. + Children []*Node + + // Dirty is true when the branch differs + // from the node stored in the database. + Dirty bool + // HashDigest is the cached hash digest of the + // node encoding. + HashDigest []byte + // Encoding is the cached encoding of the node. + Encoding []byte + + // Descendants is the number of descendant nodes for + // this particular node. + Descendants uint32 +} + +// Type returns Leaf or Branch depending on what type +// the node is. +func (n *Node) Type() Type { + if n.Children != nil { + return Branch + } + return Leaf +} + +func (n *Node) String() string { + return n.StringNode().String() +} + +// StringNode returns a gotree compatible node for String methods. +func (n Node) StringNode() (stringNode *gotree.Node) { + caser := cases.Title(language.BritishEnglish) + stringNode = gotree.New(caser.String(n.Type().String())) + stringNode.Appendf("Generation: %d", n.Generation) + stringNode.Appendf("Dirty: %t", n.Dirty) + stringNode.Appendf("Key: " + bytesToString(n.Key)) + stringNode.Appendf("Value: " + bytesToString(n.Value)) + if n.Descendants > 0 { // must be a branch + stringNode.Appendf("Descendants: %d", n.Descendants) + } + stringNode.Appendf("Calculated encoding: " + bytesToString(n.Encoding)) + stringNode.Appendf("Calculated digest: " + bytesToString(n.HashDigest)) + + for i, child := range n.Children { + if child == nil { + continue + } + childNode := stringNode.Appendf("Child %d", i) + childNode.AppendNode(child.StringNode()) + } + + return stringNode +} + +func bytesToString(b []byte) (s string) { + switch { + case b == nil: + return "nil" + case len(b) <= 20: + return fmt.Sprintf("0x%x", b) + default: + return fmt.Sprintf("0x%x...%x", b[:8], b[len(b)-8:]) + } } diff --git a/internal/trie/node/branch_test.go b/internal/trie/node/node_test.go similarity index 61% rename from internal/trie/node/branch_test.go rename to internal/trie/node/node_test.go index 8e3d56e6bd..a16f3c8912 100644 --- a/internal/trie/node/branch_test.go +++ b/internal/trie/node/node_test.go @@ -9,96 +9,67 @@ import ( "github.com/stretchr/testify/assert" ) -func Test_NewBranch(t *testing.T) { +func Test_Node_String(t *testing.T) { t.Parallel() - key := []byte{1, 2} - value := []byte{3, 4} - const dirty = true - const generation = 9 - - branch := NewBranch(key, value, dirty, generation) - - expectedBranch := &Branch{ - Key: key, - Value: value, - Dirty: dirty, - Generation: generation, - } - assert.Equal(t, expectedBranch, branch) - - // Check modifying passed slice modifies branch slices - key[0] = 11 - value[0] = 13 - assert.Equal(t, expectedBranch, branch) -} - -func Test_Branch_Type(t *testing.T) { testCases := map[string]struct { - branch *Branch - Type Type + node *Node + s string }{ - "nil value": { - branch: &Branch{}, - Type: BranchType, + "empty leaf": { + node: &Node{}, + s: `Leaf +├── Generation: 0 +├── Dirty: false +├── Key: nil +├── Value: nil +├── Calculated encoding: nil +└── Calculated digest: nil`, }, - "empty value": { - branch: &Branch{ - Value: []byte{}, + "leaf with value smaller than 1024": { + node: &Node{ + Key: []byte{1, 2}, + Value: []byte{3, 4}, + Dirty: true, }, - Type: BranchWithValueType, + s: `Leaf +├── Generation: 0 +├── Dirty: true +├── Key: 0x0102 +├── Value: 0x0304 +├── Calculated encoding: nil +└── Calculated digest: nil`, }, - "non empty value": { - branch: &Branch{ - Value: []byte{1}, + "leaf with value higher than 1024": { + node: &Node{ + Key: []byte{1, 2}, + Value: make([]byte, 1025), + Dirty: true, }, - Type: BranchWithValueType, - }, - } - - for name, testCase := range testCases { - testCase := testCase - t.Run(name, func(t *testing.T) { - t.Parallel() - - Type := testCase.branch.Type() - - assert.Equal(t, testCase.Type, Type) - }) - } -} - -func Test_Branch_String(t *testing.T) { - t.Parallel() - - testCases := map[string]struct { - branch *Branch - s string - }{ - "empty branch": { - branch: &Branch{}, - s: `Branch + s: `Leaf ├── Generation: 0 -├── Dirty: false -├── Key: nil -├── Value: nil -├── Descendants: 0 +├── Dirty: true +├── Key: 0x0102 +├── Value: 0x0000000000000000...0000000000000000 ├── Calculated encoding: nil └── Calculated digest: nil`, }, "branch with value smaller than 1024": { - branch: &Branch{ + node: &Node{ Key: []byte{1, 2}, Value: []byte{3, 4}, Dirty: true, Descendants: 3, - Children: [16]Node{ + Children: []*Node{ nil, nil, nil, - &Leaf{}, + {}, nil, nil, nil, - &Branch{}, + { + Descendants: 1, + Children: padRightChildren([]*Node{{}}), + }, nil, nil, nil, - &Leaf{}, + {}, nil, nil, nil, nil, }, }, @@ -124,9 +95,17 @@ func Test_Branch_String(t *testing.T) { | ├── Dirty: false | ├── Key: nil | ├── Value: nil -| ├── Descendants: 0 +| ├── Descendants: 1 | ├── Calculated encoding: nil -| └── Calculated digest: nil +| ├── Calculated digest: nil +| └── Child 0 +| └── Leaf +| ├── Generation: 0 +| ├── Dirty: false +| ├── Key: nil +| ├── Value: nil +| ├── Calculated encoding: nil +| └── Calculated digest: nil └── Child 11 └── Leaf ├── Generation: 0 @@ -137,18 +116,21 @@ func Test_Branch_String(t *testing.T) { └── Calculated digest: nil`, }, "branch with value higher than 1024": { - branch: &Branch{ + node: &Node{ Key: []byte{1, 2}, Value: make([]byte, 1025), Dirty: true, Descendants: 3, - Children: [16]Node{ + Children: []*Node{ nil, nil, nil, - &Leaf{}, + {}, nil, nil, nil, - &Branch{}, + { + Descendants: 1, + Children: padRightChildren([]*Node{{}}), + }, nil, nil, nil, - &Leaf{}, + {}, nil, nil, nil, nil, }, }, @@ -174,9 +156,17 @@ func Test_Branch_String(t *testing.T) { | ├── Dirty: false | ├── Key: nil | ├── Value: nil -| ├── Descendants: 0 +| ├── Descendants: 1 | ├── Calculated encoding: nil -| └── Calculated digest: nil +| ├── Calculated digest: nil +| └── Child 0 +| └── Leaf +| ├── Generation: 0 +| ├── Dirty: false +| ├── Key: nil +| ├── Value: nil +| ├── Calculated encoding: nil +| └── Calculated digest: nil └── Child 11 └── Leaf ├── Generation: 0 @@ -193,7 +183,7 @@ func Test_Branch_String(t *testing.T) { t.Run(name, func(t *testing.T) { t.Parallel() - s := testCase.branch.String() + s := testCase.node.String() assert.Equal(t, testCase.s, s) }) diff --git a/internal/trie/node/stats.go b/internal/trie/node/stats.go deleted file mode 100644 index 61ad8045cc..0000000000 --- a/internal/trie/node/stats.go +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2022 ChainSafe Systems (ON) -// SPDX-License-Identifier: LGPL-3.0-only - -package node - -// GetDescendants returns the number of descendants in the branch. -func (b *Branch) GetDescendants() (descendants uint32) { - return b.Descendants -} - -// AddDescendants adds descendant nodes count to the node stats. -func (b *Branch) AddDescendants(n uint32) { - b.Descendants += n -} - -// SubDescendants subtracts descendant nodes count from the node stats. -func (b *Branch) SubDescendants(n uint32) { - b.Descendants -= n -} diff --git a/internal/trie/node/stats_test.go b/internal/trie/node/stats_test.go deleted file mode 100644 index d96563d0dc..0000000000 --- a/internal/trie/node/stats_test.go +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2022 ChainSafe Systems (ON) -// SPDX-License-Identifier: LGPL-3.0-only - -package node - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func Test_Branch_GetDescendants(t *testing.T) { - t.Parallel() - - const descendants uint32 = 10 - branch := &Branch{ - Descendants: descendants, - } - result := branch.GetDescendants() - - assert.Equal(t, descendants, result) -} - -func Test_Branch_AddDescendants(t *testing.T) { - t.Parallel() - - const ( - initialDescendants uint32 = 10 - addDescendants uint32 = 2 - finalDescendants uint32 = 12 - ) - branch := &Branch{ - Descendants: initialDescendants, - } - branch.AddDescendants(addDescendants) - expected := &Branch{ - Descendants: finalDescendants, - } - - assert.Equal(t, expected, branch) -} - -func Test_Branch_SubDescendants(t *testing.T) { - t.Parallel() - - const ( - initialDescendants uint32 = 10 - subDescendants uint32 = 2 - finalDescendants uint32 = 8 - ) - branch := &Branch{ - Descendants: initialDescendants, - } - branch.SubDescendants(subDescendants) - expected := &Branch{ - Descendants: finalDescendants, - } - - assert.Equal(t, expected, branch) -} diff --git a/internal/trie/node/types.go b/internal/trie/node/types.go index 33e31a651d..ea4a9be136 100644 --- a/internal/trie/node/types.go +++ b/internal/trie/node/types.go @@ -3,17 +3,25 @@ package node -// Type is the byte type for the node. +import "fmt" + +// Type is the type of the node. type Type byte const ( - _ Type = iota - // LeafType type is 1 - LeafType - // BranchType type is 2 - BranchType - // BranchWithValueType type is 3 - BranchWithValueType - // InvalidType is used in tests only - InvalidType + // Leaf type for leaf nodes. + Leaf Type = iota + // Branch type for branches (with or without value). + Branch ) + +func (t Type) String() string { + switch t { + case Leaf: + return "leaf" + case Branch: + return "branch" + default: + panic(fmt.Sprintf("invalid node type: %d", t)) + } +} diff --git a/internal/trie/node/value.go b/internal/trie/node/value.go deleted file mode 100644 index 5ab07fb589..0000000000 --- a/internal/trie/node/value.go +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2021 ChainSafe Systems (ON) -// SPDX-License-Identifier: LGPL-3.0-only - -package node - -// GetValue returns the value of the branch. -// Note it does not copy the byte slice so modifying the returned -// byte slice will modify the byte slice of the branch. -func (b *Branch) GetValue() (value []byte) { - return b.Value -} - -// GetValue returns the value of the leaf. -// Note it does not copy the byte slice so modifying the returned -// byte slice will modify the byte slice of the leaf. -func (l *Leaf) GetValue() (value []byte) { - return l.Value -} diff --git a/internal/trie/node/value_test.go b/internal/trie/node/value_test.go deleted file mode 100644 index f6fe989d1d..0000000000 --- a/internal/trie/node/value_test.go +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2021 ChainSafe Systems (ON) -// SPDX-License-Identifier: LGPL-3.0-only - -package node - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func Test_Branch_GetValue(t *testing.T) { - t.Parallel() - - branch := &Branch{ - Value: []byte{2}, - } - value := branch.GetValue() - assert.Equal(t, []byte{2}, value) -} - -func Test_Leaf_GetValue(t *testing.T) { - t.Parallel() - - leaf := &Leaf{ - Value: []byte{2}, - } - value := leaf.GetValue() - assert.Equal(t, []byte{2}, value) -} diff --git a/lib/trie/database.go b/lib/trie/database.go index 772559f43c..6c300a8b04 100644 --- a/lib/trie/database.go +++ b/lib/trie/database.go @@ -27,7 +27,7 @@ var ( 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) + return fmt.Errorf("failed to store child trie with root hash=0x%x in the db: %w", v.root.HashDigest, err) } } @@ -41,7 +41,7 @@ func (t *Trie) Store(db chaindb.Database) error { return batch.Flush() } -func (t *Trie) store(db chaindb.Batch, n Node) error { +func (t *Trie) store(db chaindb.Batch, n *Node) error { if n == nil { return nil } @@ -56,10 +56,8 @@ func (t *Trie) store(db chaindb.Batch, n Node) error { return err } - switch n.Type() { - case node.BranchType, node.BranchWithValueType: - branch := n.(*node.Branch) - for _, child := range branch.Children { + if n.Type() == node.Branch { + for _, child := range n.Children { if child == nil { continue } @@ -71,7 +69,7 @@ func (t *Trie) store(db chaindb.Batch, n Node) error { } } - if n.IsDirty() { + if n.Dirty { n.SetDirty(false) } @@ -86,7 +84,7 @@ func (t *Trie) LoadFromProof(proofEncodedNodes [][]byte, rootHash []byte) error return ErrEmptyProof } - proofHashToNode := make(map[string]Node, len(proofEncodedNodes)) + proofHashToNode := make(map[string]*Node, len(proofEncodedNodes)) for i, rawNode := range proofEncodedNodes { decodedNode, err := node.Decode(bytes.NewReader(rawNode)) @@ -97,7 +95,8 @@ func (t *Trie) LoadFromProof(proofEncodedNodes [][]byte, rootHash []byte) error const dirty = false decodedNode.SetDirty(dirty) - decodedNode.SetEncodingAndHash(rawNode, nil) + decodedNode.Encoding = rawNode + decodedNode.HashDigest = nil _, hash, err := decodedNode.EncodeAndHash(false) if err != nil { @@ -120,21 +119,18 @@ func (t *Trie) LoadFromProof(proofEncodedNodes [][]byte, rootHash []byte) error // loadProof is a recursive function that will create all the trie paths based // on the mapped proofs slice starting at the root -func (t *Trie) loadProof(proofHashToNode map[string]Node, n Node) { - switch n.Type() { - case node.BranchType, node.BranchWithValueType: - default: +func (t *Trie) loadProof(proofHashToNode map[string]*Node, n *Node) { + if n.Type() != node.Branch { return } - branch := n.(*node.Branch) - + branch := n for i, child := range branch.Children { if child == nil { continue } - proofHash := common.BytesToHex(child.GetHash()) + proofHash := common.BytesToHex(child.HashDigest) node, ok := proofHashToNode[proofHash] if !ok { continue @@ -167,29 +163,26 @@ func (t *Trie) Load(db chaindb.Database, rootHash common.Hash) error { t.root = root t.root.SetDirty(false) - t.root.SetEncodingAndHash(encodedNode, rootHashBytes) + t.root.Encoding = encodedNode + t.root.HashDigest = rootHashBytes return t.load(db, t.root) } -func (t *Trie) load(db chaindb.Database, n Node) error { - switch n.Type() { - case node.BranchType, node.BranchWithValueType: - default: // not a branch +func (t *Trie) load(db chaindb.Database, n *Node) error { + if n.Type() != node.Branch { return nil } - branch := n.(*node.Branch) - + branch := n for i, child := range branch.Children { if child == nil { continue } - hash := child.GetHash() + hash := child.HashDigest - _, isLeaf := child.(*node.Leaf) - if len(hash) == 0 && isLeaf { + if len(hash) == 0 && child.Type() == node.Leaf { // node has already been loaded inline // just set encoding + hash digest _, _, err := child.EncodeAndHash(false) @@ -212,7 +205,8 @@ func (t *Trie) load(db chaindb.Database, n Node) error { } decodedNode.SetDirty(false) - decodedNode.SetEncodingAndHash(encodedNode, hash) + decodedNode.Encoding = encodedNode + decodedNode.HashDigest = hash branch.Children[i] = decodedNode err = t.load(db, decodedNode) @@ -220,15 +214,15 @@ func (t *Trie) load(db chaindb.Database, n Node) error { return fmt.Errorf("cannot load child at index %d with hash 0x%x: %w", i, hash, err) } - if decodedNode.Type() != node.LeafType { // branch decoded node + if decodedNode.Type() == node.Branch { // Note 1: the node is fully loaded with all its descendants // count only after the database load above. // Note 2: direct child node is already counted as descendant // when it was read as a leaf with hash only in decodeBranch, // so we only add the descendants of the child branch to the // current branch. - childBranchDescendants := decodedNode.(*node.Branch).Descendants - branch.AddDescendants(childBranchDescendants) + childBranchDescendants := decodedNode.Descendants + branch.Descendants += childBranchDescendants } } @@ -253,21 +247,18 @@ func (t *Trie) load(db chaindb.Database, n Node) error { // PopulateNodeHashes writes hashes of each children of the node given // as keys to the map hashesSet. -func (t *Trie) PopulateNodeHashes(n Node, hashesSet map[common.Hash]struct{}) { - switch n.Type() { - case node.BranchType, node.BranchWithValueType: - default: +func (t *Trie) PopulateNodeHashes(n *Node, hashesSet map[common.Hash]struct{}) { + if n.Type() != node.Branch { return } - branch := n.(*node.Branch) - + branch := n for _, child := range branch.Children { if child == nil { continue } - hash := common.BytesToHash(child.GetHash()) + hash := common.BytesToHash(child.HashDigest) hashesSet[hash] = struct{}{} t.PopulateNodeHashes(child, hashesSet) @@ -329,17 +320,16 @@ func GetFromDB(db chaindb.Database, rootHash common.Hash, key []byte) ( // for the value corresponding to a key. // Note it does not copy the value so modifying the value bytes // slice will modify the value of the node in the trie. -func getFromDB(db chaindb.Database, n Node, key []byte) ( +func getFromDB(db chaindb.Database, n *Node, key []byte) ( value []byte, err error) { - leaf, ok := n.(*node.Leaf) - if ok { - if bytes.Equal(leaf.Key, key) { - return leaf.Value, nil + if n.Type() == node.Leaf { + if bytes.Equal(n.Key, key) { + return n.Value, nil } return nil, nil } - branch := n.(*node.Branch) + branch := n // Key is equal to the key of this branch or is empty if len(key) == 0 || bytes.Equal(branch.Key, key) { return branch.Value, nil @@ -361,9 +351,8 @@ func getFromDB(db chaindb.Database, n Node, key []byte) ( } // Child can be either inlined or a hash pointer. - childHash := child.GetHash() - _, isLeaf := child.(*node.Leaf) - if len(childHash) == 0 && isLeaf { + childHash := child.HashDigest + if len(childHash) == 0 && child.Type() == node.Leaf { return getFromDB(db, child, key[commonPrefixLength+1:]) } @@ -398,8 +387,8 @@ func (t *Trie) WriteDirty(db chaindb.Database) error { return batch.Flush() } -func (t *Trie) writeDirty(db chaindb.Batch, n Node) error { - if n == nil || !n.IsDirty() { +func (t *Trie) writeDirty(db chaindb.Batch, n *Node) error { + if n == nil || !n.Dirty { return nil } @@ -407,7 +396,7 @@ func (t *Trie) writeDirty(db chaindb.Batch, n Node) error { if err != nil { return fmt.Errorf( "cannot encode and hash node with hash 0x%x: %w", - n.GetHash(), err) + n.HashDigest, err) } err = db.Put(hash, encoding) @@ -417,16 +406,12 @@ func (t *Trie) writeDirty(db chaindb.Batch, n Node) error { hash, err) } - switch n.Type() { - case node.BranchType, node.BranchWithValueType: - default: // not a branch + if n.Type() != node.Branch { n.SetDirty(false) return nil } - branch := n.(*node.Branch) - - for _, child := range branch.Children { + for _, child := range n.Children { if child == nil { continue } @@ -440,11 +425,11 @@ func (t *Trie) writeDirty(db chaindb.Batch, n 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) + return fmt.Errorf("failed to write dirty node=0x%x to database: %w", childTrie.root.HashDigest, err) } } - branch.SetDirty(false) + n.SetDirty(false) return nil } @@ -462,10 +447,10 @@ func (t *Trie) GetInsertedNodeHashes() (hashesSet map[common.Hash]struct{}, err return hashesSet, nil } -func (t *Trie) getInsertedNodeHashes(n Node, hashes map[common.Hash]struct{}) (err error) { +func (t *Trie) getInsertedNodeHashes(n *Node, hashes map[common.Hash]struct{}) (err error) { // TODO pass map of hashes or slice as argument to avoid copying // and using more memory. - if n == nil || !n.IsDirty() { + if n == nil || !n.Dirty { return nil } @@ -473,20 +458,16 @@ func (t *Trie) getInsertedNodeHashes(n Node, hashes map[common.Hash]struct{}) (e if err != nil { return fmt.Errorf( "cannot encode and hash node with hash 0x%x: %w", - n.GetHash(), err) + n.HashDigest, err) } hashes[common.BytesToHash(hash)] = struct{}{} - switch n.Type() { - case node.BranchType, node.BranchWithValueType: - default: // not a branch + if n.Type() != node.Branch { return nil } - branch := n.(*node.Branch) - - for _, child := range branch.Children { + for _, child := range n.Children { if child == nil { continue } diff --git a/lib/trie/helpers_test.go b/lib/trie/helpers_test.go index d5afefff95..1cbd618f5b 100644 --- a/lib/trie/helpers_test.go +++ b/lib/trie/helpers_test.go @@ -9,6 +9,7 @@ import ( "testing" "time" + "github.com/ChainSafe/gossamer/internal/trie/node" "github.com/stretchr/testify/require" ) @@ -120,3 +121,9 @@ func pickKeys(keyValues map[string][]byte, return keys } + +func padRightChildren(slice []*Node) (paddedSlice []*Node) { + paddedSlice = make([]*Node, node.ChildrenCapacity) + copy(paddedSlice, slice) + return paddedSlice +} diff --git a/lib/trie/lookup.go b/lib/trie/lookup.go index e3df5f0960..4c0a169936 100644 --- a/lib/trie/lookup.go +++ b/lib/trie/lookup.go @@ -21,7 +21,7 @@ func findAndRecord(t *Trie, key []byte, recorder recorder) error { return find(t.root, key, recorder, true) } -func find(parent Node, key []byte, recorder recorder, isCurrentRoot bool) error { +func find(parent *Node, key []byte, recorder recorder, isCurrentRoot bool) error { enc, hash, err := parent.EncodeAndHash(isCurrentRoot) if err != nil { return err @@ -29,25 +29,22 @@ func find(parent Node, key []byte, recorder recorder, isCurrentRoot bool) error recorder.Record(hash, enc) - switch parent.Type() { - case node.BranchType, node.BranchWithValueType: - default: // not a branch + if parent.Type() != node.Branch { return nil } - b := parent.(*node.Branch) - - length := lenCommonPrefix(b.Key, key) + branch := parent + length := lenCommonPrefix(branch.Key, key) // found the value at this node - if bytes.Equal(b.Key, key) || len(key) == 0 { + if bytes.Equal(branch.Key, key) || len(key) == 0 { return nil } // did not find value - if bytes.Equal(b.Key[:length], key) && len(key) < len(b.Key) { + if bytes.Equal(branch.Key[:length], key) && len(key) < len(branch.Key) { return nil } - return find(b.Children[key[length]], key[length+1:], recorder, false) + return find(branch.Children[key[length]], key[length+1:], recorder, false) } diff --git a/lib/trie/node.go b/lib/trie/node.go index 8ab60a5455..b16ffabb3e 100644 --- a/lib/trie/node.go +++ b/lib/trie/node.go @@ -3,7 +3,9 @@ package trie -import "github.com/ChainSafe/gossamer/internal/trie/node" +import ( + "github.com/ChainSafe/gossamer/internal/trie/node" +) // Node is a node in the trie and can be a leaf or a branch. -type Node node.Node +type Node = node.Node diff --git a/lib/trie/print_test.go b/lib/trie/print_test.go index cf2343569b..d4bb2f0e49 100644 --- a/lib/trie/print_test.go +++ b/lib/trie/print_test.go @@ -6,7 +6,6 @@ package trie import ( "testing" - "github.com/ChainSafe/gossamer/internal/trie/node" "github.com/stretchr/testify/assert" ) @@ -22,7 +21,7 @@ func Test_Trie_String(t *testing.T) { }, "leaf root": { trie: Trie{ - root: &node.Leaf{ + root: &Node{ Key: []byte{1, 2, 3}, Value: []byte{3, 4, 5}, Generation: 1, @@ -38,18 +37,18 @@ func Test_Trie_String(t *testing.T) { }, "branch root": { trie: Trie{ - root: &node.Branch{ + root: &Node{ Key: nil, Value: []byte{1, 2}, Descendants: 2, - Children: [16]node.Node{ - &node.Leaf{ + Children: []*Node{ + { Key: []byte{1, 2, 3}, Value: []byte{3, 4, 5}, Generation: 2, }, nil, nil, - &node.Leaf{ + { Key: []byte{1, 2, 3}, Value: []byte{3, 4, 5}, Generation: 3, diff --git a/lib/trie/proof_test.go b/lib/trie/proof_test.go index f00d97abcb..78d58a1675 100644 --- a/lib/trie/proof_test.go +++ b/lib/trie/proof_test.go @@ -72,7 +72,7 @@ func testGenerateProof(t *testing.T, entries []Pair, keys [][]byte) ([]byte, [][ err = trie.Store(memdb) require.NoError(t, err) - root := trie.root.GetHash() + root := trie.root.HashDigest proof, err := GenerateProof(root, keys, memdb) require.NoError(t, err) diff --git a/lib/trie/trie.go b/lib/trie/trie.go index c0b408eb2d..988afa3262 100644 --- a/lib/trie/trie.go +++ b/lib/trie/trie.go @@ -19,7 +19,7 @@ var EmptyHash, _ = NewEmptyTrie().Hash() // Trie is a base 16 modified Merkle Patricia trie. type Trie struct { generation uint64 - root Node + root *Node childTries map[common.Hash]*Trie deletedKeys map[common.Hash]struct{} } @@ -30,7 +30,7 @@ func NewEmptyTrie() *Trie { } // NewTrie creates a trie with an existing root node -func NewTrie(root Node) *Trie { +func NewTrie(root *Node) *Trie { return &Trie{ root: root, childTries: make(map[common.Hash]*Trie), @@ -64,29 +64,27 @@ func (t *Trie) Snapshot() (newTrie *Trie) { } } -func (t *Trie) prepLeafForMutation(currentLeaf *node.Leaf, - copySettings node.CopySettings) (newLeaf *node.Leaf) { +func (t *Trie) prepLeafForMutation(currentLeaf *Node, + copySettings node.CopySettings) (newLeaf *Node) { if currentLeaf.Generation == t.generation { // no need to deep copy and update generation // of current leaf. newLeaf = currentLeaf } else { - newNode := updateGeneration(currentLeaf, t.generation, t.deletedKeys, copySettings) - newLeaf = newNode.(*node.Leaf) + newLeaf = updateGeneration(currentLeaf, t.generation, t.deletedKeys, copySettings) } newLeaf.SetDirty(true) return newLeaf } -func (t *Trie) prepBranchForMutation(currentBranch *node.Branch, - copySettings node.CopySettings) (newBranch *node.Branch) { +func (t *Trie) prepBranchForMutation(currentBranch *Node, + copySettings node.CopySettings) (newBranch *Node) { if currentBranch.Generation == t.generation { // no need to deep copy and update generation // of current branch. newBranch = currentBranch } else { - newNode := updateGeneration(currentBranch, t.generation, t.deletedKeys, copySettings) - newBranch = newNode.(*node.Branch) + newBranch = updateGeneration(currentBranch, t.generation, t.deletedKeys, copySettings) } newBranch.SetDirty(true) return newBranch @@ -95,15 +93,15 @@ func (t *Trie) prepBranchForMutation(currentBranch *node.Branch, // updateGeneration is called when the currentNode is from // an older trie generation (snapshot) so we deep copy the // node and update the generation on the newer copy. -func updateGeneration(currentNode Node, trieGeneration uint64, +func updateGeneration(currentNode *Node, trieGeneration uint64, deletedHashes map[common.Hash]struct{}, copySettings node.CopySettings) ( - newNode Node) { + newNode *Node) { newNode = currentNode.Copy(copySettings) - newNode.SetGeneration(trieGeneration) + newNode.Generation = trieGeneration // The hash of the node from a previous snapshotted trie // is usually already computed. - deletedHashBytes := currentNode.GetHash() + deletedHashBytes := currentNode.HashDigest if len(deletedHashBytes) > 0 { deletedHash := common.BytesToHash(deletedHashBytes) deletedHashes[deletedHash] = struct{}{} @@ -149,14 +147,14 @@ func (t *Trie) DeepCopy() (trieCopy *Trie) { } // RootNode returns a copy of the root node of the trie. -func (t *Trie) RootNode() Node { +func (t *Trie) RootNode() *Node { copySettings := node.DefaultCopySettings copySettings.CopyCached = true return t.root.Copy(copySettings) } // encodeRoot writes the encoding of the root node to the buffer. -func encodeRoot(root node.Node, buffer node.Buffer) (err error) { +func encodeRoot(root *Node, buffer node.Buffer) (err error) { if root == nil { _, err = buffer.Write([]byte{0}) if err != nil { @@ -198,22 +196,20 @@ func (t *Trie) Entries() map[string][]byte { return entries(t.root, nil, make(map[string][]byte)) } -func entries(parent Node, prefix []byte, kv map[string][]byte) map[string][]byte { +func entries(parent *Node, prefix []byte, kv map[string][]byte) map[string][]byte { if parent == nil { return kv } - if parent.Type() == node.LeafType { - parentKey := parent.GetKey() + if parent.Type() == node.Leaf { + parentKey := parent.Key fullKeyNibbles := concatenateSlices(prefix, parentKey) keyLE := string(codec.NibblesToKeyLE(fullKeyNibbles)) - kv[keyLE] = parent.GetValue() + kv[keyLE] = parent.Value return kv } - // Branch with/without value - branch := parent.(*node.Branch) - + branch := parent if branch.Value != nil { fullKeyNibbles := concatenateSlices(prefix, branch.Key) keyLE := string(codec.NibblesToKeyLE(fullKeyNibbles)) @@ -243,22 +239,18 @@ func (t *Trie) NextKey(keyLE []byte) (nextKeyLE []byte) { return nextKeyLE } -func findNextKey(parent Node, prefix, searchKey []byte) (nextKey []byte) { +func findNextKey(parent *Node, prefix, searchKey []byte) (nextKey []byte) { if parent == nil { return nil } - if parent.Type() == node.LeafType { - parentLeaf := parent.(*node.Leaf) - return findNextKeyLeaf(parentLeaf, prefix, searchKey) + if parent.Type() == node.Leaf { + return findNextKeyLeaf(parent, prefix, searchKey) } - - // Branch - parentBranch := parent.(*node.Branch) - return findNextKeyBranch(parentBranch, prefix, searchKey) + return findNextKeyBranch(parent, prefix, searchKey) } -func findNextKeyLeaf(leaf *node.Leaf, prefix, searchKey []byte) (nextKey []byte) { +func findNextKeyLeaf(leaf *Node, prefix, searchKey []byte) (nextKey []byte) { parentLeafKey := leaf.Key fullKey := concatenateSlices(prefix, parentLeafKey) @@ -269,7 +261,7 @@ func findNextKeyLeaf(leaf *node.Leaf, prefix, searchKey []byte) (nextKey []byte) return fullKey } -func findNextKeyBranch(parentBranch *node.Branch, prefix, searchKey []byte) (nextKey []byte) { +func findNextKeyBranch(parentBranch *Node, prefix, searchKey []byte) (nextKey []byte) { fullKey := concatenateSlices(prefix, parentBranch.Key) if bytes.Equal(searchKey, fullKey) { @@ -305,7 +297,7 @@ func keyIsLexicographicallyBigger(key, key2 []byte) (bigger bool) { // findNextKeyChild searches for a next key in the children // given and returns a next key or nil if no next key is found. -func findNextKeyChild(children [16]node.Node, startIndex byte, +func findNextKeyChild(children []*Node, startIndex byte, fullKey, key []byte) (nextKey []byte) { for i := startIndex; i < node.ChildrenCapacity; i++ { child := children[i] @@ -336,10 +328,10 @@ func (t *Trie) put(key, value []byte) { // insert inserts a value in the trie at the key specified. // It may create one or more new nodes or update an existing node. -func (t *Trie) insert(parent Node, key, value []byte) (newParent Node, nodesCreated uint32) { +func (t *Trie) insert(parent *Node, key, value []byte) (newParent *Node, nodesCreated uint32) { if parent == nil { const nodesCreated = 1 - return &node.Leaf{ + return &Node{ Key: key, Value: value, Generation: t.generation, @@ -349,18 +341,14 @@ func (t *Trie) insert(parent Node, key, value []byte) (newParent Node, nodesCrea // TODO ensure all values have dirty set to true - switch parent.Type() { - case node.BranchType, node.BranchWithValueType: - parentBranch := parent.(*node.Branch) - return t.insertInBranch(parentBranch, key, value) - default: - parentLeaf := parent.(*node.Leaf) - return t.insertInLeaf(parentLeaf, key, value) + if parent.Type() == node.Branch { + return t.insertInBranch(parent, key, value) } + return t.insertInLeaf(parent, key, value) } -func (t *Trie) insertInLeaf(parentLeaf *node.Leaf, key, value []byte) ( - newParent Node, nodesCreated uint32) { +func (t *Trie) insertInLeaf(parentLeaf *Node, key, value []byte) ( + newParent *Node, nodesCreated uint32) { if bytes.Equal(parentLeaf.Key, key) { nodesCreated = 0 if bytes.Equal(value, parentLeaf.Value) { @@ -377,9 +365,10 @@ func (t *Trie) insertInLeaf(parentLeaf *node.Leaf, key, value []byte) ( commonPrefixLength := lenCommonPrefix(key, parentLeaf.Key) // Convert the current leaf parent into a branch parent - newBranchParent := &node.Branch{ + newBranchParent := &Node{ Key: key[:commonPrefixLength], Generation: t.generation, + Children: make([]*node.Node, node.ChildrenCapacity), Dirty: true, } parentLeafKey := parentLeaf.Key @@ -395,7 +384,7 @@ func (t *Trie) insertInLeaf(parentLeaf *node.Leaf, key, value []byte) ( childIndex := parentLeafKey[commonPrefixLength] parentLeaf.Key = parentLeaf.Key[commonPrefixLength+1:] newBranchParent.Children[childIndex] = parentLeaf - newBranchParent.AddDescendants(1) + newBranchParent.Descendants++ nodesCreated++ } @@ -412,24 +401,24 @@ func (t *Trie) insertInLeaf(parentLeaf *node.Leaf, key, value []byte) ( childIndex := parentLeafKey[commonPrefixLength] parentLeaf.Key = parentLeaf.Key[commonPrefixLength+1:] newBranchParent.Children[childIndex] = parentLeaf - newBranchParent.AddDescendants(1) + newBranchParent.Descendants++ nodesCreated++ } childIndex := key[commonPrefixLength] - newBranchParent.Children[childIndex] = &node.Leaf{ + newBranchParent.Children[childIndex] = &Node{ Key: key[commonPrefixLength+1:], Value: value, Generation: t.generation, Dirty: true, } - newBranchParent.AddDescendants(1) + newBranchParent.Descendants++ nodesCreated++ return newBranchParent, nodesCreated } -func (t *Trie) insertInBranch(parentBranch *node.Branch, key, value []byte) ( - newParent Node, nodesCreated uint32) { +func (t *Trie) insertInBranch(parentBranch *Node, key, value []byte) ( + newParent *Node, nodesCreated uint32) { copySettings := node.DefaultCopySettings parentBranch = t.prepBranchForMutation(parentBranch, copySettings) @@ -446,7 +435,7 @@ func (t *Trie) insertInBranch(parentBranch *node.Branch, key, value []byte) ( child := parentBranch.Children[childIndex] if child == nil { - child = &node.Leaf{ + child = &Node{ Key: remainingKey, Value: value, Generation: t.generation, @@ -458,7 +447,7 @@ func (t *Trie) insertInBranch(parentBranch *node.Branch, key, value []byte) ( } parentBranch.Children[childIndex] = child - parentBranch.AddDescendants(nodesCreated) + parentBranch.Descendants += nodesCreated return parentBranch, nodesCreated } @@ -466,9 +455,10 @@ func (t *Trie) insertInBranch(parentBranch *node.Branch, key, value []byte) ( // update partial keys, new branch has key up to matching length nodesCreated = 1 commonPrefixLength := lenCommonPrefix(key, parentBranch.Key) - newParentBranch := &node.Branch{ + newParentBranch := &Node{ Key: key[:commonPrefixLength], Generation: t.generation, + Children: make([]*node.Node, node.ChildrenCapacity), Dirty: true, } @@ -477,7 +467,7 @@ func (t *Trie) insertInBranch(parentBranch *node.Branch, key, value []byte) ( parentBranch.Key = remainingOldParentKey newParentBranch.Children[oldParentIndex] = parentBranch - newParentBranch.AddDescendants(1 + parentBranch.GetDescendants()) + newParentBranch.Descendants += 1 + parentBranch.Descendants if len(key) <= commonPrefixLength { newParentBranch.Value = value @@ -487,7 +477,7 @@ func (t *Trie) insertInBranch(parentBranch *node.Branch, key, value []byte) ( var additionalNodesCreated uint32 newParentBranch.Children[childIndex], additionalNodesCreated = t.insert(nil, remainingKey, value) nodesCreated += additionalNodesCreated - newParentBranch.AddDescendants(additionalNodesCreated) + newParentBranch.Descendants += additionalNodesCreated } return newParentBranch, nodesCreated @@ -533,22 +523,20 @@ func (t *Trie) GetKeysWithPrefix(prefixLE []byte) (keysLE [][]byte) { // prefix given. The prefix and key byte slices are in nibbles format. // TODO pass in map of keysLE if order is not needed. // TODO do all processing on nibbles keys and then convert to LE. -func getKeysWithPrefix(parent Node, prefix, key []byte, +func getKeysWithPrefix(parent *Node, prefix, key []byte, keysLE [][]byte) (newKeysLE [][]byte) { if parent == nil { return keysLE } - if parent.Type() == node.LeafType { - parentLeaf := parent.(*node.Leaf) - return getKeysWithPrefixFromLeaf(parentLeaf, prefix, key, keysLE) + if parent.Type() == node.Leaf { + return getKeysWithPrefixFromLeaf(parent, prefix, key, keysLE) } - parentBranch := parent.(*node.Branch) - return getKeysWithPrefixFromBranch(parentBranch, prefix, key, keysLE) + return getKeysWithPrefixFromBranch(parent, prefix, key, keysLE) } -func getKeysWithPrefixFromLeaf(parent *node.Leaf, prefix, key []byte, +func getKeysWithPrefixFromLeaf(parent *Node, prefix, key []byte, keysLE [][]byte) (newKeysLE [][]byte) { if len(key) == 0 || bytes.HasPrefix(parent.Key, key) { fullKeyLE := makeFullKeyLE(prefix, parent.Key) @@ -557,7 +545,7 @@ func getKeysWithPrefixFromLeaf(parent *node.Leaf, prefix, key []byte, return keysLE } -func getKeysWithPrefixFromBranch(parent *node.Branch, prefix, key []byte, +func getKeysWithPrefixFromBranch(parent *Node, prefix, key []byte, keysLE [][]byte) (newKeysLE [][]byte) { if len(key) == 0 || bytes.HasPrefix(parent.Key, key) { return addAllKeys(parent, prefix, keysLE) @@ -582,26 +570,24 @@ func getKeysWithPrefixFromBranch(parent *node.Branch, prefix, key []byte, // to the slice of keys given and returns this slice. // It uses the prefix in nibbles format to determine the full key. // The slice of keys has its keys formatted in little Endian. -func addAllKeys(parent Node, prefix []byte, keysLE [][]byte) (newKeysLE [][]byte) { +func addAllKeys(parent *Node, prefix []byte, keysLE [][]byte) (newKeysLE [][]byte) { if parent == nil { return keysLE } - if parent.Type() == node.LeafType { - keyLE := makeFullKeyLE(prefix, parent.GetKey()) + if parent.Type() == node.Leaf { + keyLE := makeFullKeyLE(prefix, parent.Key) keysLE = append(keysLE, keyLE) return keysLE } - // Branches - branchParent := parent.(*node.Branch) - if branchParent.Value != nil { - keyLE := makeFullKeyLE(prefix, branchParent.Key) + if parent.Value != nil { + keyLE := makeFullKeyLE(prefix, parent.Key) keysLE = append(keysLE, keyLE) } - for i, child := range branchParent.Children { - childPrefix := makeChildPrefix(prefix, branchParent.Key, i) + for i, child := range parent.Children { + childPrefix := makeChildPrefix(prefix, parent.Key, i) keysLE = addAllKeys(child, childPrefix, keysLE) } @@ -628,29 +614,25 @@ func (t *Trie) Get(keyLE []byte) (value []byte) { return retrieve(t.root, keyNibbles) } -func retrieve(parent Node, key []byte) (value []byte) { +func retrieve(parent *Node, key []byte) (value []byte) { if parent == nil { return nil } - if parent.Type() == node.LeafType { - leaf := parent.(*node.Leaf) - return retrieveFromLeaf(leaf, key) + if parent.Type() == node.Leaf { + return retrieveFromLeaf(parent, key) } - - // Branches - branch := parent.(*node.Branch) - return retrieveFromBranch(branch, key) + return retrieveFromBranch(parent, key) } -func retrieveFromLeaf(leaf *node.Leaf, key []byte) (value []byte) { +func retrieveFromLeaf(leaf *Node, key []byte) (value []byte) { if bytes.Equal(leaf.Key, key) { return leaf.Value } return nil } -func retrieveFromBranch(branch *node.Branch, key []byte) (value []byte) { +func retrieveFromBranch(branch *Node, key []byte) (value []byte) { if len(key) == 0 || bytes.Equal(branch.Key, key) { return branch.Value } @@ -685,30 +667,28 @@ func (t *Trie) ClearPrefixLimit(prefixLE []byte, limit uint32) (deleted uint32, // clearPrefixLimit deletes the keys having the prefix until the value deletion limit is reached. // It returns the updated node newParent, the number of deleted values valuesDeleted and the // allDeleted boolean indicating if there is no key left with the prefix. -func (t *Trie) clearPrefixLimit(parent Node, prefix []byte, limit uint32) ( - newParent Node, valuesDeleted, nodesRemoved uint32, allDeleted bool) { +func (t *Trie) clearPrefixLimit(parent *Node, prefix []byte, limit uint32) ( + newParent *Node, valuesDeleted, nodesRemoved uint32, allDeleted bool) { if parent == nil { return nil, 0, 0, true } - if parent.Type() == node.LeafType { - leaf := parent.(*node.Leaf) + if parent.Type() == node.Leaf { // if prefix is not found, it's also all deleted. // TODO check this is the same behaviour as in substrate const allDeleted = true - if bytes.HasPrefix(leaf.Key, prefix) { + if bytes.HasPrefix(parent.Key, prefix) { valuesDeleted, nodesRemoved = 1, 1 return nil, valuesDeleted, nodesRemoved, allDeleted } return parent, 0, 0, allDeleted } - branch := parent.(*node.Branch) - return t.clearPrefixLimitBranch(branch, prefix, limit) + return t.clearPrefixLimitBranch(parent, prefix, limit) } -func (t *Trie) clearPrefixLimitBranch(branch *node.Branch, prefix []byte, limit uint32) ( - newParent Node, valuesDeleted uint32, nodesRemoved uint32, allDeleted bool) { +func (t *Trie) clearPrefixLimitBranch(branch *Node, prefix []byte, limit uint32) ( + newParent *Node, valuesDeleted, nodesRemoved uint32, allDeleted bool) { newParent = branch if bytes.HasPrefix(branch.Key, prefix) { @@ -744,7 +724,7 @@ func (t *Trie) clearPrefixLimitBranch(branch *node.Branch, prefix []byte, limit copySettings := node.DefaultCopySettings branch = t.prepBranchForMutation(branch, copySettings) branch.Children[childIndex] = child - branch.SubDescendants(nodesRemoved) + branch.Descendants -= nodesRemoved newParent, branchChildMerged := handleDeletion(branch, prefix) if branchChildMerged { nodesRemoved++ @@ -753,8 +733,8 @@ func (t *Trie) clearPrefixLimitBranch(branch *node.Branch, prefix []byte, limit return newParent, valuesDeleted, nodesRemoved, allDeleted } -func (t *Trie) clearPrefixLimitChild(branch *node.Branch, prefix []byte, limit uint32) ( - newParent Node, valuesDeleted, nodesRemoved uint32, allDeleted bool) { +func (t *Trie) clearPrefixLimitChild(branch *Node, prefix []byte, limit uint32) ( + newParent *Node, valuesDeleted, nodesRemoved uint32, allDeleted bool) { newParent = branch childIndex := prefix[len(branch.Key)] @@ -777,7 +757,7 @@ func (t *Trie) clearPrefixLimitChild(branch *node.Branch, prefix []byte, limit u copySettings := node.DefaultCopySettings branch = t.prepBranchForMutation(branch, copySettings) branch.Children[childIndex] = child - branch.SubDescendants(nodesRemoved) + branch.Descendants -= nodesRemoved newParent, branchChildMerged := handleDeletion(branch, prefix) if branchChildMerged { @@ -788,8 +768,8 @@ func (t *Trie) clearPrefixLimitChild(branch *node.Branch, prefix []byte, limit u return newParent, valuesDeleted, nodesRemoved, allDeleted } -func (t *Trie) deleteNodesLimit(parent Node, prefix []byte, limit uint32) ( - newParent Node, valuesDeleted, nodesRemoved uint32) { +func (t *Trie) deleteNodesLimit(parent *Node, prefix []byte, limit uint32) ( + newParent *Node, valuesDeleted, nodesRemoved uint32) { if limit == 0 { valuesDeleted, nodesRemoved = 0, 0 return parent, valuesDeleted, nodesRemoved @@ -800,12 +780,12 @@ func (t *Trie) deleteNodesLimit(parent Node, prefix []byte, limit uint32) ( return nil, valuesDeleted, nodesRemoved } - if parent.Type() == node.LeafType { + if parent.Type() == node.Leaf { valuesDeleted, nodesRemoved = 1, 1 return nil, valuesDeleted, nodesRemoved } - branch := parent.(*node.Branch) + branch := parent fullKey := concatenateSlices(prefix, branch.Key) @@ -827,7 +807,7 @@ func (t *Trie) deleteNodesLimit(parent Node, prefix []byte, limit uint32) ( limit -= newDeleted valuesDeleted += newDeleted nodesRemoved += newNodesRemoved - branch.SubDescendants(newNodesRemoved) + branch.Descendants -= newNodesRemoved branch.SetDirty(true) @@ -868,28 +848,24 @@ func (t *Trie) ClearPrefix(prefixLE []byte) { t.root, _ = t.clearPrefix(t.root, prefix) } -func (t *Trie) clearPrefix(parent Node, prefix []byte) ( - newParent Node, nodesRemoved uint32) { +func (t *Trie) clearPrefix(parent *Node, prefix []byte) ( + newParent *Node, nodesRemoved uint32) { if parent == nil { const nodesRemoved = 0 return nil, nodesRemoved } - if bytes.HasPrefix(parent.GetKey(), prefix) { - nodesRemoved = 1 - if parent.Type() != node.LeafType { // branch - nodesRemoved += parent.(*node.Branch).GetDescendants() - } + if bytes.HasPrefix(parent.Key, prefix) { + nodesRemoved = 1 + parent.Descendants return nil, nodesRemoved } - if parent.Type() == node.LeafType { + if parent.Type() == node.Leaf { const nodesRemoved = 0 return parent, nodesRemoved } - branch := parent.(*node.Branch) - + branch := parent if len(prefix) == len(branch.Key)+1 && bytes.HasPrefix(branch.Key, prefix[:len(prefix)-1]) { // Prefix is one of the children of the branch @@ -931,7 +907,7 @@ func (t *Trie) clearPrefix(parent Node, prefix []byte) ( copySettings := node.DefaultCopySettings branch = t.prepBranchForMutation(branch, copySettings) - branch.SubDescendants(nodesRemoved) + branch.Descendants -= nodesRemoved branch.Children[childIndex] = child newParent, branchChildMerged := handleDeletion(branch, prefix) if branchChildMerged { @@ -949,14 +925,14 @@ func (t *Trie) Delete(keyLE []byte) { t.root, _, _ = t.delete(t.root, key) } -func (t *Trie) delete(parent Node, key []byte) ( - newParent Node, deleted bool, nodesRemoved uint32) { +func (t *Trie) delete(parent *Node, key []byte) ( + newParent *Node, deleted bool, nodesRemoved uint32) { if parent == nil { const nodesRemoved = 0 return nil, false, nodesRemoved } - if parent.Type() == node.LeafType { + if parent.Type() == node.Leaf { if deleteLeaf(parent, key) == nil { const nodesRemoved = 1 return nil, true, nodesRemoved @@ -964,20 +940,18 @@ func (t *Trie) delete(parent Node, key []byte) ( const nodesRemoved = 0 return parent, false, nodesRemoved } - - branch := parent.(*node.Branch) - return t.deleteBranch(branch, key) + return t.deleteBranch(parent, key) } -func deleteLeaf(parent Node, key []byte) (newParent Node) { - if len(key) == 0 || bytes.Equal(key, parent.GetKey()) { +func deleteLeaf(parent *Node, key []byte) (newParent *Node) { + if len(key) == 0 || bytes.Equal(key, parent.Key) { return nil } return parent } -func (t *Trie) deleteBranch(branch *node.Branch, key []byte) ( - newParent Node, deleted bool, nodesRemoved uint32) { +func (t *Trie) deleteBranch(branch *Node, key []byte) ( + newParent *Node, deleted bool, nodesRemoved uint32) { if len(key) == 0 || bytes.Equal(branch.Key, key) { copySettings := node.DefaultCopySettings copySettings.CopyValue = false @@ -1007,7 +981,7 @@ func (t *Trie) deleteBranch(branch *node.Branch, key []byte) ( copySettings := node.DefaultCopySettings branch = t.prepBranchForMutation(branch, copySettings) - branch.SubDescendants(nodesRemoved) + branch.Descendants -= nodesRemoved branch.Children[childIndex] = newChild newParent, branchChildMerged := handleDeletion(branch, key) @@ -1024,7 +998,7 @@ func (t *Trie) deleteBranch(branch *node.Branch, key []byte) ( // In this first case, branchChildMerged is returned as true to keep track of the removal // of one node in callers. // If the branch has a value and no child, it will be changed into a leaf. -func handleDeletion(branch *node.Branch, key []byte) (newNode Node, branchChildMerged bool) { +func handleDeletion(branch *Node, key []byte) (newNode *Node, branchChildMerged bool) { childrenCount := 0 firstChildIndex := -1 for i, child := range branch.Children { @@ -1044,7 +1018,7 @@ func handleDeletion(branch *node.Branch, key []byte) (newNode Node, branchChildM case childrenCount == 0 && branch.Value != nil: const branchChildMerged = false commonPrefixLength := lenCommonPrefix(branch.Key, key) - return &node.Leaf{ + return &Node{ Key: key[:commonPrefixLength], Value: branch.Value, Dirty: true, @@ -1055,26 +1029,26 @@ func handleDeletion(branch *node.Branch, key []byte) (newNode Node, branchChildM childIndex := firstChildIndex child := branch.Children[firstChildIndex] - if child.Type() == node.LeafType { - childLeafKey := child.GetKey() - newLeafKey := concatenateSlices(branch.Key, intToByteSlice(childIndex), childLeafKey) - return &node.Leaf{ + if child.Type() == node.Leaf { + newLeafKey := concatenateSlices(branch.Key, intToByteSlice(childIndex), child.Key) + return &Node{ Key: newLeafKey, - Value: child.GetValue(), + Value: child.Value, Dirty: true, Generation: branch.Generation, }, branchChildMerged } - childBranch := child.(*node.Branch) + childBranch := child newBranchKey := concatenateSlices(branch.Key, intToByteSlice(childIndex), childBranch.Key) - newBranch := &node.Branch{ + newBranch := &Node{ Key: newBranchKey, Value: childBranch.Value, Generation: branch.Generation, + Children: make([]*node.Node, node.ChildrenCapacity), Dirty: true, // this is the descendants of the original branch minus one - Descendants: childBranch.GetDescendants(), + Descendants: childBranch.Descendants, } // Adopt the grand-children diff --git a/lib/trie/trie_endtoend_test.go b/lib/trie/trie_endtoend_test.go index a3adf97320..7abc7288bb 100644 --- a/lib/trie/trie_endtoend_test.go +++ b/lib/trie/trie_endtoend_test.go @@ -16,7 +16,6 @@ import ( "github.com/stretchr/testify/require" "github.com/ChainSafe/gossamer/internal/trie/codec" - "github.com/ChainSafe/gossamer/internal/trie/node" "github.com/ChainSafe/gossamer/lib/common" ) @@ -305,7 +304,7 @@ func TestTrieDiff(t *testing.T) { } dbTrie := NewEmptyTrie() - err = dbTrie.Load(storageDB, common.BytesToHash(newTrie.root.GetHash())) + err = dbTrie.Load(storageDB, common.BytesToHash(newTrie.root.HashDigest)) require.NoError(t, err) } @@ -493,7 +492,7 @@ func TestClearPrefix_Small(t *testing.T) { ssTrie.ClearPrefix([]byte("noo")) - expectedRoot := &node.Leaf{ + expectedRoot := &Node{ Key: codec.KeyLEToNibbles([]byte("other")), Value: []byte("other"), Generation: 1, @@ -1020,30 +1019,27 @@ func Test_encodeRoot_fuzz(t *testing.T) { } } -func countNodesRecursively(root Node) (nodesCount uint32) { +func countNodesRecursively(root *Node) (nodesCount uint32) { if root == nil { return 0 - } else if root.Type() == node.LeafType { - return 1 } - branch := root.(*node.Branch) - for _, child := range branch.Children { + + nodesCount = 1 + for _, child := range root.Children { nodesCount += countNodesRecursively(child) } - - return 1 + nodesCount + return nodesCount } -func countNodesFromStats(root Node) (nodesCount uint32) { +func countNodesFromStats(root *Node) (nodesCount uint32) { if root == nil { return 0 - } else if root.Type() == node.LeafType { - return 1 } - return 1 + root.(*node.Branch).GetDescendants() + + return 1 + root.Descendants } -func testDescendants(t *testing.T, root Node) { +func testDescendants(t *testing.T, root *Node) { t.Helper() expectedCount := countNodesRecursively(root) statsCount := countNodesFromStats(root) diff --git a/lib/trie/trie_test.go b/lib/trie/trie_test.go index 3ab6b09653..e05587477b 100644 --- a/lib/trie/trie_test.go +++ b/lib/trie/trie_test.go @@ -26,12 +26,12 @@ func Test_NewEmptyTrie(t *testing.T) { } func Test_NewTrie(t *testing.T) { - root := &node.Leaf{ + root := &Node{ Key: []byte{0}, Value: []byte{17}, } expectedTrie := &Trie{ - root: &node.Leaf{ + root: &Node{ Key: []byte{0}, Value: []byte{17}, }, @@ -47,18 +47,18 @@ func Test_Trie_Snapshot(t *testing.T) { trie := &Trie{ generation: 8, - root: &node.Leaf{Key: []byte{8}}, + root: &Node{Key: []byte{8}}, childTries: map[common.Hash]*Trie{ {1}: { generation: 1, - root: &node.Leaf{Key: []byte{1}}, + root: &Node{Key: []byte{1}}, deletedKeys: map[common.Hash]struct{}{ {1}: {}, }, }, {2}: { generation: 2, - root: &node.Leaf{Key: []byte{2}}, + root: &Node{Key: []byte{2}}, deletedKeys: map[common.Hash]struct{}{ {2}: {}, }, @@ -72,16 +72,16 @@ func Test_Trie_Snapshot(t *testing.T) { expectedTrie := &Trie{ generation: 9, - root: &node.Leaf{Key: []byte{8}}, + root: &Node{Key: []byte{8}}, childTries: map[common.Hash]*Trie{ {1}: { generation: 2, - root: &node.Leaf{Key: []byte{1}}, + root: &Node{Key: []byte{1}}, deletedKeys: map[common.Hash]struct{}{}, }, {2}: { generation: 3, - root: &node.Leaf{Key: []byte{2}}, + root: &Node{Key: []byte{2}}, deletedKeys: map[common.Hash]struct{}{}, }, }, @@ -98,20 +98,20 @@ func Test_Trie_updateGeneration(t *testing.T) { testCases := map[string]struct { trieGeneration uint64 - node Node + node *Node copySettings node.CopySettings - newNode Node + newNode *Node copied bool expectedDeletedHashes map[common.Hash]struct{} }{ "trie generation higher and empty hash": { trieGeneration: 2, - node: &node.Leaf{ + node: &Node{ Generation: 1, Key: []byte{1}, }, copySettings: node.DefaultCopySettings, - newNode: &node.Leaf{ + newNode: &Node{ Generation: 2, Key: []byte{1}, }, @@ -120,13 +120,13 @@ func Test_Trie_updateGeneration(t *testing.T) { }, "trie generation higher and hash": { trieGeneration: 2, - node: &node.Leaf{ + node: &Node{ Generation: 1, Key: []byte{1}, HashDigest: []byte{1, 2, 3}, }, copySettings: node.DefaultCopySettings, - newNode: &node.Leaf{ + newNode: &Node{ Generation: 2, Key: []byte{1}, }, @@ -157,7 +157,7 @@ func Test_Trie_updateGeneration(t *testing.T) { // Check for deep copy if newNode != nil && testCase.copied { - newNode.SetDirty(!newNode.IsDirty()) + newNode.SetDirty(!newNode.Dirty) assert.NotEqual(t, testCase.node, newNode) } }) @@ -221,11 +221,11 @@ func Test_Trie_DeepCopy(t *testing.T) { "filled trie": { trieOriginal: &Trie{ generation: 1, - root: &node.Leaf{Key: []byte{1, 2}}, + root: &Node{Key: []byte{1, 2}}, childTries: map[common.Hash]*Trie{ {1, 2, 3}: { generation: 2, - root: &node.Leaf{Key: []byte{1}}, + root: &Node{Key: []byte{1}}, deletedKeys: map[common.Hash]struct{}{ {1, 2, 3}: {}, {3, 4, 5}: {}, @@ -239,11 +239,11 @@ func Test_Trie_DeepCopy(t *testing.T) { }, trieCopy: &Trie{ generation: 1, - root: &node.Leaf{Key: []byte{1, 2}}, + root: &Node{Key: []byte{1, 2}}, childTries: map[common.Hash]*Trie{ {1, 2, 3}: { generation: 2, - root: &node.Leaf{Key: []byte{1}}, + root: &Node{Key: []byte{1}}, deletedKeys: map[common.Hash]struct{}{ {1, 2, 3}: {}, {3, 4, 5}: {}, @@ -276,11 +276,11 @@ func Test_Trie_RootNode(t *testing.T) { t.Parallel() trie := Trie{ - root: &node.Leaf{ + root: &Node{ Key: []byte{1, 2, 3}, }, } - expectedRoot := &node.Leaf{ + expectedRoot := &Node{ Key: []byte{1, 2, 3}, } @@ -303,11 +303,11 @@ func Test_encodeRoot(t *testing.T) { } testCases := map[string]struct { - root node.Node + root *Node bufferCalls bufferCalls errWrapped error errMessage string - expectedRoot node.Node + expectedRoot *Node }{ "nil root and no error": { bufferCalls: bufferCalls{ @@ -329,7 +329,7 @@ func Test_encodeRoot(t *testing.T) { errMessage: "cannot write nil root node to buffer: test error", }, "root encoding error": { - root: &node.Leaf{ + root: &Node{ Key: []byte{1, 2}, }, bufferCalls: bufferCalls{ @@ -342,12 +342,12 @@ func Test_encodeRoot(t *testing.T) { }, errWrapped: errTest, errMessage: "cannot encode header: test error", - expectedRoot: &node.Leaf{ + expectedRoot: &Node{ Key: []byte{1, 2}, }, }, "root encoding success": { - root: &node.Leaf{ + root: &Node{ Key: []byte{1, 2}, }, bufferCalls: bufferCalls{ @@ -361,7 +361,7 @@ func Test_encodeRoot(t *testing.T) { bytesCall: true, bytesReturn: []byte{66, 18, 0}, }, - expectedRoot: &node.Leaf{ + expectedRoot: &Node{ Key: []byte{1, 2}, Encoding: []byte{66, 18, 0}, }, @@ -445,7 +445,7 @@ func Test_Trie_Hash(t *testing.T) { }, "leaf root": { trie: Trie{ - root: &node.Leaf{ + root: &Node{ Key: []byte{1, 2, 3}, }, }, @@ -455,7 +455,7 @@ func Test_Trie_Hash(t *testing.T) { 0x83, 0xb8, 0xe8, 0xc4, 0x80, 0x88, 0xea, 0x4d, 0x9f, 0x57, 0x82, 0x94, 0xc9, 0x76, 0xf4, 0x6f}, expectedTrie: Trie{ - root: &node.Leaf{ + root: &Node{ Key: []byte{1, 2, 3}, Encoding: []byte{67, 1, 35, 0}, }, @@ -463,13 +463,13 @@ func Test_Trie_Hash(t *testing.T) { }, "branch root": { trie: Trie{ - root: &node.Branch{ + root: &Node{ Key: []byte{1, 2, 3}, Value: []byte("branch"), Descendants: 1, - Children: [16]node.Node{ - &node.Leaf{Key: []byte{9}}, - }, + Children: padRightChildren([]*Node{ + {Key: []byte{9}}, + }), }, }, hash: common.Hash{ @@ -478,16 +478,16 @@ func Test_Trie_Hash(t *testing.T) { 0x21, 0x96, 0xce, 0xbf, 0x4c, 0xa4, 0xd, 0xaa, 0xbe, 0x27, 0xab, 0x13, 0xcb, 0xf0, 0xfd, 0xd7}, expectedTrie: Trie{ - root: &node.Branch{ + root: &Node{ Key: []byte{1, 2, 3}, Value: []byte("branch"), Descendants: 1, - Children: [16]node.Node{ - &node.Leaf{ + Children: padRightChildren([]*Node{ + { Key: []byte{9}, Encoding: []byte{0x41, 0x09, 0x00}, }, - }, + }), }, }, }, @@ -543,21 +543,21 @@ func Test_Trie_Entries(t *testing.T) { t.Run("simple root", func(t *testing.T) { t.Parallel() - root := &node.Branch{ + root := &Node{ Key: []byte{0xa}, Value: []byte("root"), Descendants: 2, - Children: [16]node.Node{ - &node.Leaf{ // index 0 + Children: padRightChildren([]*Node{ + { // index 0 Key: []byte{2, 0xb}, Value: []byte("leaf"), }, nil, - &node.Leaf{ // index 2 + { // index 2 Key: []byte{0xb}, Value: []byte("leaf"), }, - }, + }), } trie := NewTrie(root) @@ -576,42 +576,42 @@ func Test_Trie_Entries(t *testing.T) { t.Run("custom root", func(t *testing.T) { t.Parallel() - root := &node.Branch{ + root := &Node{ Key: []byte{0xa, 0xb}, Value: []byte("root"), Descendants: 5, - Children: [16]node.Node{ + Children: padRightChildren([]*Node{ nil, nil, nil, - &node.Branch{ // branch with value at child index 3 + { // branch with value at child index 3 Key: []byte{0xb}, Value: []byte("branch 1"), Descendants: 1, - Children: [16]node.Node{ + Children: padRightChildren([]*Node{ nil, nil, nil, - &node.Leaf{ // leaf at child index 3 + { // leaf at child index 3 Key: []byte{0xc}, Value: []byte("bottom leaf"), }, - }, + }), }, nil, nil, nil, - &node.Leaf{ // leaf at child index 7 + { // leaf at child index 7 Key: []byte{0xd}, Value: []byte("top leaf"), }, nil, - &node.Branch{ // branch without value at child index 9 + { // branch without value at child index 9 Key: []byte{0xe}, Value: []byte("branch 2"), Descendants: 1, - Children: [16]node.Node{ - &node.Leaf{ // leaf at child index 0 + Children: padRightChildren([]*Node{ + { // leaf at child index 0 Key: []byte{0xf}, Value: []byte("bottom leaf 2"), }, nil, nil, - }, + }), }, - }, + }), } trie := NewTrie(root) @@ -670,7 +670,7 @@ func Test_Trie_NextKey(t *testing.T) { }, "nil key returns root leaf": { trie: Trie{ - root: &node.Leaf{ + root: &Node{ Key: []byte{2}, }, }, @@ -678,7 +678,7 @@ func Test_Trie_NextKey(t *testing.T) { }, "key smaller than root leaf full key": { trie: Trie{ - root: &node.Leaf{ + root: &Node{ Key: []byte{2}, }, }, @@ -716,7 +716,7 @@ func Test_nextKey(t *testing.T) { }, "nil key returns root leaf": { trie: Trie{ - root: &node.Leaf{ + root: &Node{ Key: []byte{2}, }, }, @@ -724,7 +724,7 @@ func Test_nextKey(t *testing.T) { }, "key smaller than root leaf full key": { trie: Trie{ - root: &node.Leaf{ + root: &Node{ Key: []byte{2}, }, }, @@ -733,7 +733,7 @@ func Test_nextKey(t *testing.T) { }, "key equal to root leaf full key": { trie: Trie{ - root: &node.Leaf{ + root: &Node{ Key: []byte{2}, }, }, @@ -741,7 +741,7 @@ func Test_nextKey(t *testing.T) { }, "key greater than root leaf full key": { trie: Trie{ - root: &node.Leaf{ + root: &Node{ Key: []byte{2}, }, }, @@ -749,15 +749,15 @@ func Test_nextKey(t *testing.T) { }, "key smaller than root branch full key": { trie: Trie{ - root: &node.Branch{ + root: &Node{ Key: []byte{2}, Value: []byte("branch"), Descendants: 1, - Children: [16]node.Node{ - &node.Leaf{ + Children: padRightChildren([]*Node{ + { Key: []byte{1}, }, - }, + }), }, }, key: []byte{1}, @@ -765,32 +765,32 @@ func Test_nextKey(t *testing.T) { }, "key equal to root branch full key": { trie: Trie{ - root: &node.Branch{ + root: &Node{ Key: []byte{2}, Value: []byte("branch"), Descendants: 1, - Children: [16]node.Node{ - &node.Leaf{ + Children: padRightChildren([]*Node{ + { Key: []byte{1}, }, - }, + }), }, }, key: []byte{2, 0, 1}, }, "key smaller than leaf full key": { trie: Trie{ - root: &node.Branch{ + root: &Node{ Key: []byte{1}, Value: []byte("branch"), Descendants: 1, - Children: [16]node.Node{ + Children: padRightChildren([]*Node{ nil, nil, - &node.Leaf{ + { // full key [1, 2, 3] Key: []byte{3}, }, - }, + }), }, }, key: []byte{1, 2, 2}, @@ -798,61 +798,61 @@ func Test_nextKey(t *testing.T) { }, "key equal to leaf full key": { trie: Trie{ - root: &node.Branch{ + root: &Node{ Key: []byte{1}, Value: []byte("branch"), Descendants: 1, - Children: [16]node.Node{ + Children: padRightChildren([]*Node{ nil, nil, - &node.Leaf{ + { // full key [1, 2, 3] Key: []byte{3}, }, - }, + }), }, }, key: []byte{1, 2, 3}, }, "key greater than leaf full key": { trie: Trie{ - root: &node.Branch{ + root: &Node{ Key: []byte{1}, Value: []byte("branch"), Descendants: 1, - Children: [16]node.Node{ + Children: padRightChildren([]*Node{ nil, nil, - &node.Leaf{ + { // full key [1, 2, 3] Key: []byte{3}, }, - }, + }), }, }, key: []byte{1, 2, 4}, }, "next key branch with value": { trie: Trie{ - root: &node.Branch{ + root: &Node{ Key: []byte{1}, Value: []byte("top branch"), Descendants: 2, - Children: [16]node.Node{ + Children: padRightChildren([]*Node{ nil, nil, - &node.Branch{ + { // full key [1, 2, 3] Key: []byte{3}, Value: []byte("branch 1"), Descendants: 1, - Children: [16]node.Node{ + Children: padRightChildren([]*Node{ nil, nil, nil, nil, - &node.Leaf{ + { // full key [1, 2, 3, 4, 5] Key: []byte{0x5}, Value: []byte("bottom leaf"), }, - }, + }), }, - }, + }), }, }, key: []byte{1}, @@ -860,25 +860,25 @@ func Test_nextKey(t *testing.T) { }, "next key go through branch without value": { trie: Trie{ - root: &node.Branch{ + root: &Node{ Key: []byte{1}, Descendants: 2, - Children: [16]node.Node{ + Children: padRightChildren([]*Node{ nil, nil, - &node.Branch{ + { // full key [1, 2, 3] Key: []byte{3}, Descendants: 1, - Children: [16]node.Node{ + Children: padRightChildren([]*Node{ nil, nil, nil, nil, - &node.Leaf{ + { // full key [1, 2, 3, 4, 5] Key: []byte{0x5}, Value: []byte("bottom leaf"), }, - }, + }), }, - }, + }), }, }, key: []byte{0}, @@ -886,26 +886,26 @@ func Test_nextKey(t *testing.T) { }, "next key leaf from bottom branch": { trie: Trie{ - root: &node.Branch{ + root: &Node{ Key: []byte{1}, Descendants: 2, - Children: [16]node.Node{ + Children: padRightChildren([]*Node{ nil, nil, - &node.Branch{ + { // full key [1, 2, 3] Key: []byte{3}, Value: []byte("bottom branch"), Descendants: 1, - Children: [16]node.Node{ + Children: padRightChildren([]*Node{ nil, nil, nil, nil, - &node.Leaf{ + { // full key [1, 2, 3, 4, 5] Key: []byte{0x5}, Value: []byte("bottom leaf"), }, - }, + }), }, - }, + }), }, }, key: []byte{1, 2, 3}, @@ -913,26 +913,26 @@ func Test_nextKey(t *testing.T) { }, "next key greater than branch": { trie: Trie{ - root: &node.Branch{ + root: &Node{ Key: []byte{1}, Descendants: 2, - Children: [16]node.Node{ + Children: padRightChildren([]*Node{ nil, nil, - &node.Branch{ + { // full key [1, 2, 3] Key: []byte{3}, Value: []byte("bottom branch"), Descendants: 1, - Children: [16]node.Node{ + Children: padRightChildren([]*Node{ nil, nil, nil, nil, - &node.Leaf{ + { // full key [1, 2, 3, 4, 5] Key: []byte{0x5}, Value: []byte("bottom leaf"), }, - }, + }), }, - }, + }), }, }, key: []byte{1, 2, 3}, @@ -940,20 +940,20 @@ func Test_nextKey(t *testing.T) { }, "key smaller length and greater than root branch full key": { trie: Trie{ - root: &node.Branch{ + root: &Node{ Key: []byte{2, 0}, Value: []byte("branch"), Descendants: 1, - Children: [16]node.Node{ - &node.Leaf{Key: []byte{1}}, - }, + Children: padRightChildren([]*Node{ + {Key: []byte{1}}, + }), }, }, key: []byte{3}, }, "key smaller length and greater than root leaf full key": { trie: Trie{ - root: &node.Leaf{ + root: &Node{ Key: []byte{2, 0}, Value: []byte("leaf"), }, @@ -989,7 +989,7 @@ func Test_Trie_Put(t *testing.T) { "trie with key and value": { trie: Trie{ generation: 1, - root: &node.Leaf{ + root: &Node{ Key: []byte{1, 2, 0, 5}, Value: []byte{1}, }, @@ -998,25 +998,25 @@ func Test_Trie_Put(t *testing.T) { value: []byte{2}, expectedTrie: Trie{ generation: 1, - root: &node.Branch{ + root: &Node{ Key: []byte{1, 2}, Generation: 1, Dirty: true, Descendants: 2, - Children: [16]node.Node{ - &node.Leaf{ + Children: padRightChildren([]*Node{ + { Key: []byte{5}, Value: []byte{1}, Generation: 1, Dirty: true, }, - &node.Leaf{ + { Key: []byte{6}, Value: []byte{2}, Generation: 1, Dirty: true, }, - }, + }), }, }, }, @@ -1050,7 +1050,7 @@ func Test_Trie_put(t *testing.T) { }, expectedTrie: Trie{ generation: 1, - root: &node.Leaf{ + root: &Node{ Generation: 1, Dirty: true, }, @@ -1063,7 +1063,7 @@ func Test_Trie_put(t *testing.T) { value: []byte{3, 4}, expectedTrie: Trie{ generation: 1, - root: &node.Leaf{ + root: &Node{ Value: []byte{3, 4}, Generation: 1, Dirty: true, @@ -1078,7 +1078,7 @@ func Test_Trie_put(t *testing.T) { value: []byte{3, 4}, expectedTrie: Trie{ generation: 1, - root: &node.Leaf{ + root: &Node{ Key: []byte{1, 2}, Value: []byte{3, 4}, Generation: 1, @@ -1089,7 +1089,7 @@ func Test_Trie_put(t *testing.T) { "trie with key and value": { trie: Trie{ generation: 1, - root: &node.Leaf{ + root: &Node{ Key: []byte{1, 0, 5}, Value: []byte{1}, }, @@ -1098,25 +1098,25 @@ func Test_Trie_put(t *testing.T) { value: []byte{2}, expectedTrie: Trie{ generation: 1, - root: &node.Branch{ + root: &Node{ Key: []byte{1}, Generation: 1, Dirty: true, Descendants: 2, - Children: [16]node.Node{ - &node.Leaf{ + Children: padRightChildren([]*Node{ + { Key: []byte{5}, Value: []byte{1}, Generation: 1, Dirty: true, }, - &node.Leaf{ + { Key: []byte{6}, Value: []byte{2}, Generation: 1, Dirty: true, }, - }, + }), }, }, }, @@ -1140,10 +1140,10 @@ func Test_Trie_insert(t *testing.T) { testCases := map[string]struct { trie Trie - parent Node + parent *Node key []byte value []byte - newNode Node + newNode *Node nodesCreated uint32 }{ "nil parent": { @@ -1152,7 +1152,7 @@ func Test_Trie_insert(t *testing.T) { }, key: []byte{1}, value: []byte("leaf"), - newNode: &node.Leaf{ + newNode: &Node{ Key: []byte{1}, Value: []byte("leaf"), Generation: 1, @@ -1164,32 +1164,32 @@ func Test_Trie_insert(t *testing.T) { trie: Trie{ generation: 1, }, - parent: &node.Branch{ + parent: &Node{ Key: []byte{1}, Value: []byte("branch"), Descendants: 1, - Children: [16]node.Node{ + Children: padRightChildren([]*Node{ nil, - &node.Leaf{Key: []byte{2}}, - }, + {Key: []byte{2}}, + }), }, key: []byte{1, 0}, value: []byte("leaf"), - newNode: &node.Branch{ + newNode: &Node{ Key: []byte{1}, Value: []byte("branch"), Generation: 1, Dirty: true, Descendants: 2, - Children: [16]node.Node{ - &node.Leaf{ + Children: padRightChildren([]*Node{ + { Key: []byte{}, Value: []byte("leaf"), Generation: 1, Dirty: true, }, - &node.Leaf{Key: []byte{2}}, - }, + {Key: []byte{2}}, + }), }, nodesCreated: 1, }, @@ -1197,13 +1197,13 @@ func Test_Trie_insert(t *testing.T) { trie: Trie{ generation: 1, }, - parent: &node.Leaf{ + parent: &Node{ Key: []byte{1}, Value: []byte("original leaf"), }, key: []byte{1}, value: []byte("new leaf"), - newNode: &node.Leaf{ + newNode: &Node{ Key: []byte{1}, Value: []byte("new leaf"), Generation: 1, @@ -1214,13 +1214,13 @@ func Test_Trie_insert(t *testing.T) { trie: Trie{ generation: 1, }, - parent: &node.Leaf{ + parent: &Node{ Key: []byte{1}, Value: []byte("same"), }, key: []byte{1}, value: []byte("same"), - newNode: &node.Leaf{ + newNode: &Node{ Key: []byte{1}, Value: []byte("same"), }, @@ -1229,26 +1229,26 @@ func Test_Trie_insert(t *testing.T) { trie: Trie{ generation: 1, }, - parent: &node.Leaf{ + parent: &Node{ Key: []byte{1}, Value: []byte("original leaf"), }, key: []byte{1, 0}, value: []byte("leaf"), - newNode: &node.Branch{ + newNode: &Node{ Key: []byte{1}, Value: []byte("original leaf"), Dirty: true, Generation: 1, Descendants: 1, - Children: [16]node.Node{ - &node.Leaf{ + Children: padRightChildren([]*Node{ + { Key: []byte{}, Value: []byte("leaf"), Generation: 1, Dirty: true, }, - }, + }), }, nodesCreated: 1, }, @@ -1256,32 +1256,32 @@ func Test_Trie_insert(t *testing.T) { trie: Trie{ generation: 1, }, - parent: &node.Leaf{ + parent: &Node{ Key: []byte{1, 2}, Value: []byte("original leaf"), }, key: []byte{2, 3}, value: []byte("leaf"), - newNode: &node.Branch{ + newNode: &Node{ Key: []byte{}, Dirty: true, Generation: 1, Descendants: 2, - Children: [16]node.Node{ + Children: padRightChildren([]*Node{ nil, - &node.Leaf{ + { Key: []byte{2}, Value: []byte("original leaf"), Dirty: true, Generation: 1, }, - &node.Leaf{ + { Key: []byte{3}, Value: []byte("leaf"), Generation: 1, Dirty: true, }, - }, + }), }, nodesCreated: 2, }, @@ -1289,12 +1289,12 @@ func Test_Trie_insert(t *testing.T) { trie: Trie{ generation: 1, }, - parent: &node.Leaf{ + parent: &Node{ Key: []byte{1}, }, key: []byte{1}, value: []byte("leaf"), - newNode: &node.Leaf{ + newNode: &Node{ Key: []byte{1}, Value: []byte("leaf"), Dirty: true, @@ -1305,25 +1305,25 @@ func Test_Trie_insert(t *testing.T) { trie: Trie{ generation: 1, }, - parent: &node.Leaf{ + parent: &Node{ Key: []byte{1, 2}, }, key: []byte{1}, value: []byte("leaf"), - newNode: &node.Branch{ + newNode: &Node{ Key: []byte{1}, Value: []byte("leaf"), Dirty: true, Generation: 1, Descendants: 1, - Children: [16]node.Node{ + Children: padRightChildren([]*Node{ nil, nil, - &node.Leaf{ + { Key: []byte{}, Dirty: true, Generation: 1, }, - }, + }), }, nodesCreated: 1, }, @@ -1350,223 +1350,223 @@ func Test_Trie_insertInBranch(t *testing.T) { t.Parallel() testCases := map[string]struct { - parent *node.Branch + parent *Node key []byte value []byte - newNode Node + newNode *Node nodesCreated uint32 }{ "update with branch": { - parent: &node.Branch{ + parent: &Node{ Key: []byte{2}, Value: []byte("old"), Descendants: 1, - Children: [16]node.Node{ - &node.Leaf{Key: []byte{1}}, - }, + Children: padRightChildren([]*Node{ + {Key: []byte{1}}, + }), }, key: []byte{2}, value: []byte("new"), - newNode: &node.Branch{ + newNode: &Node{ Key: []byte{2}, Value: []byte("new"), Dirty: true, Descendants: 1, - Children: [16]node.Node{ - &node.Leaf{Key: []byte{1}}, - }, + Children: padRightChildren([]*Node{ + {Key: []byte{1}}, + }), }, }, "update with leaf": { - parent: &node.Branch{ + parent: &Node{ Key: []byte{2}, Value: []byte("old"), Descendants: 1, - Children: [16]node.Node{ - &node.Leaf{Key: []byte{1}}, - }, + Children: padRightChildren([]*Node{ + {Key: []byte{1}}, + }), }, key: []byte{2}, value: []byte("new"), - newNode: &node.Branch{ + newNode: &Node{ Key: []byte{2}, Value: []byte("new"), Dirty: true, Descendants: 1, - Children: [16]node.Node{ - &node.Leaf{Key: []byte{1}}, - }, + Children: padRightChildren([]*Node{ + {Key: []byte{1}}, + }), }, }, "add leaf as direct child": { - parent: &node.Branch{ + parent: &Node{ Key: []byte{2}, Value: []byte{5}, Descendants: 1, - Children: [16]node.Node{ - &node.Leaf{Key: []byte{1}}, - }, + Children: padRightChildren([]*Node{ + {Key: []byte{1}}, + }), }, key: []byte{2, 3, 4, 5}, value: []byte{6}, - newNode: &node.Branch{ + newNode: &Node{ Key: []byte{2}, Value: []byte{5}, Dirty: true, Descendants: 2, - Children: [16]node.Node{ - &node.Leaf{Key: []byte{1}}, + Children: padRightChildren([]*Node{ + {Key: []byte{1}}, nil, nil, - &node.Leaf{ + { Key: []byte{4, 5}, Value: []byte{6}, Dirty: true, }, - }, + }), }, nodesCreated: 1, }, "add leaf as nested child": { - parent: &node.Branch{ + parent: &Node{ Key: []byte{2}, Value: []byte{5}, Descendants: 2, - Children: [16]node.Node{ + Children: padRightChildren([]*Node{ nil, nil, nil, - &node.Branch{ + { Key: []byte{4}, Descendants: 1, - Children: [16]node.Node{ - &node.Leaf{Key: []byte{1}}, - }, + Children: padRightChildren([]*Node{ + {Key: []byte{1}}, + }), }, - }, + }), }, key: []byte{2, 3, 4, 5, 6}, value: []byte{6}, - newNode: &node.Branch{ + newNode: &Node{ Key: []byte{2}, Value: []byte{5}, Dirty: true, Descendants: 3, - Children: [16]node.Node{ + Children: padRightChildren([]*Node{ nil, nil, nil, - &node.Branch{ + { Key: []byte{4}, Dirty: true, Descendants: 2, - Children: [16]node.Node{ - &node.Leaf{Key: []byte{1}}, + Children: padRightChildren([]*Node{ + {Key: []byte{1}}, nil, nil, nil, nil, - &node.Leaf{ + { Key: []byte{6}, Value: []byte{6}, Dirty: true, }, - }, + }), }, - }, + }), }, nodesCreated: 1, }, "split branch for longer key": { - parent: &node.Branch{ + parent: &Node{ Key: []byte{2, 3}, Value: []byte{5}, Descendants: 1, - Children: [16]node.Node{ - &node.Leaf{Key: []byte{1}}, - }, + Children: padRightChildren([]*Node{ + {Key: []byte{1}}, + }), }, key: []byte{2, 4, 5, 6}, value: []byte{6}, - newNode: &node.Branch{ + newNode: &Node{ Key: []byte{2}, Dirty: true, Descendants: 3, - Children: [16]node.Node{ + Children: padRightChildren([]*Node{ nil, nil, nil, - &node.Branch{ + { Key: []byte{}, Value: []byte{5}, Dirty: true, Descendants: 1, - Children: [16]node.Node{ - &node.Leaf{Key: []byte{1}}, - }, + Children: padRightChildren([]*Node{ + {Key: []byte{1}}, + }), }, - &node.Leaf{ + { Key: []byte{5, 6}, Value: []byte{6}, Dirty: true, }, - }, + }), }, nodesCreated: 2, }, "split root branch": { - parent: &node.Branch{ + parent: &Node{ Key: []byte{2, 3}, Value: []byte{5}, Descendants: 1, - Children: [16]node.Node{ - &node.Leaf{Key: []byte{1}}, - }, + Children: padRightChildren([]*Node{ + {Key: []byte{1}}, + }), }, key: []byte{3}, value: []byte{6}, - newNode: &node.Branch{ + newNode: &Node{ Key: []byte{}, Dirty: true, Descendants: 3, - Children: [16]node.Node{ + Children: padRightChildren([]*Node{ nil, nil, - &node.Branch{ + { Key: []byte{3}, Value: []byte{5}, Dirty: true, Descendants: 1, - Children: [16]node.Node{ - &node.Leaf{Key: []byte{1}}, - }, + Children: padRightChildren([]*Node{ + {Key: []byte{1}}, + }), }, - &node.Leaf{ + { Key: []byte{}, Value: []byte{6}, Dirty: true, }, - }, + }), }, nodesCreated: 2, }, "update with leaf at empty key": { - parent: &node.Branch{ + parent: &Node{ Key: []byte{2}, Value: []byte{5}, Descendants: 1, - Children: [16]node.Node{ - &node.Leaf{Key: []byte{1}}, - }, + Children: padRightChildren([]*Node{ + {Key: []byte{1}}, + }), }, key: []byte{}, value: []byte{6}, - newNode: &node.Branch{ + newNode: &Node{ Key: []byte{}, Value: []byte{6}, Dirty: true, Descendants: 2, - Children: [16]node.Node{ + Children: padRightChildren([]*Node{ nil, nil, - &node.Branch{ + { Key: []byte{}, Value: []byte{5}, Dirty: true, Descendants: 1, - Children: [16]node.Node{ - &node.Leaf{Key: []byte{1}}, - }, + Children: padRightChildren([]*Node{ + {Key: []byte{1}}, + }), }, - }, + }), }, nodesCreated: 1, }, @@ -1623,45 +1623,45 @@ func Test_Trie_LoadFromMap(t *testing.T) { "0x0130": "0x08", }, expectedTrie: Trie{ - root: &node.Branch{ + root: &Node{ Key: []byte{00, 01}, Value: []byte{6}, Dirty: true, Descendants: 2, - Children: [16]node.Node{ + Children: padRightChildren([]*Node{ nil, nil, - &node.Leaf{ + { Key: []byte{0}, Value: []byte{7}, Dirty: true, }, - &node.Leaf{ + { Key: []byte{0}, Value: []byte{8}, Dirty: true, }, - }, + }), }, }, }, "override trie": { trie: Trie{ - root: &node.Branch{ + root: &Node{ Key: []byte{00, 01}, Value: []byte{106}, Dirty: true, Descendants: 2, - Children: [16]node.Node{ - &node.Leaf{ + Children: padRightChildren([]*Node{ + { Value: []byte{9}, }, nil, - &node.Leaf{ + { Key: []byte{0}, Value: []byte{107}, Dirty: true, }, - }, + }), }, }, data: map[string]string{ @@ -1670,27 +1670,27 @@ func Test_Trie_LoadFromMap(t *testing.T) { "0x0130": "0x08", }, expectedTrie: Trie{ - root: &node.Branch{ + root: &Node{ Key: []byte{00, 01}, Value: []byte{6}, Dirty: true, Descendants: 3, - Children: [16]node.Node{ - &node.Leaf{ + Children: padRightChildren([]*Node{ + { Value: []byte{9}, }, nil, - &node.Leaf{ + { Key: []byte{0}, Value: []byte{7}, Dirty: true, }, - &node.Leaf{ + { Key: []byte{0}, Value: []byte{8}, Dirty: true, }, - }, + }), }, }, }, @@ -1723,26 +1723,26 @@ func Test_Trie_GetKeysWithPrefix(t *testing.T) { }{ "some trie": { trie: Trie{ - root: &node.Branch{ + root: &Node{ Key: []byte{0, 1}, Descendants: 4, - Children: [16]node.Node{ - &node.Branch{ // full key 0, 1, 0, 3 + Children: padRightChildren([]*Node{ + { // full key 0, 1, 0, 3 Key: []byte{3}, Descendants: 2, - Children: [16]node.Node{ - &node.Leaf{ // full key 0, 1, 0, 0, 4 + Children: padRightChildren([]*Node{ + { // full key 0, 1, 0, 0, 4 Key: []byte{4}, }, - &node.Leaf{ // full key 0, 1, 0, 1, 5 + { // full key 0, 1, 0, 1, 5 Key: []byte{5}, }, - }, + }), }, - &node.Leaf{ // full key 0, 1, 1, 9 + { // full key 0, 1, 1, 9 Key: []byte{9}, }, - }, + }), }, }, prefix: []byte{1}, @@ -1770,7 +1770,7 @@ func Test_getKeysWithPrefix(t *testing.T) { t.Parallel() testCases := map[string]struct { - parent Node + parent *Node prefix []byte key []byte keys [][]byte @@ -1781,13 +1781,13 @@ func Test_getKeysWithPrefix(t *testing.T) { expectedKeys: [][]byte{{1}, {2}}, }, "common prefix for parent branch and search key": { - parent: &node.Branch{ + parent: &Node{ Key: []byte{1, 2, 3}, Descendants: 2, - Children: [16]node.Node{ - &node.Leaf{Key: []byte{4}}, - &node.Leaf{Key: []byte{5}}, - }, + Children: padRightChildren([]*Node{ + {Key: []byte{4}}, + {Key: []byte{5}}, + }), }, prefix: []byte{9, 8, 7}, key: []byte{1, 2}, @@ -1797,13 +1797,13 @@ func Test_getKeysWithPrefix(t *testing.T) { {0x98, 0x71, 0x23, 0x15}}, }, "parent branch and empty key": { - parent: &node.Branch{ + parent: &Node{ Key: []byte{1, 2, 3}, Descendants: 2, - Children: [16]node.Node{ - &node.Leaf{Key: []byte{4}}, - &node.Leaf{Key: []byte{5}}, - }, + Children: padRightChildren([]*Node{ + {Key: []byte{4}}, + {Key: []byte{5}}, + }), }, prefix: []byte{9, 8, 7}, key: []byte{}, @@ -1813,39 +1813,39 @@ func Test_getKeysWithPrefix(t *testing.T) { {0x98, 0x71, 0x23, 0x15}}, }, "search key smaller than branch key with no full common prefix": { - parent: &node.Branch{ + parent: &Node{ Key: []byte{1, 2, 3}, Descendants: 2, - Children: [16]node.Node{ - &node.Leaf{Key: []byte{4}}, - &node.Leaf{Key: []byte{5}}, - }, + Children: padRightChildren([]*Node{ + {Key: []byte{4}}, + {Key: []byte{5}}, + }), }, key: []byte{1, 3}, keys: [][]byte{{1}, {2}}, expectedKeys: [][]byte{{1}, {2}}, }, "common prefix smaller tan search key": { - parent: &node.Branch{ + parent: &Node{ Key: []byte{1, 2}, Descendants: 2, - Children: [16]node.Node{ - &node.Leaf{Key: []byte{4}}, - &node.Leaf{Key: []byte{5}}, - }, + Children: padRightChildren([]*Node{ + {Key: []byte{4}}, + {Key: []byte{5}}, + }), }, key: []byte{1, 2, 3}, keys: [][]byte{{1}, {2}}, expectedKeys: [][]byte{{1}, {2}}, }, "recursive call": { - parent: &node.Branch{ + parent: &Node{ Key: []byte{1, 2, 3}, Descendants: 2, - Children: [16]node.Node{ - &node.Leaf{Key: []byte{4}}, - &node.Leaf{Key: []byte{5}}, - }, + Children: padRightChildren([]*Node{ + {Key: []byte{4}}, + {Key: []byte{5}}, + }), }, prefix: []byte{9, 8, 7}, key: []byte{1, 2, 3, 0}, @@ -1854,7 +1854,7 @@ func Test_getKeysWithPrefix(t *testing.T) { {0x98, 0x71, 0x23, 0x04}}, }, "parent leaf with search key equal to common prefix": { - parent: &node.Leaf{ + parent: &Node{ Key: []byte{1, 2, 3}, }, prefix: []byte{9, 8, 7}, @@ -1864,7 +1864,7 @@ func Test_getKeysWithPrefix(t *testing.T) { {0x98, 0x71, 0x23}}, }, "parent leaf with empty search key": { - parent: &node.Leaf{ + parent: &Node{ Key: []byte{1, 2, 3}, }, prefix: []byte{9, 8, 7}, @@ -1874,7 +1874,7 @@ func Test_getKeysWithPrefix(t *testing.T) { {0x98, 0x71, 0x23}}, }, "parent leaf with too deep search key": { - parent: &node.Leaf{ + parent: &Node{ Key: []byte{1, 2, 3}, }, prefix: []byte{9, 8, 7}, @@ -1883,7 +1883,7 @@ func Test_getKeysWithPrefix(t *testing.T) { expectedKeys: [][]byte{{1}, {2}}, }, "parent leaf with shorter matching search key": { - parent: &node.Leaf{ + parent: &Node{ Key: []byte{1, 2, 3}, }, prefix: []byte{9, 8, 7}, @@ -1893,7 +1893,7 @@ func Test_getKeysWithPrefix(t *testing.T) { {0x98, 0x71, 0x23}}, }, "parent leaf with not matching search key": { - parent: &node.Leaf{ + parent: &Node{ Key: []byte{1, 2, 3}, }, prefix: []byte{9, 8, 7}, @@ -1920,7 +1920,7 @@ func Test_addAllKeys(t *testing.T) { t.Parallel() testCases := map[string]struct { - parent Node + parent *Node prefix []byte keys [][]byte expectedKeys [][]byte @@ -1930,7 +1930,7 @@ func Test_addAllKeys(t *testing.T) { expectedKeys: [][]byte{{1}, {2}}, }, "leaf parent": { - parent: &node.Leaf{ + parent: &Node{ Key: []byte{1, 2, 3}, }, prefix: []byte{9, 8, 7}, @@ -1939,13 +1939,13 @@ func Test_addAllKeys(t *testing.T) { {0x98, 0x71, 0x23}}, }, "parent branch without value": { - parent: &node.Branch{ + parent: &Node{ Key: []byte{1, 2, 3}, Descendants: 2, - Children: [16]node.Node{ - &node.Leaf{Key: []byte{4}}, - &node.Leaf{Key: []byte{5}}, - }, + Children: padRightChildren([]*Node{ + {Key: []byte{4}}, + {Key: []byte{5}}, + }), }, prefix: []byte{9, 8, 7}, keys: [][]byte{{1}, {2}}, @@ -1954,14 +1954,14 @@ func Test_addAllKeys(t *testing.T) { {0x98, 0x71, 0x23, 0x15}}, }, "parent branch with empty value": { - parent: &node.Branch{ + parent: &Node{ Key: []byte{1, 2, 3}, Value: []byte{}, Descendants: 2, - Children: [16]node.Node{ - &node.Leaf{Key: []byte{4}}, - &node.Leaf{Key: []byte{5}}, - }, + Children: padRightChildren([]*Node{ + {Key: []byte{4}}, + {Key: []byte{5}}, + }), }, prefix: []byte{9, 8, 7}, keys: [][]byte{{1}, {2}}, @@ -1995,24 +1995,24 @@ func Test_Trie_Get(t *testing.T) { }{ "some trie": { trie: Trie{ - root: &node.Branch{ + root: &Node{ Key: []byte{0, 1}, Value: []byte{1, 3}, Descendants: 3, - Children: [16]node.Node{ - &node.Branch{ // full key 0, 1, 0, 3 + Children: padRightChildren([]*Node{ + { // full key 0, 1, 0, 3 Key: []byte{3}, Value: []byte{1, 2}, Descendants: 1, - Children: [16]node.Node{ - &node.Leaf{Key: []byte{1}}, - }, + Children: padRightChildren([]*Node{ + {Key: []byte{1}}, + }), }, - &node.Leaf{ // full key 0, 1, 1, 9 + { // full key 0, 1, 1, 9 Key: []byte{9}, Value: []byte{1, 2, 3, 4, 5}, }, - }, + }), }, }, key: []byte{0x01, 0x19}, @@ -2036,7 +2036,7 @@ func Test_retrieve(t *testing.T) { t.Parallel() testCases := map[string]struct { - parent Node + parent *Node key []byte value []byte }{ @@ -2044,7 +2044,7 @@ func Test_retrieve(t *testing.T) { key: []byte{1}, }, "leaf key match": { - parent: &node.Leaf{ + parent: &Node{ Key: []byte{1}, Value: []byte{2}, }, @@ -2052,66 +2052,66 @@ func Test_retrieve(t *testing.T) { value: []byte{2}, }, "leaf key mismatch": { - parent: &node.Leaf{ + parent: &Node{ Key: []byte{1, 2}, Value: []byte{2}, }, key: []byte{1}, }, "branch key match": { - parent: &node.Branch{ + parent: &Node{ Key: []byte{1}, Value: []byte{2}, Descendants: 1, - Children: [16]node.Node{ - &node.Leaf{Key: []byte{1}}, - }, + Children: padRightChildren([]*Node{ + {Key: []byte{1}}, + }), }, key: []byte{1}, value: []byte{2}, }, "branch key with empty search key": { - parent: &node.Branch{ + parent: &Node{ Key: []byte{1}, Value: []byte{2}, Descendants: 1, - Children: [16]node.Node{ - &node.Leaf{Key: []byte{1}}, - }, + Children: padRightChildren([]*Node{ + {Key: []byte{1}}, + }), }, value: []byte{2}, }, "branch key mismatch with shorter search key": { - parent: &node.Branch{ + parent: &Node{ Key: []byte{1, 2}, Value: []byte{2}, Descendants: 1, - Children: [16]node.Node{ - &node.Leaf{Key: []byte{1}}, - }, + Children: padRightChildren([]*Node{ + {Key: []byte{1}}, + }), }, key: []byte{1}, }, "bottom leaf in branch": { - parent: &node.Branch{ + parent: &Node{ Key: []byte{1}, Value: []byte{1}, Descendants: 2, - Children: [16]node.Node{ + Children: padRightChildren([]*Node{ nil, nil, - &node.Branch{ // full key 1, 2, 3 + { // full key 1, 2, 3 Key: []byte{3}, Value: []byte{2}, Descendants: 1, - Children: [16]node.Node{ + Children: padRightChildren([]*Node{ nil, nil, nil, nil, - &node.Leaf{ // full key 1, 2, 3, 4, 5 + { // full key 1, 2, 3, 4, 5 Key: []byte{5}, Value: []byte{3}, }, - }, + }), }, - }, + }), }, key: []byte{1, 2, 3, 4, 5}, value: []byte{3}, @@ -2125,7 +2125,7 @@ func Test_retrieve(t *testing.T) { // Check no mutation was done copySettings := node.DeepCopySettings - var expectedParent Node + var expectedParent *Node if testCase.parent != nil { expectedParent = testCase.parent.Copy(copySettings) } @@ -2152,16 +2152,16 @@ func Test_Trie_ClearPrefixLimit(t *testing.T) { "limit is zero": {}, "clear prefix limit": { trie: Trie{ - root: &node.Branch{ + root: &Node{ Key: []byte{1, 2}, Value: []byte{1}, Descendants: 1, - Children: [16]node.Node{ + Children: padRightChildren([]*Node{ nil, nil, nil, - &node.Leaf{ + { Key: []byte{4}, }, - }, + }), }, }, prefix: []byte{0x12}, @@ -2192,10 +2192,10 @@ func Test_Trie_clearPrefixLimit(t *testing.T) { testCases := map[string]struct { trie Trie - parent Node + parent *Node prefix []byte limit uint32 - newParent Node + newParent *Node valuesDeleted uint32 nodesRemoved uint32 allDeleted bool @@ -2208,7 +2208,7 @@ func Test_Trie_clearPrefixLimit(t *testing.T) { allDeleted: true, }, "leaf parent with common prefix": { - parent: &node.Leaf{ + parent: &Node{ Key: []byte{1, 2}, }, prefix: []byte{1}, @@ -2218,7 +2218,7 @@ func Test_Trie_clearPrefixLimit(t *testing.T) { allDeleted: true, }, "leaf parent with key equal prefix": { - parent: &node.Leaf{ + parent: &Node{ Key: []byte{1}, }, prefix: []byte{1}, @@ -2231,12 +2231,12 @@ func Test_Trie_clearPrefixLimit(t *testing.T) { trie: Trie{ generation: 1, }, - parent: &node.Leaf{ + parent: &Node{ Key: []byte{1, 2}, }, prefix: []byte{1, 3}, limit: 1, - newParent: &node.Leaf{ + newParent: &Node{ Key: []byte{1, 2}, }, allDeleted: true, @@ -2245,24 +2245,24 @@ func Test_Trie_clearPrefixLimit(t *testing.T) { trie: Trie{ generation: 1, }, - parent: &node.Leaf{ + parent: &Node{ Key: []byte{1}, }, prefix: []byte{1, 2}, limit: 1, - newParent: &node.Leaf{ + newParent: &Node{ Key: []byte{1}, }, allDeleted: true, }, "branch without value parent with common prefix": { - parent: &node.Branch{ + parent: &Node{ Key: []byte{1, 2}, Descendants: 2, - Children: [16]node.Node{ - &node.Leaf{Key: []byte{1}}, - &node.Leaf{Key: []byte{2}}, - }, + Children: padRightChildren([]*Node{ + {Key: []byte{1}}, + {Key: []byte{2}}, + }), }, prefix: []byte{1}, limit: 3, @@ -2271,13 +2271,13 @@ func Test_Trie_clearPrefixLimit(t *testing.T) { allDeleted: true, }, "branch without value with key equal prefix": { - parent: &node.Branch{ + parent: &Node{ Key: []byte{1, 2}, Descendants: 2, - Children: [16]node.Node{ - &node.Leaf{Key: []byte{1}}, - &node.Leaf{Key: []byte{2}}, - }, + Children: padRightChildren([]*Node{ + {Key: []byte{1}}, + {Key: []byte{2}}, + }), }, prefix: []byte{1, 2}, limit: 3, @@ -2289,23 +2289,23 @@ func Test_Trie_clearPrefixLimit(t *testing.T) { trie: Trie{ generation: 1, }, - parent: &node.Branch{ + parent: &Node{ Key: []byte{1, 2}, Descendants: 2, - Children: [16]node.Node{ - &node.Leaf{Key: []byte{1}}, - &node.Leaf{Key: []byte{2}}, - }, + Children: padRightChildren([]*Node{ + {Key: []byte{1}}, + {Key: []byte{2}}, + }), }, prefix: []byte{1, 3}, limit: 1, - newParent: &node.Branch{ + newParent: &Node{ Key: []byte{1, 2}, Descendants: 2, - Children: [16]node.Node{ - &node.Leaf{Key: []byte{1}}, - &node.Leaf{Key: []byte{2}}, - }, + Children: padRightChildren([]*Node{ + {Key: []byte{1}}, + {Key: []byte{2}}, + }), }, allDeleted: true, }, @@ -2313,23 +2313,23 @@ func Test_Trie_clearPrefixLimit(t *testing.T) { trie: Trie{ generation: 1, }, - parent: &node.Branch{ + parent: &Node{ Key: []byte{1}, Descendants: 2, - Children: [16]node.Node{ - &node.Leaf{Key: []byte{1}}, - &node.Leaf{Key: []byte{2}}, - }, + Children: padRightChildren([]*Node{ + {Key: []byte{1}}, + {Key: []byte{2}}, + }), }, prefix: []byte{1, 2, 3}, limit: 1, - newParent: &node.Branch{ + newParent: &Node{ Key: []byte{1}, Descendants: 2, - Children: [16]node.Node{ - &node.Leaf{Key: []byte{1}}, - &node.Leaf{Key: []byte{2}}, - }, + Children: padRightChildren([]*Node{ + {Key: []byte{1}}, + {Key: []byte{2}}, + }), }, allDeleted: true, }, @@ -2337,34 +2337,34 @@ func Test_Trie_clearPrefixLimit(t *testing.T) { trie: Trie{ generation: 1, }, - parent: &node.Branch{ + parent: &Node{ Key: []byte{1}, Descendants: 2, - Children: [16]node.Node{ - &node.Leaf{Key: []byte{1}}, - &node.Leaf{Key: []byte{2}}, - }, + Children: padRightChildren([]*Node{ + {Key: []byte{1}}, + {Key: []byte{2}}, + }), }, prefix: []byte{1, 2}, limit: 1, - newParent: &node.Branch{ + newParent: &Node{ Key: []byte{1}, Descendants: 2, - Children: [16]node.Node{ - &node.Leaf{Key: []byte{1}}, - &node.Leaf{Key: []byte{2}}, - }, + Children: padRightChildren([]*Node{ + {Key: []byte{1}}, + {Key: []byte{2}}, + }), }, allDeleted: true, }, "branch with value with common prefix": { - parent: &node.Branch{ + parent: &Node{ Key: []byte{1, 2}, Value: []byte{1}, Descendants: 1, - Children: [16]node.Node{ - &node.Leaf{Key: []byte{1}}, - }, + Children: padRightChildren([]*Node{ + {Key: []byte{1}}, + }), }, prefix: []byte{1}, limit: 2, @@ -2373,13 +2373,13 @@ func Test_Trie_clearPrefixLimit(t *testing.T) { allDeleted: true, }, "branch with value with key equal prefix": { - parent: &node.Branch{ + parent: &Node{ Key: []byte{1, 2}, Value: []byte{1}, Descendants: 1, - Children: [16]node.Node{ - &node.Leaf{Key: []byte{1}}, - }, + Children: padRightChildren([]*Node{ + {Key: []byte{1}}, + }), }, prefix: []byte{1, 2}, limit: 2, @@ -2391,23 +2391,23 @@ func Test_Trie_clearPrefixLimit(t *testing.T) { trie: Trie{ generation: 1, }, - parent: &node.Branch{ + parent: &Node{ Key: []byte{1, 2}, Value: []byte{1}, Descendants: 1, - Children: [16]node.Node{ - &node.Leaf{Key: []byte{1}}, - }, + Children: padRightChildren([]*Node{ + {Key: []byte{1}}, + }), }, prefix: []byte{1, 3}, limit: 1, - newParent: &node.Branch{ + newParent: &Node{ Key: []byte{1, 2}, Value: []byte{1}, Descendants: 1, - Children: [16]node.Node{ - &node.Leaf{Key: []byte{1}}, - }, + Children: padRightChildren([]*Node{ + {Key: []byte{1}}, + }), }, allDeleted: true, }, @@ -2415,23 +2415,23 @@ func Test_Trie_clearPrefixLimit(t *testing.T) { trie: Trie{ generation: 1, }, - parent: &node.Branch{ + parent: &Node{ Key: []byte{1}, Value: []byte{1}, Descendants: 1, - Children: [16]node.Node{ - &node.Leaf{Key: []byte{1}}, - }, + Children: padRightChildren([]*Node{ + {Key: []byte{1}}, + }), }, prefix: []byte{1, 2, 3}, limit: 1, - newParent: &node.Branch{ + newParent: &Node{ Key: []byte{1}, Value: []byte{1}, Descendants: 1, - Children: [16]node.Node{ - &node.Leaf{Key: []byte{1}}, - }, + Children: padRightChildren([]*Node{ + {Key: []byte{1}}, + }), }, allDeleted: true, }, @@ -2439,23 +2439,23 @@ func Test_Trie_clearPrefixLimit(t *testing.T) { trie: Trie{ generation: 1, }, - parent: &node.Branch{ + parent: &Node{ Key: []byte{1}, Value: []byte{1}, Descendants: 1, - Children: [16]node.Node{ - &node.Leaf{Key: []byte{1}}, - }, + Children: padRightChildren([]*Node{ + {Key: []byte{1}}, + }), }, prefix: []byte{1, 2}, limit: 1, - newParent: &node.Branch{ + newParent: &Node{ Key: []byte{1}, Value: []byte{1}, Descendants: 1, - Children: [16]node.Node{ - &node.Leaf{Key: []byte{1}}, - }, + Children: padRightChildren([]*Node{ + {Key: []byte{1}}, + }), }, allDeleted: true, }, @@ -2463,43 +2463,43 @@ func Test_Trie_clearPrefixLimit(t *testing.T) { trie: Trie{ generation: 1, }, - parent: &node.Branch{ + parent: &Node{ Key: []byte{1}, Value: []byte{1}, Descendants: 2, - Children: [16]node.Node{ - &node.Leaf{Key: []byte{3}}, - &node.Leaf{Key: []byte{4}}, - }, + Children: padRightChildren([]*Node{ + {Key: []byte{3}}, + {Key: []byte{4}}, + }), }, prefix: []byte{1}, limit: 1, - newParent: &node.Branch{ + newParent: &Node{ Key: []byte{1}, Value: []byte{1}, Dirty: true, Generation: 1, Descendants: 1, - Children: [16]node.Node{ + Children: padRightChildren([]*Node{ nil, - &node.Leaf{Key: []byte{4}}, - }, + {Key: []byte{4}}, + }), }, valuesDeleted: 1, nodesRemoved: 1, }, "delete only child of branch": { - parent: &node.Branch{ + parent: &Node{ Key: []byte{1}, Value: []byte{1}, Descendants: 1, - Children: [16]node.Node{ - &node.Leaf{Key: []byte{3}}, - }, + Children: padRightChildren([]*Node{ + {Key: []byte{3}}, + }), }, prefix: []byte{1, 0}, limit: 1, - newParent: &node.Leaf{ + newParent: &Node{ Key: []byte{1}, Value: []byte{1}, Dirty: true, @@ -2512,18 +2512,18 @@ func Test_Trie_clearPrefixLimit(t *testing.T) { trie: Trie{ generation: 1, }, - parent: &node.Branch{ + parent: &Node{ Key: []byte{1}, Value: []byte{1}, Descendants: 2, - Children: [16]node.Node{ - &node.Leaf{Key: []byte{3}}, - &node.Leaf{Key: []byte{4}}, - }, + Children: padRightChildren([]*Node{ + {Key: []byte{3}}, + {Key: []byte{4}}, + }), }, prefix: []byte{1}, limit: 2, - newParent: &node.Leaf{ + newParent: &Node{ Key: []byte{1}, Value: []byte{1}, Dirty: true, @@ -2533,13 +2533,13 @@ func Test_Trie_clearPrefixLimit(t *testing.T) { nodesRemoved: 2, }, "fully delete children of branch without value": { - parent: &node.Branch{ + parent: &Node{ Key: []byte{1}, Descendants: 2, - Children: [16]node.Node{ - &node.Leaf{Key: []byte{3}}, - &node.Leaf{Key: []byte{4}}, - }, + Children: padRightChildren([]*Node{ + {Key: []byte{3}}, + {Key: []byte{4}}, + }), }, prefix: []byte{1}, limit: 2, @@ -2551,46 +2551,46 @@ func Test_Trie_clearPrefixLimit(t *testing.T) { trie: Trie{ generation: 1, }, - parent: &node.Branch{ + parent: &Node{ Key: []byte{1}, Value: []byte{1}, Descendants: 3, - Children: [16]node.Node{ - &node.Branch{ // full key 1, 0, 3 + Children: padRightChildren([]*Node{ + { // full key 1, 0, 3 Key: []byte{3}, Value: []byte{1}, Descendants: 1, - Children: [16]node.Node{ - &node.Leaf{ // full key 1, 0, 3, 0, 5 + Children: padRightChildren([]*Node{ + { // full key 1, 0, 3, 0, 5 Key: []byte{5}, }, - }, + }), }, - &node.Leaf{ + { Key: []byte{6}, }, - }, + }), }, prefix: []byte{1, 0}, limit: 1, - newParent: &node.Branch{ + newParent: &Node{ Key: []byte{1}, Value: []byte{1}, Dirty: true, Generation: 1, Descendants: 2, - Children: [16]node.Node{ - &node.Leaf{ // full key 1, 0, 3 + Children: padRightChildren([]*Node{ + { // full key 1, 0, 3 Key: []byte{3}, Value: []byte{1}, Dirty: true, Generation: 1, }, - &node.Leaf{ + { Key: []byte{6}, // Not modified so same generation as before }, - }, + }), }, valuesDeleted: 1, nodesRemoved: 1, @@ -2599,24 +2599,24 @@ func Test_Trie_clearPrefixLimit(t *testing.T) { trie: Trie{ generation: 1, }, - parent: &node.Branch{ + parent: &Node{ Key: []byte{1}, Value: []byte{1}, Descendants: 2, - Children: [16]node.Node{ - &node.Branch{ // full key 1, 0, 2 + Children: padRightChildren([]*Node{ + { // full key 1, 0, 2 Key: []byte{2}, Value: []byte{1}, Descendants: 1, - Children: [16]node.Node{ - &node.Leaf{Key: []byte{1}}, - }, + Children: padRightChildren([]*Node{ + {Key: []byte{1}}, + }), }, - }, + }), }, prefix: []byte{1, 0, 2}, limit: 2, - newParent: &node.Leaf{ + newParent: &Node{ Key: []byte{1}, Value: []byte{1}, Dirty: true, @@ -2630,17 +2630,17 @@ func Test_Trie_clearPrefixLimit(t *testing.T) { trie: Trie{ generation: 1, }, - parent: &node.Branch{ + parent: &Node{ Key: []byte{1}, Descendants: 2, - Children: [16]node.Node{ - &node.Leaf{Key: []byte{3}}, - &node.Leaf{Key: []byte{4}}, - }, + Children: padRightChildren([]*Node{ + {Key: []byte{3}}, + {Key: []byte{4}}, + }), }, prefix: []byte{1, 0, 3}, limit: 3, - newParent: &node.Leaf{ + newParent: &Node{ Key: []byte{1, 1, 4}, Dirty: true, Generation: 1, @@ -2653,17 +2653,17 @@ func Test_Trie_clearPrefixLimit(t *testing.T) { trie: Trie{ generation: 1, }, - parent: &node.Branch{ + parent: &Node{ Key: []byte{1}, Descendants: 2, - Children: [16]node.Node{ - &node.Leaf{Key: []byte{3}}, - &node.Leaf{Key: []byte{4}}, - }, + Children: padRightChildren([]*Node{ + {Key: []byte{3}}, + {Key: []byte{4}}, + }), }, prefix: []byte{1, 0}, limit: 3, - newParent: &node.Leaf{ + newParent: &Node{ Key: []byte{1, 1, 4}, Dirty: true, Generation: 1, @@ -2676,20 +2676,22 @@ func Test_Trie_clearPrefixLimit(t *testing.T) { trie: Trie{ generation: 1, }, - parent: &node.Branch{ - Key: []byte{1}, - Value: []byte{1}, - Children: [16]node.Node{ - &node.Leaf{Key: []byte{3}}, - }, + parent: &Node{ + Key: []byte{1}, + Value: []byte{1}, + Descendants: 1, + Children: padRightChildren([]*Node{ + {Key: []byte{3}}, + }), }, prefix: []byte{1, 0}, - newParent: &node.Branch{ - Key: []byte{1}, - Value: []byte{1}, - Children: [16]node.Node{ - &node.Leaf{Key: []byte{3}}, - }, + newParent: &Node{ + Key: []byte{1}, + Value: []byte{1}, + Descendants: 1, + Children: padRightChildren([]*Node{ + {Key: []byte{3}}, + }), }, }, } @@ -2719,10 +2721,10 @@ func Test_Trie_deleteNodesLimit(t *testing.T) { testCases := map[string]struct { trie Trie - parent Node + parent *Node prefix []byte limit uint32 - newNode Node + newNode *Node valuesDeleted uint32 nodesRemoved uint32 }{ @@ -2730,10 +2732,10 @@ func Test_Trie_deleteNodesLimit(t *testing.T) { trie: Trie{ generation: 1, }, - parent: &node.Leaf{ + parent: &Node{ Key: []byte{1}, }, - newNode: &node.Leaf{ + newNode: &Node{ Key: []byte{1}, }, }, @@ -2741,44 +2743,44 @@ func Test_Trie_deleteNodesLimit(t *testing.T) { limit: 1, }, "delete leaf": { - parent: &node.Leaf{}, + parent: &Node{}, limit: 2, valuesDeleted: 1, nodesRemoved: 1, }, "delete branch without value": { - parent: &node.Branch{ + parent: &Node{ Descendants: 2, - Children: [16]node.Node{ - &node.Leaf{}, - &node.Leaf{}, - }, + Children: padRightChildren([]*Node{ + {}, + {}, + }), }, limit: 3, valuesDeleted: 2, nodesRemoved: 3, }, "delete branch with value": { - parent: &node.Branch{ + parent: &Node{ Key: []byte{3}, Value: []byte{1}, Descendants: 1, - Children: [16]node.Node{ - &node.Leaf{}, - }, + Children: padRightChildren([]*Node{ + {}, + }), }, limit: 3, valuesDeleted: 2, nodesRemoved: 2, }, "delete branch and all children": { - parent: &node.Branch{ + parent: &Node{ Key: []byte{3}, Descendants: 2, - Children: [16]node.Node{ - &node.Leaf{Key: []byte{1}}, - &node.Leaf{Key: []byte{2}}, - }, + Children: padRightChildren([]*Node{ + {Key: []byte{1}}, + {Key: []byte{2}}, + }), }, limit: 10, valuesDeleted: 2, @@ -2788,26 +2790,26 @@ func Test_Trie_deleteNodesLimit(t *testing.T) { trie: Trie{ generation: 1, }, - parent: &node.Branch{ + parent: &Node{ Key: []byte{3}, Value: []byte{1, 2, 3}, Descendants: 2, - Children: [16]node.Node{ - &node.Leaf{Key: []byte{1}}, - &node.Leaf{Key: []byte{2}}, - }, + Children: padRightChildren([]*Node{ + {Key: []byte{1}}, + {Key: []byte{2}}, + }), }, limit: 1, - newNode: &node.Branch{ + newNode: &Node{ Key: []byte{3}, Value: []byte{1, 2, 3}, Dirty: true, Generation: 1, Descendants: 1, - Children: [16]node.Node{ + Children: padRightChildren([]*Node{ nil, - &node.Leaf{Key: []byte{2}}, - }, + {Key: []byte{2}}, + }), }, valuesDeleted: 1, nodesRemoved: 1, @@ -2816,17 +2818,17 @@ func Test_Trie_deleteNodesLimit(t *testing.T) { trie: Trie{ generation: 1, }, - parent: &node.Branch{ + parent: &Node{ Key: []byte{3}, Value: []byte{1, 2, 3}, Descendants: 2, - Children: [16]node.Node{ - &node.Leaf{Key: []byte{1}}, - &node.Leaf{Key: []byte{2}}, - }, + Children: padRightChildren([]*Node{ + {Key: []byte{1}}, + {Key: []byte{2}}, + }), }, limit: 2, - newNode: &node.Leaf{ + newNode: &Node{ Key: []byte{3}, Value: []byte{1, 2, 3}, Dirty: true, @@ -2839,21 +2841,21 @@ func Test_Trie_deleteNodesLimit(t *testing.T) { trie: Trie{ generation: 1, }, - parent: &node.Branch{ + parent: &Node{ Key: []byte{3}, Descendants: 3, - Children: [16]node.Node{ + Children: padRightChildren([]*Node{ nil, - &node.Leaf{Key: []byte{1}}, + {Key: []byte{1}}, nil, - &node.Leaf{Key: []byte{2}}, + {Key: []byte{2}}, nil, - &node.Leaf{Key: []byte{3}}, - }, + {Key: []byte{3}}, + }), }, prefix: []byte{1, 2}, limit: 2, - newNode: &node.Leaf{ + newNode: &Node{ Key: []byte{3, 5, 3}, Generation: 1, Dirty: true, @@ -2892,12 +2894,12 @@ func Test_Trie_ClearPrefix(t *testing.T) { }{ "nil prefix": { trie: Trie{ - root: &node.Leaf{}, + root: &Node{}, }, }, "empty prefix": { trie: Trie{ - root: &node.Leaf{}, + root: &Node{}, }, prefix: []byte{}, }, @@ -2906,28 +2908,28 @@ func Test_Trie_ClearPrefix(t *testing.T) { }, "clear prefix": { trie: Trie{ - root: &node.Branch{ + root: &Node{ Key: []byte{1, 2}, Descendants: 3, - Children: [16]node.Node{ - &node.Leaf{ // full key in nibbles 1, 2, 0, 5 + Children: padRightChildren([]*Node{ + { // full key in nibbles 1, 2, 0, 5 Key: []byte{5}, }, - &node.Branch{ // full key in nibbles 1, 2, 1, 6 + { // full key in nibbles 1, 2, 1, 6 Key: []byte{6}, Value: []byte("bottom branch"), - Children: [16]node.Node{ - &node.Leaf{ // full key in nibbles 1, 2, 1, 6, 0, 7 + Children: padRightChildren([]*Node{ + { // full key in nibbles 1, 2, 1, 6, 0, 7 Key: []byte{7}, }, - }, + }), }, - }, + }), }, }, prefix: []byte{0x12, 0x16}, expectedTrie: Trie{ - root: &node.Leaf{ + root: &Node{ Key: []byte{1, 2, 0, 5}, Dirty: true, }, @@ -2960,25 +2962,25 @@ func Test_Trie_clearPrefix(t *testing.T) { testCases := map[string]struct { trie Trie - parent Node + parent *Node prefix []byte - newParent Node + newParent *Node nodesRemoved uint32 }{ "delete one of two children of branch": { trie: Trie{ generation: 1, }, - parent: &node.Branch{ + parent: &Node{ Key: []byte{1}, Descendants: 2, - Children: [16]node.Node{ - &node.Leaf{Key: []byte{3}}, - &node.Leaf{Key: []byte{4}}, - }, + Children: padRightChildren([]*Node{ + {Key: []byte{3}}, + {Key: []byte{4}}, + }), }, prefix: []byte{1, 0}, - newParent: &node.Leaf{ + newParent: &Node{ Key: []byte{1, 1, 4}, Dirty: true, Generation: 1, @@ -2987,14 +2989,14 @@ func Test_Trie_clearPrefix(t *testing.T) { }, "nil parent": {}, "leaf parent with common prefix": { - parent: &node.Leaf{ + parent: &Node{ Key: []byte{1, 2}, }, prefix: []byte{1}, nodesRemoved: 1, }, "leaf parent with key equal prefix": { - parent: &node.Leaf{ + parent: &Node{ Key: []byte{1}, }, prefix: []byte{1}, @@ -3004,11 +3006,11 @@ func Test_Trie_clearPrefix(t *testing.T) { trie: Trie{ generation: 1, }, - parent: &node.Leaf{ + parent: &Node{ Key: []byte{1, 2}, }, prefix: []byte{1, 3}, - newParent: &node.Leaf{ + newParent: &Node{ Key: []byte{1, 2}, }, }, @@ -3016,34 +3018,34 @@ func Test_Trie_clearPrefix(t *testing.T) { trie: Trie{ generation: 1, }, - parent: &node.Leaf{ + parent: &Node{ Key: []byte{1}, }, prefix: []byte{1, 2}, - newParent: &node.Leaf{ + newParent: &Node{ Key: []byte{1}, }, }, "branch parent with common prefix": { - parent: &node.Branch{ + parent: &Node{ Key: []byte{1, 2}, Value: []byte{1}, Descendants: 1, - Children: [16]node.Node{ - &node.Leaf{}, - }, + Children: padRightChildren([]*Node{ + {}, + }), }, prefix: []byte{1}, nodesRemoved: 2, }, "branch with key equal prefix": { - parent: &node.Branch{ + parent: &Node{ Key: []byte{1, 2}, Value: []byte{1}, Descendants: 1, - Children: [16]node.Node{ - &node.Leaf{}, - }, + Children: padRightChildren([]*Node{ + {}, + }), }, prefix: []byte{1, 2}, nodesRemoved: 2, @@ -3052,92 +3054,92 @@ func Test_Trie_clearPrefix(t *testing.T) { trie: Trie{ generation: 1, }, - parent: &node.Branch{ + parent: &Node{ Key: []byte{1, 2}, Value: []byte{1}, Descendants: 1, - Children: [16]node.Node{ - &node.Leaf{}, - }, + Children: padRightChildren([]*Node{ + {}, + }), }, prefix: []byte{1, 3}, - newParent: &node.Branch{ + newParent: &Node{ Key: []byte{1, 2}, Value: []byte{1}, Descendants: 1, - Children: [16]node.Node{ - &node.Leaf{}, - }, + Children: padRightChildren([]*Node{ + {}, + }), }, }, "branch with key smaller than prefix by more than one": { trie: Trie{ generation: 1, }, - parent: &node.Branch{ + parent: &Node{ Key: []byte{1}, Value: []byte{1}, Descendants: 1, - Children: [16]node.Node{ - &node.Leaf{}, - }, + Children: padRightChildren([]*Node{ + {}, + }), }, prefix: []byte{1, 2, 3}, - newParent: &node.Branch{ + newParent: &Node{ Key: []byte{1}, Value: []byte{1}, Descendants: 1, - Children: [16]node.Node{ - &node.Leaf{}, - }, + Children: padRightChildren([]*Node{ + {}, + }), }, }, "branch with key smaller than prefix by one": { trie: Trie{ generation: 1, }, - parent: &node.Branch{ + parent: &Node{ Key: []byte{1}, Value: []byte{1}, Descendants: 1, - Children: [16]node.Node{ - &node.Leaf{}, - }, + Children: padRightChildren([]*Node{ + {}, + }), }, prefix: []byte{1, 2}, - newParent: &node.Branch{ + newParent: &Node{ Key: []byte{1}, Value: []byte{1}, Descendants: 1, - Children: [16]node.Node{ - &node.Leaf{}, - }, + Children: padRightChildren([]*Node{ + {}, + }), }, }, "delete one child of branch": { trie: Trie{ generation: 1, }, - parent: &node.Branch{ + parent: &Node{ Key: []byte{1}, Value: []byte{1}, Descendants: 2, - Children: [16]node.Node{ - &node.Leaf{Key: []byte{3}}, - &node.Leaf{Key: []byte{4}}, - }, + Children: padRightChildren([]*Node{ + {Key: []byte{3}}, + {Key: []byte{4}}, + }), }, prefix: []byte{1, 0, 3}, - newParent: &node.Branch{ + newParent: &Node{ Key: []byte{1}, Value: []byte{1}, Dirty: true, Generation: 1, Descendants: 1, - Children: [16]node.Node{ + Children: padRightChildren([]*Node{ nil, - &node.Leaf{Key: []byte{4}}, - }, + {Key: []byte{4}}, + }), }, nodesRemoved: 1, }, @@ -3145,16 +3147,16 @@ func Test_Trie_clearPrefix(t *testing.T) { trie: Trie{ generation: 1, }, - parent: &node.Branch{ + parent: &Node{ Key: []byte{1}, Value: []byte{1}, Descendants: 1, - Children: [16]node.Node{ - &node.Leaf{Key: []byte{3}}, - }, + Children: padRightChildren([]*Node{ + {Key: []byte{3}}, + }), }, prefix: []byte{1, 0}, - newParent: &node.Leaf{ + newParent: &Node{ Key: []byte{1}, Value: []byte{1}, Dirty: true, @@ -3166,38 +3168,38 @@ func Test_Trie_clearPrefix(t *testing.T) { trie: Trie{ generation: 1, }, - parent: &node.Branch{ + parent: &Node{ Key: []byte{1}, Value: []byte{1}, Descendants: 2, - Children: [16]node.Node{ - &node.Branch{ // full key 1, 0, 3 + Children: padRightChildren([]*Node{ + { // full key 1, 0, 3 Key: []byte{3}, Value: []byte{1}, Descendants: 1, - Children: [16]node.Node{ - &node.Leaf{ // full key 1, 0, 3, 0, 5 + Children: padRightChildren([]*Node{ + { // full key 1, 0, 3, 0, 5 Key: []byte{5}, }, - }, + }), }, - }, + }), }, prefix: []byte{1, 0, 3, 0}, - newParent: &node.Branch{ + newParent: &Node{ Key: []byte{1}, Value: []byte{1}, Dirty: true, Generation: 1, Descendants: 1, - Children: [16]node.Node{ - &node.Leaf{ // full key 1, 0, 3 + Children: padRightChildren([]*Node{ + { // full key 1, 0, 3 Key: []byte{3}, Value: []byte{1}, Dirty: true, Generation: 1, }, - }, + }), }, nodesRemoved: 1, }, @@ -3205,16 +3207,16 @@ func Test_Trie_clearPrefix(t *testing.T) { trie: Trie{ generation: 1, }, - parent: &node.Branch{ + parent: &Node{ Key: []byte{1}, Descendants: 2, - Children: [16]node.Node{ - &node.Leaf{Key: []byte{3}}, // full key 1, 0, 3 - &node.Leaf{Key: []byte{4}}, // full key 1, 1, 4 - }, + Children: padRightChildren([]*Node{ + {Key: []byte{3}}, // full key 1, 0, 3 + {Key: []byte{4}}, // full key 1, 1, 4 + }), }, prefix: []byte{1, 0, 3}, - newParent: &node.Leaf{ + newParent: &Node{ Key: []byte{1, 1, 4}, Dirty: true, Generation: 1, @@ -3251,12 +3253,12 @@ func Test_Trie_Delete(t *testing.T) { }{ "nil key": { trie: Trie{ - root: &node.Leaf{}, + root: &Node{}, }, }, "empty key": { trie: Trie{ - root: &node.Leaf{}, + root: &Node{}, }, }, "empty trie": { @@ -3265,48 +3267,48 @@ func Test_Trie_Delete(t *testing.T) { "delete branch node": { trie: Trie{ generation: 1, - root: &node.Branch{ + root: &Node{ Key: []byte{1, 2}, Descendants: 3, - Children: [16]node.Node{ - &node.Leaf{ + Children: padRightChildren([]*Node{ + { Key: []byte{5}, Value: []byte{97}, }, - &node.Branch{ // full key in nibbles 1, 2, 1, 6 + { // full key in nibbles 1, 2, 1, 6 Key: []byte{6}, Value: []byte{98}, Descendants: 1, - Children: [16]node.Node{ - &node.Leaf{ // full key in nibbles 1, 2, 1, 6, 0, 7 + Children: padRightChildren([]*Node{ + { // full key in nibbles 1, 2, 1, 6, 0, 7 Key: []byte{7}, Value: []byte{99}, }, - }, + }), }, - }, + }), }, }, key: []byte{0x12, 0x16}, expectedTrie: Trie{ generation: 1, - root: &node.Branch{ + root: &Node{ Key: []byte{1, 2}, Dirty: true, Generation: 1, Descendants: 2, - Children: [16]node.Node{ - &node.Leaf{ + Children: padRightChildren([]*Node{ + { Key: []byte{5}, Value: []byte{97}, }, - &node.Leaf{ // full key in nibbles 1, 2, 1, 6 + { // full key in nibbles 1, 2, 1, 6 Key: []byte{6, 0, 7}, Value: []byte{99}, Dirty: true, Generation: 1, }, - }, + }), }, }, }, @@ -3337,9 +3339,9 @@ func Test_Trie_delete(t *testing.T) { testCases := map[string]struct { trie Trie - parent Node + parent *Node key []byte - newParent Node + newParent *Node updated bool nodesRemoved uint32 }{ @@ -3347,14 +3349,14 @@ func Test_Trie_delete(t *testing.T) { key: []byte{1}, }, "leaf parent and nil key": { - parent: &node.Leaf{ + parent: &Node{ Key: []byte{1}, }, updated: true, nodesRemoved: 1, }, "leaf parent and empty key": { - parent: &node.Leaf{ + parent: &Node{ Key: []byte{1}, }, key: []byte{}, @@ -3362,7 +3364,7 @@ func Test_Trie_delete(t *testing.T) { nodesRemoved: 1, }, "leaf parent matches key": { - parent: &node.Leaf{ + parent: &Node{ Key: []byte{1}, }, key: []byte{1}, @@ -3373,11 +3375,11 @@ func Test_Trie_delete(t *testing.T) { trie: Trie{ generation: 1, }, - parent: &node.Leaf{ + parent: &Node{ Key: []byte{1}, }, key: []byte{2}, - newParent: &node.Leaf{ + newParent: &Node{ Key: []byte{1}, }, }, @@ -3385,17 +3387,17 @@ func Test_Trie_delete(t *testing.T) { trie: Trie{ generation: 1, }, - parent: &node.Branch{ + parent: &Node{ Key: []byte{1}, Value: []byte{1}, Descendants: 1, - Children: [16]node.Node{ - &node.Leaf{ + Children: padRightChildren([]*Node{ + { Key: []byte{2}, }, - }, + }), }, - newParent: &node.Leaf{ + newParent: &Node{ Key: []byte{1, 0, 2}, Dirty: true, Generation: 1, @@ -3407,18 +3409,16 @@ func Test_Trie_delete(t *testing.T) { trie: Trie{ generation: 1, }, - parent: &node.Branch{ + parent: &Node{ Key: []byte{1}, Value: []byte{1}, Descendants: 1, - Children: [16]node.Node{ - &node.Leaf{ - Key: []byte{2}, - }, - }, + Children: padRightChildren([]*Node{ + {Key: []byte{2}}, + }), }, key: []byte{}, - newParent: &node.Leaf{ + newParent: &Node{ Key: []byte{1, 0, 2}, Dirty: true, Generation: 1, @@ -3430,18 +3430,16 @@ func Test_Trie_delete(t *testing.T) { trie: Trie{ generation: 1, }, - parent: &node.Branch{ + parent: &Node{ Key: []byte{1}, Value: []byte{1}, Descendants: 1, - Children: [16]node.Node{ - &node.Leaf{ - Key: []byte{2}, - }, - }, + Children: padRightChildren([]*Node{ + {Key: []byte{2}}, + }), }, key: []byte{1}, - newParent: &node.Leaf{ + newParent: &Node{ Key: []byte{1, 0, 2}, Dirty: true, Generation: 1, @@ -3453,18 +3451,18 @@ func Test_Trie_delete(t *testing.T) { trie: Trie{ generation: 1, }, - parent: &node.Branch{ + parent: &Node{ Key: []byte{1}, Value: []byte{1}, Descendants: 1, - Children: [16]node.Node{ - &node.Leaf{ // full key 1, 0, 2 + Children: padRightChildren([]*Node{ + { // full key 1, 0, 2 Key: []byte{2}, }, - }, + }), }, key: []byte{1, 0, 2}, - newParent: &node.Leaf{ + newParent: &Node{ Key: []byte{1}, Value: []byte{1}, Dirty: true, @@ -3477,70 +3475,70 @@ func Test_Trie_delete(t *testing.T) { trie: Trie{ generation: 1, }, - parent: &node.Branch{ + parent: &Node{ Key: []byte{1}, Value: []byte{1}, Descendants: 1, - Children: [16]node.Node{ - &node.Leaf{}, - }, + Children: padRightChildren([]*Node{ + {}, + }), }, key: []byte{2}, - newParent: &node.Branch{ + newParent: &Node{ Key: []byte{1}, Value: []byte{1}, Descendants: 1, - Children: [16]node.Node{ - &node.Leaf{}, - }, + Children: padRightChildren([]*Node{ + {}, + }), }, }, "branch parent child mismatches key": { trie: Trie{ generation: 1, }, - parent: &node.Branch{ + parent: &Node{ Key: []byte{1}, Value: []byte{1}, Descendants: 1, - Children: [16]node.Node{ - &node.Leaf{ // full key 1, 0, 2 + Children: padRightChildren([]*Node{ + { // full key 1, 0, 2 Key: []byte{2}, }, - }, + }), }, key: []byte{1, 0, 3}, - newParent: &node.Branch{ + newParent: &Node{ Key: []byte{1}, Value: []byte{1}, Descendants: 1, - Children: [16]node.Node{ - &node.Leaf{ // full key 1, 0, 2 + Children: padRightChildren([]*Node{ + { // full key 1, 0, 2 Key: []byte{2}, }, - }, + }), }, }, "delete branch child and merge branch and left child": { trie: Trie{ generation: 1, }, - parent: &node.Branch{ + parent: &Node{ Key: []byte{1}, Descendants: 1, - Children: [16]node.Node{ - &node.Leaf{ // full key 1, 0, 2 + Children: padRightChildren([]*Node{ + { // full key 1, 0, 2 Key: []byte{2}, Value: []byte{1}, }, - &node.Leaf{ // full key 1, 1, 2 + { // full key 1, 1, 2 Key: []byte{2}, Value: []byte{2}, }, - }, + }), }, key: []byte{1, 0, 2}, - newParent: &node.Leaf{ + newParent: &Node{ Key: []byte{1, 1, 2}, Value: []byte{2}, Dirty: true, @@ -3553,25 +3551,25 @@ func Test_Trie_delete(t *testing.T) { trie: Trie{ generation: 1, }, - parent: &node.Branch{ + parent: &Node{ Key: []byte{1}, Value: []byte{1}, Descendants: 2, - Children: [16]node.Node{ - &node.Leaf{Key: []byte{2}}, - &node.Leaf{Key: []byte{2}}, - }, + Children: padRightChildren([]*Node{ + {Key: []byte{2}}, + {Key: []byte{2}}, + }), }, key: []byte{1}, - newParent: &node.Branch{ + newParent: &Node{ Key: []byte{1}, Generation: 1, Dirty: true, Descendants: 2, - Children: [16]node.Node{ - &node.Leaf{Key: []byte{2}}, - &node.Leaf{Key: []byte{2}}, - }, + Children: padRightChildren([]*Node{ + {Key: []byte{2}}, + {Key: []byte{2}}, + }), }, updated: true, }, @@ -3605,19 +3603,19 @@ func Test_handleDeletion(t *testing.T) { t.Parallel() testCases := map[string]struct { - branch *node.Branch + branch *Node deletedKey []byte - newNode Node + newNode *Node branchChildMerged bool }{ "branch with value and without children": { - branch: &node.Branch{ + branch: &Node{ Key: []byte{1, 2, 3}, Value: []byte{5, 6, 7}, Generation: 1, }, deletedKey: []byte{1, 2, 3, 4}, - newNode: &node.Leaf{ + newNode: &Node{ Key: []byte{1, 2, 3}, Value: []byte{5, 6, 7}, Generation: 1, @@ -3628,39 +3626,39 @@ func Test_handleDeletion(t *testing.T) { // since it would be turned into a leaf when it only has one child // remaining. "branch with value and a single child": { - branch: &node.Branch{ + branch: &Node{ Key: []byte{1, 2, 3}, Value: []byte{5, 6, 7}, Generation: 1, - Children: [16]node.Node{ + Children: padRightChildren([]*Node{ nil, - &node.Leaf{Key: []byte{9}}, - }, + {Key: []byte{9}}, + }), }, - newNode: &node.Branch{ + newNode: &Node{ Key: []byte{1, 2, 3}, Value: []byte{5, 6, 7}, Generation: 1, - Children: [16]node.Node{ + Children: padRightChildren([]*Node{ nil, - &node.Leaf{Key: []byte{9}}, - }, + {Key: []byte{9}}, + }), }, }, "branch without value and a single leaf child": { - branch: &node.Branch{ + branch: &Node{ Key: []byte{1, 2, 3}, Generation: 1, - Children: [16]node.Node{ + Children: padRightChildren([]*Node{ nil, - &node.Leaf{ // full key 1,2,3,1,9 + { // full key 1,2,3,1,9 Key: []byte{9}, Value: []byte{10}, }, - }, + }), }, deletedKey: []byte{1, 2, 3, 4}, - newNode: &node.Leaf{ + newNode: &Node{ Key: []byte{1, 2, 3, 1, 9}, Value: []byte{10}, Generation: 1, @@ -3669,32 +3667,32 @@ func Test_handleDeletion(t *testing.T) { branchChildMerged: true, }, "branch without value and a single branch child": { - branch: &node.Branch{ + branch: &Node{ Key: []byte{1, 2, 3}, Generation: 1, - Children: [16]node.Node{ + Children: padRightChildren([]*Node{ nil, - &node.Branch{ + { Key: []byte{9}, Value: []byte{10}, - Children: [16]node.Node{ - &node.Leaf{Key: []byte{7}}, + Children: padRightChildren([]*Node{ + {Key: []byte{7}}, nil, - &node.Leaf{Key: []byte{8}}, - }, + {Key: []byte{8}}, + }), }, - }, + }), }, - newNode: &node.Branch{ + newNode: &Node{ Key: []byte{1, 2, 3, 1, 9}, Value: []byte{10}, Generation: 1, Dirty: true, - Children: [16]node.Node{ - &node.Leaf{Key: []byte{7}}, + Children: padRightChildren([]*Node{ + {Key: []byte{7}}, nil, - &node.Leaf{Key: []byte{8}}, - }, + {Key: []byte{8}}, + }), }, branchChildMerged: true, },