From 30e2318a9ddfca712a319b3aecce07beaf252d85 Mon Sep 17 00:00:00 2001 From: Quentin McGaw Date: Tue, 7 Dec 2021 09:24:49 +0000 Subject: [PATCH] Merge packages in `lib/trie/node` - `lib/trie/leaf` - `lib/trie/branch` - `lib/trie/encode` - `lib/trie/decode` --- lib/trie/branch/buffer_mock_test.go | 77 ---------- lib/trie/branch/key.go | 11 -- lib/trie/branch/writer_mock_test.go | 49 ------ lib/trie/{encode/key.go => codec/nibbles.go} | 59 +++----- lib/trie/codec/nibbles_test.go | 142 ++++++++++++++++++ lib/trie/database.go | 24 ++- lib/trie/decode.go | 6 +- lib/trie/decode/key_test.go | 138 ----------------- lib/trie/decode_test.go | 11 +- lib/trie/encode/writer_mock_test.go | 49 ------ lib/trie/encodedecode_test/nibbles_test.go | 50 ------ lib/trie/leaf/copy.go | 29 ---- lib/trie/leaf/decode.go | 50 ------ lib/trie/leaf/decode_test.go | 109 -------------- lib/trie/leaf/dirty.go | 14 -- lib/trie/leaf/generation.go | 14 -- lib/trie/leaf/header.go | 34 ----- lib/trie/leaf/header_test.go | 126 ---------------- lib/trie/leaf/key.go | 11 -- lib/trie/lookup.go | 3 +- lib/trie/{branch => node}/branch.go | 7 +- .../encode.go => node/branch_encode.go} | 20 ++- .../branch_encode_test.go} | 92 +++++------- lib/trie/{encode => node}/buffer.go | 2 +- lib/trie/{leaf => node}/buffer_mock_test.go | 4 +- lib/trie/{branch => node}/children.go | 2 +- lib/trie/{branch => node}/children_test.go | 40 +++-- lib/trie/{branch => node}/copy.go | 29 +++- lib/trie/{branch => node}/decode.go | 46 +++++- lib/trie/{branch => node}/decode_test.go | 95 ++++++++++-- lib/trie/{branch => node}/dirty.go | 12 +- .../encode_decode_test.go} | 35 ++--- .../{encode/doc.go => node/encode_doc.go} | 5 +- lib/trie/node/encode_test.go | 11 ++ lib/trie/{branch => node}/generation.go | 12 +- lib/trie/{branch => node}/hash.go | 2 +- lib/trie/{branch => node}/header.go | 30 +++- lib/trie/{branch => node}/header_test.go | 121 ++++++++++++++- lib/trie/{decode => node}/key.go | 70 ++++++--- lib/trie/{encode => node}/key_test.go | 103 +++++++------ lib/trie/{leaf => node}/leaf.go | 5 +- .../{leaf/encode.go => node/leaf_encode.go} | 8 +- .../leaf_encode_test.go} | 14 +- lib/trie/node/{interface.go => node.go} | 6 +- lib/trie/{leaf => node}/writer_mock_test.go | 4 +- lib/trie/print.go | 6 +- lib/trie/proof.go | 4 +- lib/trie/trie.go | 115 +++++++------- lib/trie/trie_test.go | 22 +-- 49 files changed, 786 insertions(+), 1142 deletions(-) delete mode 100644 lib/trie/branch/buffer_mock_test.go delete mode 100644 lib/trie/branch/key.go delete mode 100644 lib/trie/branch/writer_mock_test.go rename lib/trie/{encode/key.go => codec/nibbles.go} (55%) create mode 100644 lib/trie/codec/nibbles_test.go delete mode 100644 lib/trie/decode/key_test.go delete mode 100644 lib/trie/encode/writer_mock_test.go delete mode 100644 lib/trie/encodedecode_test/nibbles_test.go delete mode 100644 lib/trie/leaf/copy.go delete mode 100644 lib/trie/leaf/decode.go delete mode 100644 lib/trie/leaf/decode_test.go delete mode 100644 lib/trie/leaf/dirty.go delete mode 100644 lib/trie/leaf/generation.go delete mode 100644 lib/trie/leaf/header.go delete mode 100644 lib/trie/leaf/header_test.go delete mode 100644 lib/trie/leaf/key.go rename lib/trie/{branch => node}/branch.go (85%) rename lib/trie/{branch/encode.go => node/branch_encode.go} (90%) rename lib/trie/{branch/encode_test.go => node/branch_encode_test.go} (86%) rename lib/trie/{encode => node}/buffer.go (94%) rename lib/trie/{leaf => node}/buffer_mock_test.go (97%) rename lib/trie/{branch => node}/children.go (97%) rename lib/trie/{branch => node}/children_test.go (75%) rename lib/trie/{branch => node}/copy.go (54%) rename lib/trie/{branch => node}/decode.go (63%) rename lib/trie/{branch => node}/decode_test.go (63%) rename lib/trie/{branch => node}/dirty.go (57%) rename lib/trie/{encodedecode_test/branch_test.go => node/encode_decode_test.go} (66%) rename lib/trie/{encode/doc.go => node/encode_doc.go} (92%) create mode 100644 lib/trie/node/encode_test.go rename lib/trie/{branch => node}/generation.go (56%) rename lib/trie/{branch => node}/hash.go (99%) rename lib/trie/{branch => node}/header.go (54%) rename lib/trie/{branch => node}/header_test.go (51%) rename lib/trie/{decode => node}/key.go (55%) rename lib/trie/{encode => node}/key_test.go (59%) rename lib/trie/{leaf => node}/leaf.go (87%) rename lib/trie/{leaf/encode.go => node/leaf_encode.go} (96%) rename lib/trie/{leaf/encode_test.go => node/leaf_encode_test.go} (96%) rename lib/trie/node/{interface.go => node.go} (77%) rename lib/trie/{leaf => node}/writer_mock_test.go (95%) diff --git a/lib/trie/branch/buffer_mock_test.go b/lib/trie/branch/buffer_mock_test.go deleted file mode 100644 index 3e864b1cfc9..00000000000 --- a/lib/trie/branch/buffer_mock_test.go +++ /dev/null @@ -1,77 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: github.com/ChainSafe/gossamer/lib/trie/encode (interfaces: Buffer) - -// Package branch is a generated GoMock package. -package branch - -import ( - reflect "reflect" - - gomock "github.com/golang/mock/gomock" -) - -// MockBuffer is a mock of Buffer interface. -type MockBuffer struct { - ctrl *gomock.Controller - recorder *MockBufferMockRecorder -} - -// MockBufferMockRecorder is the mock recorder for MockBuffer. -type MockBufferMockRecorder struct { - mock *MockBuffer -} - -// NewMockBuffer creates a new mock instance. -func NewMockBuffer(ctrl *gomock.Controller) *MockBuffer { - mock := &MockBuffer{ctrl: ctrl} - mock.recorder = &MockBufferMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockBuffer) EXPECT() *MockBufferMockRecorder { - return m.recorder -} - -// Bytes mocks base method. -func (m *MockBuffer) Bytes() []byte { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Bytes") - ret0, _ := ret[0].([]byte) - return ret0 -} - -// Bytes indicates an expected call of Bytes. -func (mr *MockBufferMockRecorder) Bytes() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Bytes", reflect.TypeOf((*MockBuffer)(nil).Bytes)) -} - -// Len mocks base method. -func (m *MockBuffer) Len() int { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Len") - ret0, _ := ret[0].(int) - return ret0 -} - -// Len indicates an expected call of Len. -func (mr *MockBufferMockRecorder) Len() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Len", reflect.TypeOf((*MockBuffer)(nil).Len)) -} - -// Write mocks base method. -func (m *MockBuffer) Write(arg0 []byte) (int, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Write", arg0) - ret0, _ := ret[0].(int) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Write indicates an expected call of Write. -func (mr *MockBufferMockRecorder) Write(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Write", reflect.TypeOf((*MockBuffer)(nil).Write), arg0) -} diff --git a/lib/trie/branch/key.go b/lib/trie/branch/key.go deleted file mode 100644 index aa88e8a0c3e..00000000000 --- a/lib/trie/branch/key.go +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright 2021 ChainSafe Systems (ON) -// SPDX-License-Identifier: LGPL-3.0-only - -package branch - -// 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 -} diff --git a/lib/trie/branch/writer_mock_test.go b/lib/trie/branch/writer_mock_test.go deleted file mode 100644 index 609c8f248d6..00000000000 --- a/lib/trie/branch/writer_mock_test.go +++ /dev/null @@ -1,49 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: io (interfaces: Writer) - -// Package branch is a generated GoMock package. -package branch - -import ( - reflect "reflect" - - gomock "github.com/golang/mock/gomock" -) - -// MockWriter is a mock of Writer interface. -type MockWriter struct { - ctrl *gomock.Controller - recorder *MockWriterMockRecorder -} - -// MockWriterMockRecorder is the mock recorder for MockWriter. -type MockWriterMockRecorder struct { - mock *MockWriter -} - -// NewMockWriter creates a new mock instance. -func NewMockWriter(ctrl *gomock.Controller) *MockWriter { - mock := &MockWriter{ctrl: ctrl} - mock.recorder = &MockWriterMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockWriter) EXPECT() *MockWriterMockRecorder { - return m.recorder -} - -// Write mocks base method. -func (m *MockWriter) Write(arg0 []byte) (int, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Write", arg0) - ret0, _ := ret[0].(int) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Write indicates an expected call of Write. -func (mr *MockWriterMockRecorder) Write(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Write", reflect.TypeOf((*MockWriter)(nil).Write), arg0) -} diff --git a/lib/trie/encode/key.go b/lib/trie/codec/nibbles.go similarity index 55% rename from lib/trie/encode/key.go rename to lib/trie/codec/nibbles.go index 3747df17b81..11e5a2e8181 100644 --- a/lib/trie/encode/key.go +++ b/lib/trie/codec/nibbles.go @@ -1,45 +1,7 @@ // Copyright 2021 ChainSafe Systems (ON) // SPDX-License-Identifier: LGPL-3.0-only -package encode - -import ( - "errors" - "fmt" - "io" -) - -const maxPartialKeySize = ^uint16(0) - -var ErrPartialKeyTooBig = errors.New("partial key length cannot be larger than or equal to 2^16") - -// KeyLength encodes the public key length. -func KeyLength(keyLength int, writer io.Writer) (err error) { - keyLength -= 63 - - if keyLength >= int(maxPartialKeySize) { - return fmt.Errorf("%w: %d", - ErrPartialKeyTooBig, keyLength) - } - - for i := uint16(0); i < maxPartialKeySize; i++ { - if keyLength < 255 { - _, err = writer.Write([]byte{byte(keyLength)}) - if err != nil { - return err - } - break - } - _, err = writer.Write([]byte{255}) - if err != nil { - return err - } - - keyLength -= 255 - } - - return nil -} +package codec // NibblesToKeyLE converts a slice of nibbles with length k into a // Little Endian byte slice. @@ -64,3 +26,22 @@ func NibblesToKeyLE(nibbles []byte) (keyLE []byte) { return keyLE } + +// KeyLEToNibbles converts a Little Endian byte slice into nibbles. +// It assumes bytes are already in Little Endian and does not rearrange nibbles. +func KeyLEToNibbles(in []byte) (nibbles []byte) { + if len(in) == 0 { + return []byte{} + } else if len(in) == 1 && in[0] == 0 { + return []byte{0, 0} + } + + l := len(in) * 2 + nibbles = make([]byte, l) + for i, b := range in { + nibbles[2*i] = b / 16 + nibbles[2*i+1] = b % 16 + } + + return nibbles +} diff --git a/lib/trie/codec/nibbles_test.go b/lib/trie/codec/nibbles_test.go new file mode 100644 index 00000000000..fa2bbf4fdd1 --- /dev/null +++ b/lib/trie/codec/nibbles_test.go @@ -0,0 +1,142 @@ +// Copyright 2021 ChainSafe Systems (ON) +// SPDX-License-Identifier: LGPL-3.0-only + +package codec + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func Test_NibblesToKeyLE(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + nibbles []byte + keyLE []byte + }{ + "nil nibbles": { + keyLE: []byte{}, + }, + "empty nibbles": { + nibbles: []byte{}, + keyLE: []byte{}, + }, + "0xF 0xF": { + nibbles: []byte{0xF, 0xF}, + keyLE: []byte{0xFF}, + }, + "0x3 0xa 0x0 0x5": { + nibbles: []byte{0x3, 0xa, 0x0, 0x5}, + keyLE: []byte{0x3a, 0x05}, + }, + "0xa 0xa 0xf 0xf 0x0 0x1": { + nibbles: []byte{0xa, 0xa, 0xf, 0xf, 0x0, 0x1}, + keyLE: []byte{0xaa, 0xff, 0x01}, + }, + "0xa 0xa 0xf 0xf 0x0 0x1 0xc 0x2": { + nibbles: []byte{0xa, 0xa, 0xf, 0xf, 0x0, 0x1, 0xc, 0x2}, + keyLE: []byte{0xaa, 0xff, 0x01, 0xc2}, + }, + "0xa 0xa 0xf 0xf 0x0 0x1 0xc": { + nibbles: []byte{0xa, 0xa, 0xf, 0xf, 0x0, 0x1, 0xc}, + keyLE: []byte{0xa, 0xaf, 0xf0, 0x1c}, + }, + } + + for name, testCase := range testCases { + testCase := testCase + t.Run(name, func(t *testing.T) { + t.Parallel() + + keyLE := NibblesToKeyLE(testCase.nibbles) + + assert.Equal(t, testCase.keyLE, keyLE) + }) + } +} + +func Test_KeyLEToNibbles(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + in []byte + nibbles []byte + }{ + "nil input": { + nibbles: []byte{}, + }, + "empty input": { + in: []byte{}, + nibbles: []byte{}, + }, + "0x0": { + in: []byte{0x0}, + nibbles: []byte{0, 0}}, + "0xFF": { + in: []byte{0xFF}, + nibbles: []byte{0xF, 0xF}}, + "0x3a 0x05": { + in: []byte{0x3a, 0x05}, + nibbles: []byte{0x3, 0xa, 0x0, 0x5}}, + "0xAA 0xFF 0x01": { + in: []byte{0xAA, 0xFF, 0x01}, + nibbles: []byte{0xa, 0xa, 0xf, 0xf, 0x0, 0x1}}, + "0xAA 0xFF 0x01 0xc2": { + in: []byte{0xAA, 0xFF, 0x01, 0xc2}, + nibbles: []byte{0xa, 0xa, 0xf, 0xf, 0x0, 0x1, 0xc, 0x2}}, + "0xAA 0xFF 0x01 0xc0": { + in: []byte{0xAA, 0xFF, 0x01, 0xc0}, + nibbles: []byte{0xa, 0xa, 0xf, 0xf, 0x0, 0x1, 0xc, 0x0}}, + } + + for name, testCase := range testCases { + testCase := testCase + t.Run(name, func(t *testing.T) { + t.Parallel() + + nibbles := KeyLEToNibbles(testCase.in) + + assert.Equal(t, testCase.nibbles, nibbles) + }) + } +} + +func Test_NibblesKeyLE(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + nibblesToEncode []byte + nibblesDecoded []byte + }{ + "empty input": { + nibblesToEncode: []byte{}, + nibblesDecoded: []byte{}, + }, + "one byte": { + nibblesToEncode: []byte{1}, + nibblesDecoded: []byte{0, 1}, + }, + "two bytes": { + nibblesToEncode: []byte{1, 2}, + nibblesDecoded: []byte{1, 2}, + }, + "three bytes": { + nibblesToEncode: []byte{1, 2, 3}, + nibblesDecoded: []byte{0, 1, 2, 3}, + }, + } + + for name, testCase := range testCases { + testCase := testCase + t.Run(name, func(t *testing.T) { + t.Parallel() + + keyLE := NibblesToKeyLE(testCase.nibblesToEncode) + nibblesDecoded := KeyLEToNibbles(keyLE) + + assert.Equal(t, testCase.nibblesDecoded, nibblesDecoded) + }) + } +} diff --git a/lib/trie/database.go b/lib/trie/database.go index 09c2a0c535b..834b707f2c9 100644 --- a/lib/trie/database.go +++ b/lib/trie/database.go @@ -9,9 +9,7 @@ import ( "fmt" "github.com/ChainSafe/gossamer/lib/common" - "github.com/ChainSafe/gossamer/lib/trie/branch" - "github.com/ChainSafe/gossamer/lib/trie/decode" - "github.com/ChainSafe/gossamer/lib/trie/leaf" + "github.com/ChainSafe/gossamer/lib/trie/codec" "github.com/ChainSafe/gossamer/lib/trie/node" "github.com/ChainSafe/chaindb" @@ -50,7 +48,7 @@ func (t *Trie) store(db chaindb.Batch, curr node.Node) error { return err } - if c, ok := curr.(*branch.Branch); ok { + if c, ok := curr.(*node.Branch); ok { for _, child := range c.Children { if child == nil { continue @@ -108,7 +106,7 @@ func (t *Trie) LoadFromProof(proof [][]byte, root []byte) error { // loadProof is a recursive function that will create all the trie paths based // on the mapped proofs slice starting by the root func (t *Trie) loadProof(proof map[string]node.Node, curr node.Node) { - c, ok := curr.(*branch.Branch) + c, ok := curr.(*node.Branch) if !ok { return } @@ -153,7 +151,7 @@ func (t *Trie) Load(db chaindb.Database, root common.Hash) error { } func (t *Trie) load(db chaindb.Database, curr node.Node) error { - if c, ok := curr.(*branch.Branch); ok { + if c, ok := curr.(*node.Branch); ok { for i, child := range c.Children { if child == nil { continue @@ -186,7 +184,7 @@ func (t *Trie) load(db chaindb.Database, curr node.Node) error { // GetNodeHashes return hash of each key of the trie. func (t *Trie) GetNodeHashes(curr node.Node, keys map[common.Hash]struct{}) error { - if c, ok := curr.(*branch.Branch); ok { + if c, ok := curr.(*node.Branch); ok { for _, child := range c.Children { if child == nil { continue @@ -238,7 +236,7 @@ func GetFromDB(db chaindb.Database, root common.Hash, key []byte) ([]byte, error return nil, nil } - k := decode.KeyLEToNibbles(key) + k := codec.KeyLEToNibbles(key) enc, err := db.Get(root[:]) if err != nil { @@ -257,7 +255,7 @@ func getFromDB(db chaindb.Database, parent node.Node, key []byte) ([]byte, error var value []byte switch p := parent.(type) { - case *branch.Branch: + case *node.Branch: length := lenCommonPrefix(p.Key, key) // found the value at this node @@ -275,7 +273,7 @@ func getFromDB(db chaindb.Database, parent node.Node, key []byte) ([]byte, error } // load child with potential value - enc, err := db.Get(p.Children[key[length]].(*leaf.Leaf).Hash) + enc, err := db.Get(p.Children[key[length]].(*node.Leaf).Hash) if err != nil { return nil, fmt.Errorf("failed to find node in database: %w", err) } @@ -289,7 +287,7 @@ func getFromDB(db chaindb.Database, parent node.Node, key []byte) ([]byte, error if err != nil { return nil, err } - case *leaf.Leaf: + case *node.Leaf: if bytes.Equal(p.Key, key) { return p.Value, nil } @@ -337,7 +335,7 @@ func (t *Trie) writeDirty(db chaindb.Batch, curr node.Node) error { return err } - if c, ok := curr.(*branch.Branch); ok { + if c, ok := curr.(*node.Branch); ok { for _, child := range c.Children { if child == nil { continue @@ -383,7 +381,7 @@ func (t *Trie) getInsertedNodeHashes(curr node.Node) ([]common.Hash, error) { nodeHash := common.BytesToHash(hash) nodeHashes = append(nodeHashes, nodeHash) - if c, ok := curr.(*branch.Branch); ok { + if c, ok := curr.(*node.Branch); ok { for _, child := range c.Children { if child == nil { continue diff --git a/lib/trie/decode.go b/lib/trie/decode.go index 46bf12d7518..c3305615c74 100644 --- a/lib/trie/decode.go +++ b/lib/trie/decode.go @@ -9,8 +9,6 @@ import ( "fmt" "io" - "github.com/ChainSafe/gossamer/lib/trie/branch" - "github.com/ChainSafe/gossamer/lib/trie/leaf" "github.com/ChainSafe/gossamer/lib/trie/node" "github.com/ChainSafe/gossamer/lib/trie/pools" ) @@ -33,13 +31,13 @@ func decodeNode(reader io.Reader) (n node.Node, err error) { nodeType := header >> 6 switch nodeType { case node.LeafType: - n, err = leaf.Decode(reader, header) + n, err = node.DecodeLeaf(reader, header) if err != nil { return nil, fmt.Errorf("cannot decode leaf: %w", err) } return n, nil case node.BranchType, node.BranchWithValueType: - n, err = branch.Decode(reader, header) + n, err = node.DecodeBranch(reader, header) if err != nil { return nil, fmt.Errorf("cannot decode branch: %w", err) } diff --git a/lib/trie/decode/key_test.go b/lib/trie/decode/key_test.go deleted file mode 100644 index c25749d7e84..00000000000 --- a/lib/trie/decode/key_test.go +++ /dev/null @@ -1,138 +0,0 @@ -// Copyright 2021 ChainSafe Systems (ON) -// SPDX-License-Identifier: LGPL-3.0-only - -package decode - -import ( - "bytes" - "io" - "testing" - - "github.com/stretchr/testify/assert" -) - -func repeatBytes(n int, b byte) (slice []byte) { - slice = make([]byte, n) - for i := range slice { - slice[i] = b - } - return slice -} - -func Test_Key(t *testing.T) { - t.Parallel() - - testCases := map[string]struct { - reader io.Reader - keyLength byte - b []byte - errWrapped error - errMessage string - }{ - "zero key length": { - b: []byte{}, - }, - "short key length": { - reader: bytes.NewBuffer([]byte{1, 2, 3}), - keyLength: 5, - b: []byte{0x1, 0x0, 0x2, 0x0, 0x3}, - }, - "key read error": { - reader: bytes.NewBuffer(nil), - keyLength: 5, - errWrapped: ErrReadKeyData, - errMessage: "cannot read key data: EOF", - }, - "long key length": { - reader: bytes.NewBuffer( - append( - []byte{ - 6, // key length - }, - repeatBytes(64, 7)..., // key data - )), - keyLength: 0x3f, - b: []byte{ - 0x7, 0x0, 0x7, 0x0, 0x7, 0x0, 0x7, 0x0, 0x7, 0x0, - 0x7, 0x0, 0x7, 0x0, 0x7, 0x0, 0x7, 0x0, 0x7, 0x0, - 0x7, 0x0, 0x7, 0x0, 0x7, 0x0, 0x7, 0x0, 0x7, 0x0, - 0x7, 0x0, 0x7, 0x0, 0x7, 0x0, 0x7, 0x0, 0x7, 0x0, - 0x7, 0x0, 0x7, 0x0, 0x7, 0x0, 0x7, 0x0, 0x7, 0x0, - 0x7, 0x0, 0x7, 0x0, 0x7, 0x0, 0x7, 0x0, 0x7, 0x0, - 0x7, 0x0, 0x7, 0x0, 0x7, 0x0, 0x7, 0x0, 0x7}, - }, - "key length read error": { - reader: bytes.NewBuffer(nil), - keyLength: 0x3f, - errWrapped: ErrReadKeyLength, - errMessage: "cannot read key length: EOF", - }, - "key length too big": { - reader: bytes.NewBuffer(repeatBytes(257, 0xff)), - keyLength: 0x3f, - errWrapped: ErrPartialKeyTooBig, - errMessage: "partial key length cannot be larger than or equal to 2^16: 65598", - }, - } - - for name, testCase := range testCases { - testCase := testCase - t.Run(name, func(t *testing.T) { - t.Parallel() - - b, err := Key(testCase.reader, testCase.keyLength) - - assert.ErrorIs(t, err, testCase.errWrapped) - if err != nil { - assert.EqualError(t, err, testCase.errMessage) - } - assert.Equal(t, testCase.b, b) - }) - } -} - -func Test_KeyToNibbles(t *testing.T) { - t.Parallel() - - testCases := map[string]struct { - in []byte - nibbles []byte - }{ - "nil input": { - nibbles: []byte{}, - }, - "empty input": { - in: []byte{}, - nibbles: []byte{}, - }, - "0x0": { - in: []byte{0x0}, - nibbles: []byte{0, 0}}, - "0xFF": { - in: []byte{0xFF}, - nibbles: []byte{0xF, 0xF}}, - "0x3a 0x05": { - in: []byte{0x3a, 0x05}, - nibbles: []byte{0x3, 0xa, 0x0, 0x5}}, - "0xAA 0xFF 0x01": { - in: []byte{0xAA, 0xFF, 0x01}, - nibbles: []byte{0xa, 0xa, 0xf, 0xf, 0x0, 0x1}}, - "0xAA 0xFF 0x01 0xc2": { - in: []byte{0xAA, 0xFF, 0x01, 0xc2}, - nibbles: []byte{0xa, 0xa, 0xf, 0xf, 0x0, 0x1, 0xc, 0x2}}, - "0xAA 0xFF 0x01 0xc0": { - in: []byte{0xAA, 0xFF, 0x01, 0xc0}, - nibbles: []byte{0xa, 0xa, 0xf, 0xf, 0x0, 0x1, 0xc, 0x0}}, - } - - for name, testCase := range testCases { - testCase := testCase - t.Run(name, func(t *testing.T) { - t.Parallel() - - nibbles := KeyLEToNibbles(testCase.in) - - assert.Equal(t, testCase.nibbles, nibbles) - }) - } -} diff --git a/lib/trie/decode_test.go b/lib/trie/decode_test.go index 32002d4f249..a4c1b4c0683 100644 --- a/lib/trie/decode_test.go +++ b/lib/trie/decode_test.go @@ -8,9 +8,6 @@ import ( "io" "testing" - "github.com/ChainSafe/gossamer/lib/trie/branch" - "github.com/ChainSafe/gossamer/lib/trie/decode" - "github.com/ChainSafe/gossamer/lib/trie/leaf" "github.com/ChainSafe/gossamer/lib/trie/node" "github.com/ChainSafe/gossamer/pkg/scale" "github.com/stretchr/testify/assert" @@ -47,7 +44,7 @@ func Test_decodeNode(t *testing.T) { 65, // node type 1 and key length 1 // missing key data byte }), - errWrapped: decode.ErrReadKeyData, + errWrapped: node.ErrReadKeyData, errMessage: "cannot decode leaf: cannot decode key: cannot read key data: EOF", }, "leaf success": { @@ -60,7 +57,7 @@ func Test_decodeNode(t *testing.T) { scaleEncodeBytes(t, 1, 2, 3)..., ), ), - n: &leaf.Leaf{ + n: &node.Leaf{ Key: []byte{9}, Value: []byte{1, 2, 3}, Dirty: true, @@ -71,7 +68,7 @@ func Test_decodeNode(t *testing.T) { 129, // node type 2 and key length 1 // missing key data byte }), - errWrapped: decode.ErrReadKeyData, + errWrapped: node.ErrReadKeyData, errMessage: "cannot decode branch: cannot decode key: cannot read key data: EOF", }, "branch success": { @@ -82,7 +79,7 @@ func Test_decodeNode(t *testing.T) { 0, 0, // no children bitmap }, ), - n: &branch.Branch{ + n: &node.Branch{ Key: []byte{9}, Dirty: true, }, diff --git a/lib/trie/encode/writer_mock_test.go b/lib/trie/encode/writer_mock_test.go deleted file mode 100644 index 171dc73964d..00000000000 --- a/lib/trie/encode/writer_mock_test.go +++ /dev/null @@ -1,49 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: io (interfaces: Writer) - -// Package encode is a generated GoMock package. -package encode - -import ( - reflect "reflect" - - gomock "github.com/golang/mock/gomock" -) - -// MockWriter is a mock of Writer interface. -type MockWriter struct { - ctrl *gomock.Controller - recorder *MockWriterMockRecorder -} - -// MockWriterMockRecorder is the mock recorder for MockWriter. -type MockWriterMockRecorder struct { - mock *MockWriter -} - -// NewMockWriter creates a new mock instance. -func NewMockWriter(ctrl *gomock.Controller) *MockWriter { - mock := &MockWriter{ctrl: ctrl} - mock.recorder = &MockWriterMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockWriter) EXPECT() *MockWriterMockRecorder { - return m.recorder -} - -// Write mocks base method. -func (m *MockWriter) Write(arg0 []byte) (int, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Write", arg0) - ret0, _ := ret[0].(int) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Write indicates an expected call of Write. -func (mr *MockWriterMockRecorder) Write(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Write", reflect.TypeOf((*MockWriter)(nil).Write), arg0) -} diff --git a/lib/trie/encodedecode_test/nibbles_test.go b/lib/trie/encodedecode_test/nibbles_test.go deleted file mode 100644 index 05fd0b5a95d..00000000000 --- a/lib/trie/encodedecode_test/nibbles_test.go +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2021 ChainSafe Systems (ON) -// SPDX-License-Identifier: LGPL-3.0-only - -package encodedecode_test - -import ( - "testing" - - "github.com/ChainSafe/gossamer/lib/trie/decode" - "github.com/ChainSafe/gossamer/lib/trie/encode" - "github.com/stretchr/testify/assert" -) - -func Test_NibblesKeyLE(t *testing.T) { - t.Parallel() - - testCases := map[string]struct { - nibblesToEncode []byte - nibblesDecoded []byte - }{ - "empty input": { - nibblesToEncode: []byte{}, - nibblesDecoded: []byte{}, - }, - "one byte": { - nibblesToEncode: []byte{1}, - nibblesDecoded: []byte{0, 1}, - }, - "two bytes": { - nibblesToEncode: []byte{1, 2}, - nibblesDecoded: []byte{1, 2}, - }, - "three bytes": { - nibblesToEncode: []byte{1, 2, 3}, - nibblesDecoded: []byte{0, 1, 2, 3}, - }, - } - - for name, testCase := range testCases { - testCase := testCase - t.Run(name, func(t *testing.T) { - t.Parallel() - - keyLE := encode.NibblesToKeyLE(testCase.nibblesToEncode) - nibblesDecoded := decode.KeyLEToNibbles(keyLE) - - assert.Equal(t, testCase.nibblesDecoded, nibblesDecoded) - }) - } -} diff --git a/lib/trie/leaf/copy.go b/lib/trie/leaf/copy.go deleted file mode 100644 index 3f079722495..00000000000 --- a/lib/trie/leaf/copy.go +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2021 ChainSafe Systems (ON) -// SPDX-License-Identifier: LGPL-3.0-only - -package leaf - -import "github.com/ChainSafe/gossamer/lib/trie/node" - -// Copy deep copies the leaf. -func (l *Leaf) Copy() node.Node { - l.RLock() - defer l.RUnlock() - - l.encodingMu.RLock() - defer l.encodingMu.RUnlock() - - cpy := &Leaf{ - Key: make([]byte, len(l.Key)), - Value: make([]byte, len(l.Value)), - Dirty: l.Dirty, - Hash: make([]byte, len(l.Hash)), - Encoding: make([]byte, len(l.Encoding)), - Generation: l.Generation, - } - copy(cpy.Key, l.Key) - copy(cpy.Value, l.Value) - copy(cpy.Hash, l.Hash) - copy(cpy.Encoding, l.Encoding) - return cpy -} diff --git a/lib/trie/leaf/decode.go b/lib/trie/leaf/decode.go deleted file mode 100644 index 6c20fffbccf..00000000000 --- a/lib/trie/leaf/decode.go +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2021 ChainSafe Systems (ON) -// SPDX-License-Identifier: LGPL-3.0-only - -package leaf - -import ( - "errors" - "fmt" - "io" - - "github.com/ChainSafe/gossamer/lib/trie/decode" - "github.com/ChainSafe/gossamer/pkg/scale" -) - -var ( - ErrReadHeaderByte = errors.New("cannot read header byte") - ErrNodeTypeIsNotALeaf = errors.New("node type is not a leaf") - ErrDecodeValue = errors.New("cannot decode value") -) - -// Decode reads and decodes from a reader with the encoding specified in lib/trie/encode/doc.go. -func Decode(r io.Reader, header byte) (leaf *Leaf, err error) { - nodeType := header >> 6 - if nodeType != 1 { - return nil, fmt.Errorf("%w: %d", ErrNodeTypeIsNotALeaf, nodeType) - } - - leaf = new(Leaf) - - keyLen := header & 0x3f - leaf.Key, err = decode.Key(r, keyLen) - if err != nil { - return nil, fmt.Errorf("cannot decode key: %w", err) - } - - sd := scale.NewDecoder(r) - var value []byte - err = sd.Decode(&value) - if err != nil { - return nil, fmt.Errorf("%w: %s", ErrDecodeValue, err) - } - - if len(value) > 0 { - leaf.Value = value - } - - leaf.Dirty = true - - return leaf, nil -} diff --git a/lib/trie/leaf/decode_test.go b/lib/trie/leaf/decode_test.go deleted file mode 100644 index e702b666e24..00000000000 --- a/lib/trie/leaf/decode_test.go +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright 2021 ChainSafe Systems (ON) -// SPDX-License-Identifier: LGPL-3.0-only - -package leaf - -import ( - "bytes" - "io" - "testing" - - "github.com/ChainSafe/gossamer/lib/trie/decode" - "github.com/ChainSafe/gossamer/pkg/scale" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func scaleEncodeBytes(t *testing.T, b ...byte) (encoded []byte) { - encoded, err := scale.Marshal(b) - require.NoError(t, err) - return encoded -} - -func concatByteSlices(slices [][]byte) (concatenated []byte) { - length := 0 - for i := range slices { - length += len(slices[i]) - } - concatenated = make([]byte, 0, length) - for _, slice := range slices { - concatenated = append(concatenated, slice...) - } - return concatenated -} - -func Test_Decode(t *testing.T) { - t.Parallel() - - testCases := map[string]struct { - reader io.Reader - header byte - leaf *Leaf - 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 - }), - header: 65, // node type 1 and key length 1 - errWrapped: decode.ErrReadKeyData, - errMessage: "cannot decode key: cannot read key data: EOF", - }, - "value decoding error": { - reader: bytes.NewBuffer([]byte{ - 9, // key data - // missing value data - }), - header: 65, // node type 1 and key length 1 - errWrapped: ErrDecodeValue, - errMessage: "cannot decode value: EOF", - }, - "zero value": { - reader: bytes.NewBuffer([]byte{ - 9, // key data - 0, // missing value data - }), - header: 65, // node type 1 and key length 1 - leaf: &Leaf{ - Key: []byte{9}, - Dirty: true, - }, - }, - "success": { - reader: bytes.NewBuffer( - concatByteSlices([][]byte{ - {9}, // key data - scaleEncodeBytes(t, 1, 2, 3, 4, 5), // value data - }), - ), - header: 65, // node type 1 and key length 1 - leaf: &Leaf{ - Key: []byte{9}, - Value: []byte{1, 2, 3, 4, 5}, - Dirty: true, - }, - }, - } - - for name, testCase := range testCases { - testCase := testCase - t.Run(name, func(t *testing.T) { - t.Parallel() - - leaf, err := Decode(testCase.reader, testCase.header) - - assert.ErrorIs(t, err, testCase.errWrapped) - if err != nil { - assert.EqualError(t, err, testCase.errMessage) - } - assert.Equal(t, testCase.leaf, leaf) - }) - } -} diff --git a/lib/trie/leaf/dirty.go b/lib/trie/leaf/dirty.go deleted file mode 100644 index b955754b038..00000000000 --- a/lib/trie/leaf/dirty.go +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2021 ChainSafe Systems (ON) -// SPDX-License-Identifier: LGPL-3.0-only - -package leaf - -// 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 -} diff --git a/lib/trie/leaf/generation.go b/lib/trie/leaf/generation.go deleted file mode 100644 index 1ce46bf81d2..00000000000 --- a/lib/trie/leaf/generation.go +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2021 ChainSafe Systems (ON) -// SPDX-License-Identifier: LGPL-3.0-only - -package leaf - -// 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() uint64 { - return l.Generation -} diff --git a/lib/trie/leaf/header.go b/lib/trie/leaf/header.go deleted file mode 100644 index 930658f65f9..00000000000 --- a/lib/trie/leaf/header.go +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2021 ChainSafe Systems (ON) -// SPDX-License-Identifier: LGPL-3.0-only - -package leaf - -import ( - "io" - - "github.com/ChainSafe/gossamer/lib/trie/encode" -) - -// encodeHeader creates the encoded header for the leaf. -func (l *Leaf) encodeHeader(writer io.Writer) (err error) { - var header byte = 1 << 6 - - if len(l.Key) < 63 { - header = header | byte(len(l.Key)) - _, err = writer.Write([]byte{header}) - return err - } - - header = header | 0x3f - _, err = writer.Write([]byte{header}) - if err != nil { - return err - } - - err = encode.KeyLength(len(l.Key), writer) - if err != nil { - return err - } - - return nil -} diff --git a/lib/trie/leaf/header_test.go b/lib/trie/leaf/header_test.go deleted file mode 100644 index 08f03d0ed9f..00000000000 --- a/lib/trie/leaf/header_test.go +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright 2021 ChainSafe Systems (ON) -// SPDX-License-Identifier: LGPL-3.0-only - -package leaf - -import ( - "testing" - - "github.com/ChainSafe/gossamer/lib/trie/encode" - "github.com/golang/mock/gomock" - "github.com/stretchr/testify/assert" -) - -func Test_Leaf_encodeHeader(t *testing.T) { - testCases := map[string]struct { - leaf *Leaf - writes []writeCall - errWrapped error - errMessage string - }{ - "no key": { - leaf: &Leaf{}, - writes: []writeCall{ - {written: []byte{0x40}}, - }, - }, - "key of length 30": { - leaf: &Leaf{ - Key: make([]byte, 30), - }, - writes: []writeCall{ - {written: []byte{0x5e}}, - }, - }, - "short key write error": { - leaf: &Leaf{ - Key: make([]byte, 30), - }, - writes: []writeCall{ - { - written: []byte{0x5e}, - err: errTest, - }, - }, - errWrapped: errTest, - errMessage: errTest.Error(), - }, - "key of length 62": { - leaf: &Leaf{ - Key: make([]byte, 62), - }, - writes: []writeCall{ - {written: []byte{0x7e}}, - }, - }, - "key of length 63": { - leaf: &Leaf{ - Key: make([]byte, 63), - }, - writes: []writeCall{ - {written: []byte{0x7f}}, - {written: []byte{0x0}}, - }, - }, - "key of length 64": { - leaf: &Leaf{ - Key: make([]byte, 64), - }, - writes: []writeCall{ - {written: []byte{0x7f}}, - {written: []byte{0x1}}, - }, - }, - "long key first byte write error": { - leaf: &Leaf{ - Key: make([]byte, 63), - }, - writes: []writeCall{ - { - written: []byte{0x7f}, - err: errTest, - }, - }, - errWrapped: errTest, - errMessage: errTest.Error(), - }, - "key too big": { - leaf: &Leaf{ - Key: make([]byte, 65535+63), - }, - writes: []writeCall{ - {written: []byte{0x7f}}, - }, - errWrapped: encode.ErrPartialKeyTooBig, - errMessage: "partial key length cannot be larger than or equal to 2^16: 65535", - }, - } - - 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.leaf.encodeHeader(writer) - - assert.ErrorIs(t, err, testCase.errWrapped) - if testCase.errWrapped != nil { - assert.EqualError(t, err, testCase.errMessage) - } - }) - } -} diff --git a/lib/trie/leaf/key.go b/lib/trie/leaf/key.go deleted file mode 100644 index 9a7d3a11d6a..00000000000 --- a/lib/trie/leaf/key.go +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright 2021 ChainSafe Systems (ON) -// SPDX-License-Identifier: LGPL-3.0-only - -package leaf - -// 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 -} diff --git a/lib/trie/lookup.go b/lib/trie/lookup.go index e51596dc69c..c0680a88fc1 100644 --- a/lib/trie/lookup.go +++ b/lib/trie/lookup.go @@ -6,7 +6,6 @@ package trie import ( "bytes" - "github.com/ChainSafe/gossamer/lib/trie/branch" "github.com/ChainSafe/gossamer/lib/trie/node" "github.com/ChainSafe/gossamer/lib/trie/record" ) @@ -30,7 +29,7 @@ func find(parent node.Node, key []byte, recorder recorder) error { recorder.Record(hash, enc) - b, ok := parent.(*branch.Branch) + b, ok := parent.(*node.Branch) if !ok { return nil } diff --git a/lib/trie/branch/branch.go b/lib/trie/node/branch.go similarity index 85% rename from lib/trie/branch/branch.go rename to lib/trie/node/branch.go index a5d0dfc36d6..45547d1b3b9 100644 --- a/lib/trie/branch/branch.go +++ b/lib/trie/node/branch.go @@ -1,22 +1,21 @@ // Copyright 2021 ChainSafe Systems (ON) // SPDX-License-Identifier: LGPL-3.0-only -package branch +package node import ( "fmt" "sync" "github.com/ChainSafe/gossamer/lib/common" - "github.com/ChainSafe/gossamer/lib/trie/node" ) -var _ node.Node = (*Branch)(nil) +var _ Node = (*Branch)(nil) // Branch is a branch in the trie. type Branch struct { Key []byte // partial key - Children [16]node.Node + Children [16]Node Value []byte Dirty bool Hash []byte diff --git a/lib/trie/branch/encode.go b/lib/trie/node/branch_encode.go similarity index 90% rename from lib/trie/branch/encode.go rename to lib/trie/node/branch_encode.go index 37d49f74cde..e24fdd72617 100644 --- a/lib/trie/branch/encode.go +++ b/lib/trie/node/branch_encode.go @@ -1,7 +1,7 @@ // Copyright 2021 ChainSafe Systems (ON) // SPDX-License-Identifier: LGPL-3.0-only -package branch +package node import ( "bytes" @@ -10,9 +10,7 @@ import ( "io" "github.com/ChainSafe/gossamer/lib/common" - "github.com/ChainSafe/gossamer/lib/trie/encode" - "github.com/ChainSafe/gossamer/lib/trie/leaf" - "github.com/ChainSafe/gossamer/lib/trie/node" + "github.com/ChainSafe/gossamer/lib/trie/codec" "github.com/ChainSafe/gossamer/lib/trie/pools" "github.com/ChainSafe/gossamer/pkg/scale" ) @@ -77,7 +75,7 @@ func (b *Branch) hash(digestBuffer io.Writer) (err error) { // Encode encodes a branch with the encoding specified at the top of this package // to the buffer given. -func (b *Branch) Encode(buffer encode.Buffer) (err error) { +func (b *Branch) Encode(buffer Buffer) (err error) { if !b.Dirty && b.Encoding != nil { _, err = buffer.Write(b.Encoding) if err != nil { @@ -91,7 +89,7 @@ func (b *Branch) Encode(buffer encode.Buffer) (err error) { return fmt.Errorf("cannot encode header: %w", err) } - keyLE := encode.NibblesToKeyLE(b.Key) + keyLE := codec.NibblesToKeyLE(b.Key) _, err = buffer.Write(keyLE) if err != nil { return fmt.Errorf("cannot write encoded key to buffer: %w", err) @@ -128,7 +126,7 @@ func (b *Branch) Encode(buffer encode.Buffer) (err error) { return nil } -func encodeChildrenInParallel(children [16]node.Node, buffer io.Writer) (err error) { +func encodeChildrenInParallel(children [16]Node, buffer io.Writer) (err error) { type result struct { index int buffer *bytes.Buffer @@ -138,7 +136,7 @@ func encodeChildrenInParallel(children [16]node.Node, buffer io.Writer) (err err resultsCh := make(chan result) for i, child := range children { - go func(index int, child node.Node) { + go func(index int, child Node) { buffer := pools.EncodingBuffers.Get().(*bytes.Buffer) buffer.Reset() // buffer is put back in the pool after processing its @@ -195,7 +193,7 @@ func encodeChildrenInParallel(children [16]node.Node, buffer io.Writer) (err err return err } -func encodeChildrenSequentially(children [16]node.Node, buffer io.Writer) (err error) { +func encodeChildrenSequentially(children [16]Node, buffer io.Writer) (err error) { for i, child := range children { err = encodeChild(child, buffer) if err != nil { @@ -205,12 +203,12 @@ func encodeChildrenSequentially(children [16]node.Node, buffer io.Writer) (err e return nil } -func encodeChild(child node.Node, buffer io.Writer) (err error) { +func encodeChild(child Node, buffer io.Writer) (err error) { var isNil bool switch impl := child.(type) { case *Branch: isNil = impl == nil - case *leaf.Leaf: + case *Leaf: isNil = impl == nil default: isNil = child == nil diff --git a/lib/trie/branch/encode_test.go b/lib/trie/node/branch_encode_test.go similarity index 86% rename from lib/trie/branch/encode_test.go rename to lib/trie/node/branch_encode_test.go index 3de07d6f5ce..2589e37e0f5 100644 --- a/lib/trie/branch/encode_test.go +++ b/lib/trie/node/branch_encode_test.go @@ -1,28 +1,16 @@ // Copyright 2021 ChainSafe Systems (ON) // SPDX-License-Identifier: LGPL-3.0-only -package branch +package node import ( - "errors" "testing" - "github.com/ChainSafe/gossamer/lib/trie/encode" - "github.com/ChainSafe/gossamer/lib/trie/leaf" - "github.com/ChainSafe/gossamer/lib/trie/node" "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) -type writeCall struct { - written []byte - n int - err error -} - -var errTest = errors.New("test error") - //go:generate mockgen -destination=buffer_mock_test.go -package $GOPACKAGE github.com/ChainSafe/gossamer/lib/trie/encode Buffer func Test_Branch_Encode(t *testing.T) { @@ -66,7 +54,7 @@ func Test_Branch_Encode(t *testing.T) { written: []byte{191}, }, }, - wrappedErr: encode.ErrPartialKeyTooBig, + 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": { @@ -90,9 +78,9 @@ func Test_Branch_Encode(t *testing.T) { branch: &Branch{ Key: []byte{1, 2, 3}, Value: []byte{100}, - Children: [16]node.Node{ - nil, nil, nil, &leaf.Leaf{Key: []byte{9}}, - nil, nil, nil, &leaf.Leaf{Key: []byte{11}}, + Children: [16]Node{ + nil, nil, nil, &Leaf{Key: []byte{9}}, + nil, nil, nil, &Leaf{Key: []byte{11}}, }, }, writes: []writeCall{ @@ -114,9 +102,9 @@ func Test_Branch_Encode(t *testing.T) { branch: &Branch{ Key: []byte{1, 2, 3}, Value: []byte{100}, - Children: [16]node.Node{ - nil, nil, nil, &leaf.Leaf{Key: []byte{9}}, - nil, nil, nil, &leaf.Leaf{Key: []byte{11}}, + Children: [16]Node{ + nil, nil, nil, &Leaf{Key: []byte{9}}, + nil, nil, nil, &Leaf{Key: []byte{11}}, }, }, writes: []writeCall{ @@ -141,9 +129,9 @@ func Test_Branch_Encode(t *testing.T) { branch: &Branch{ Key: []byte{1, 2, 3}, Value: []byte{100}, - Children: [16]node.Node{ - nil, nil, nil, &leaf.Leaf{Key: []byte{9}}, - nil, nil, nil, &leaf.Leaf{Key: []byte{11}}, + Children: [16]Node{ + nil, nil, nil, &Leaf{Key: []byte{9}}, + nil, nil, nil, &Leaf{Key: []byte{11}}, }, }, writes: []writeCall{ @@ -173,9 +161,9 @@ func Test_Branch_Encode(t *testing.T) { branch: &Branch{ Key: []byte{1, 2, 3}, Value: []byte{100}, - Children: [16]node.Node{ - nil, nil, nil, &leaf.Leaf{Key: []byte{9}}, - nil, nil, nil, &leaf.Leaf{Key: []byte{11}}, + Children: [16]Node{ + nil, nil, nil, &Leaf{Key: []byte{9}}, + nil, nil, nil, &Leaf{Key: []byte{11}}, }, }, writes: []writeCall{ @@ -236,15 +224,15 @@ func Test_encodeChildrenInParallel(t *testing.T) { t.Parallel() testCases := map[string]struct { - children [16]node.Node + children [16]Node writes []writeCall wrappedErr error errMessage string }{ "no children": {}, "first child not nil": { - children: [16]node.Node{ - &leaf.Leaf{Key: []byte{1}}, + children: [16]Node{ + &Leaf{Key: []byte{1}}, }, writes: []writeCall{ { @@ -253,11 +241,11 @@ func Test_encodeChildrenInParallel(t *testing.T) { }, }, "last child not nil": { - children: [16]node.Node{ + children: [16]Node{ nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - &leaf.Leaf{Key: []byte{1}}, + &Leaf{Key: []byte{1}}, }, writes: []writeCall{ { @@ -266,9 +254,9 @@ func Test_encodeChildrenInParallel(t *testing.T) { }, }, "first two children not nil": { - children: [16]node.Node{ - &leaf.Leaf{Key: []byte{1}}, - &leaf.Leaf{Key: []byte{2}}, + children: [16]Node{ + &Leaf{Key: []byte{1}}, + &Leaf{Key: []byte{2}}, }, writes: []writeCall{ { @@ -280,11 +268,11 @@ func Test_encodeChildrenInParallel(t *testing.T) { }, }, "encoding error": { - children: [16]node.Node{ + children: [16]Node{ nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - &leaf.Leaf{ + &Leaf{ Key: []byte{1}, }, nil, nil, nil, nil, @@ -336,15 +324,15 @@ func Test_encodeChildrenSequentially(t *testing.T) { t.Parallel() testCases := map[string]struct { - children [16]node.Node + children [16]Node writes []writeCall wrappedErr error errMessage string }{ "no children": {}, "first child not nil": { - children: [16]node.Node{ - &leaf.Leaf{Key: []byte{1}}, + children: [16]Node{ + &Leaf{Key: []byte{1}}, }, writes: []writeCall{ { @@ -353,11 +341,11 @@ func Test_encodeChildrenSequentially(t *testing.T) { }, }, "last child not nil": { - children: [16]node.Node{ + children: [16]Node{ nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - &leaf.Leaf{Key: []byte{1}}, + &Leaf{Key: []byte{1}}, }, writes: []writeCall{ { @@ -366,9 +354,9 @@ func Test_encodeChildrenSequentially(t *testing.T) { }, }, "first two children not nil": { - children: [16]node.Node{ - &leaf.Leaf{Key: []byte{1}}, - &leaf.Leaf{Key: []byte{2}}, + children: [16]Node{ + &Leaf{Key: []byte{1}}, + &Leaf{Key: []byte{2}}, }, writes: []writeCall{ { @@ -380,11 +368,11 @@ func Test_encodeChildrenSequentially(t *testing.T) { }, }, "encoding error": { - children: [16]node.Node{ + children: [16]Node{ nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - &leaf.Leaf{ + &Leaf{ Key: []byte{1}, }, nil, nil, nil, nil, @@ -438,7 +426,7 @@ func Test_encodeChild(t *testing.T) { t.Parallel() testCases := map[string]struct { - child node.Node + child Node writeCall bool write writeCall wrappedErr error @@ -446,13 +434,13 @@ func Test_encodeChild(t *testing.T) { }{ "nil node": {}, "nil leaf": { - child: (*leaf.Leaf)(nil), + child: (*Leaf)(nil), }, "nil branch": { child: (*Branch)(nil), }, "empty leaf child": { - child: &leaf.Leaf{}, + child: &Leaf{}, writeCall: true, write: writeCall{ written: []byte{8, 64, 0}, @@ -476,7 +464,7 @@ func Test_encodeChild(t *testing.T) { errMessage: "failed to write child to buffer: test error", }, "leaf child": { - child: &leaf.Leaf{ + child: &Leaf{ Key: []byte{1}, Value: []byte{2}, }, @@ -489,8 +477,8 @@ func Test_encodeChild(t *testing.T) { child: &Branch{ Key: []byte{1}, Value: []byte{2}, - Children: [16]node.Node{ - nil, nil, &leaf.Leaf{ + Children: [16]Node{ + nil, nil, &Leaf{ Key: []byte{5}, Value: []byte{6}, }, diff --git a/lib/trie/encode/buffer.go b/lib/trie/node/buffer.go similarity index 94% rename from lib/trie/encode/buffer.go rename to lib/trie/node/buffer.go index 748f30ed970..c1515f94c6e 100644 --- a/lib/trie/encode/buffer.go +++ b/lib/trie/node/buffer.go @@ -1,7 +1,7 @@ // Copyright 2021 ChainSafe Systems (ON) // SPDX-License-Identifier: LGPL-3.0-only -package encode +package node import "io" diff --git a/lib/trie/leaf/buffer_mock_test.go b/lib/trie/node/buffer_mock_test.go similarity index 97% rename from lib/trie/leaf/buffer_mock_test.go rename to lib/trie/node/buffer_mock_test.go index d3404d0a480..1357336f236 100644 --- a/lib/trie/leaf/buffer_mock_test.go +++ b/lib/trie/node/buffer_mock_test.go @@ -1,8 +1,8 @@ // Code generated by MockGen. DO NOT EDIT. // Source: github.com/ChainSafe/gossamer/lib/trie/encode (interfaces: Buffer) -// Package leaf is a generated GoMock package. -package leaf +// Package node is a generated GoMock package. +package node import ( reflect "reflect" diff --git a/lib/trie/branch/children.go b/lib/trie/node/children.go similarity index 97% rename from lib/trie/branch/children.go rename to lib/trie/node/children.go index ff911dc5138..bd581cf6570 100644 --- a/lib/trie/branch/children.go +++ b/lib/trie/node/children.go @@ -1,7 +1,7 @@ // Copyright 2021 ChainSafe Systems (ON) // SPDX-License-Identifier: LGPL-3.0-only -package branch +package node // ChildrenBitmap returns the 16 bit bitmap // of the children in the branch. diff --git a/lib/trie/branch/children_test.go b/lib/trie/node/children_test.go similarity index 75% rename from lib/trie/branch/children_test.go rename to lib/trie/node/children_test.go index a525a577695..4b60039656c 100644 --- a/lib/trie/branch/children_test.go +++ b/lib/trie/node/children_test.go @@ -1,13 +1,11 @@ // Copyright 2021 ChainSafe Systems (ON) // SPDX-License-Identifier: LGPL-3.0-only -package branch +package node import ( "testing" - "github.com/ChainSafe/gossamer/lib/trie/leaf" - "github.com/ChainSafe/gossamer/lib/trie/node" "github.com/stretchr/testify/assert" ) @@ -23,31 +21,31 @@ func Test_Branch_ChildrenBitmap(t *testing.T) { }, "index 0": { branch: &Branch{ - Children: [16]node.Node{ - &leaf.Leaf{}, + Children: [16]Node{ + &Leaf{}, }, }, bitmap: 1, }, "index 0 and 4": { branch: &Branch{ - Children: [16]node.Node{ - &leaf.Leaf{}, + Children: [16]Node{ + &Leaf{}, nil, nil, nil, - &leaf.Leaf{}, + &Leaf{}, }, }, bitmap: 1<<4 + 1, }, "index 0, 4 and 15": { branch: &Branch{ - Children: [16]node.Node{ - &leaf.Leaf{}, + Children: [16]Node{ + &Leaf{}, nil, nil, nil, - &leaf.Leaf{}, + &Leaf{}, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - &leaf.Leaf{}, + &Leaf{}, }, }, bitmap: 1<<15 + 1<<4 + 1, @@ -78,31 +76,31 @@ func Test_Branch_NumChildren(t *testing.T) { }, "one": { branch: &Branch{ - Children: [16]node.Node{ - &leaf.Leaf{}, + Children: [16]Node{ + &Leaf{}, }, }, count: 1, }, "two": { branch: &Branch{ - Children: [16]node.Node{ - &leaf.Leaf{}, + Children: [16]Node{ + &Leaf{}, nil, nil, nil, - &leaf.Leaf{}, + &Leaf{}, }, }, count: 2, }, "three": { branch: &Branch{ - Children: [16]node.Node{ - &leaf.Leaf{}, + Children: [16]Node{ + &Leaf{}, nil, nil, nil, - &leaf.Leaf{}, + &Leaf{}, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - &leaf.Leaf{}, + &Leaf{}, }, }, count: 3, diff --git a/lib/trie/branch/copy.go b/lib/trie/node/copy.go similarity index 54% rename from lib/trie/branch/copy.go rename to lib/trie/node/copy.go index 6e7d78b6b36..41f1b068d1c 100644 --- a/lib/trie/branch/copy.go +++ b/lib/trie/node/copy.go @@ -1,12 +1,10 @@ // Copyright 2021 ChainSafe Systems (ON) // SPDX-License-Identifier: LGPL-3.0-only -package branch - -import "github.com/ChainSafe/gossamer/lib/trie/node" +package node // Copy deep copies the branch. -func (b *Branch) Copy() node.Node { +func (b *Branch) Copy() Node { b.RLock() defer b.RUnlock() @@ -31,3 +29,26 @@ func (b *Branch) Copy() node.Node { copy(cpy.Encoding, b.Encoding) return cpy } + +// Copy deep copies the leaf. +func (l *Leaf) Copy() Node { + l.RLock() + defer l.RUnlock() + + l.encodingMu.RLock() + defer l.encodingMu.RUnlock() + + cpy := &Leaf{ + Key: make([]byte, len(l.Key)), + Value: make([]byte, len(l.Value)), + Dirty: l.Dirty, + Hash: make([]byte, len(l.Hash)), + Encoding: make([]byte, len(l.Encoding)), + Generation: l.Generation, + } + copy(cpy.Key, l.Key) + copy(cpy.Value, l.Value) + copy(cpy.Hash, l.Hash) + copy(cpy.Encoding, l.Encoding) + return cpy +} diff --git a/lib/trie/branch/decode.go b/lib/trie/node/decode.go similarity index 63% rename from lib/trie/branch/decode.go rename to lib/trie/node/decode.go index 118f0713866..a640196c4fa 100644 --- a/lib/trie/branch/decode.go +++ b/lib/trie/node/decode.go @@ -1,32 +1,31 @@ // Copyright 2021 ChainSafe Systems (ON) // SPDX-License-Identifier: LGPL-3.0-only -package branch +package node import ( "errors" "fmt" "io" - "github.com/ChainSafe/gossamer/lib/trie/decode" - "github.com/ChainSafe/gossamer/lib/trie/leaf" "github.com/ChainSafe/gossamer/pkg/scale" ) var ( ErrReadHeaderByte = errors.New("cannot read header byte") ErrNodeTypeIsNotABranch = errors.New("node type is not a branch") - ErrReadChildrenBitmap = errors.New("cannot read children bitmap") + 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") ) -// Decode reads and decodes from a reader with the encoding specified in lib/trie/encode/doc.go. +// DecodeBranch reads and decodes from a reader with the encoding specified in lib/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 Decode(reader io.Reader, header byte) (branch *Branch, err error) { +func DecodeBranch(reader io.Reader, header byte) (branch *Branch, err error) { nodeType := header >> 6 if nodeType != 2 && nodeType != 3 { return nil, fmt.Errorf("%w: %d", ErrNodeTypeIsNotABranch, nodeType) @@ -35,7 +34,7 @@ func Decode(reader io.Reader, header byte) (branch *Branch, err error) { branch = new(Branch) keyLen := header & 0x3f - branch.Key, err = decode.Key(reader, keyLen) + branch.Key, err = decodeKey(reader, keyLen) if err != nil { return nil, fmt.Errorf("cannot decode key: %w", err) } @@ -69,7 +68,7 @@ func Decode(reader io.Reader, header byte) (branch *Branch, err error) { ErrDecodeChildHash, i, err) } - branch.Children[i] = &leaf.Leaf{ + branch.Children[i] = &Leaf{ Hash: hash, } } @@ -78,3 +77,34 @@ func Decode(reader io.Reader, header byte) (branch *Branch, err error) { return branch, nil } + +// DecodeLeaf reads and decodes from a reader with the encoding specified in lib/trie/node/encode_doc.go. +func DecodeLeaf(r io.Reader, header byte) (leaf *Leaf, err error) { + nodeType := header >> 6 + if nodeType != 1 { + return nil, fmt.Errorf("%w: %d", ErrNodeTypeIsNotALeaf, nodeType) + } + + leaf = new(Leaf) + + keyLen := header & 0x3f + leaf.Key, err = decodeKey(r, keyLen) + if err != nil { + return nil, fmt.Errorf("cannot decode key: %w", err) + } + + sd := scale.NewDecoder(r) + var value []byte + err = sd.Decode(&value) + if err != nil { + return nil, fmt.Errorf("%w: %s", ErrDecodeValue, err) + } + + if len(value) > 0 { + leaf.Value = value + } + + leaf.Dirty = true + + return leaf, nil +} diff --git a/lib/trie/branch/decode_test.go b/lib/trie/node/decode_test.go similarity index 63% rename from lib/trie/branch/decode_test.go rename to lib/trie/node/decode_test.go index db2ce1f43f5..daf64b5992f 100644 --- a/lib/trie/branch/decode_test.go +++ b/lib/trie/node/decode_test.go @@ -1,16 +1,13 @@ // Copyright 2021 ChainSafe Systems (ON) // SPDX-License-Identifier: LGPL-3.0-only -package branch +package node import ( "bytes" "io" "testing" - "github.com/ChainSafe/gossamer/lib/trie/decode" - "github.com/ChainSafe/gossamer/lib/trie/leaf" - "github.com/ChainSafe/gossamer/lib/trie/node" "github.com/ChainSafe/gossamer/pkg/scale" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -34,7 +31,7 @@ func concatByteSlices(slices [][]byte) (concatenated []byte) { return concatenated } -func Test_Decode(t *testing.T) { +func Test_DecodeBranch(t *testing.T) { t.Parallel() testCases := map[string]struct { @@ -55,7 +52,7 @@ func Test_Decode(t *testing.T) { // missing key data byte }), header: 129, // node type 2 and key length 1 - errWrapped: decode.ErrReadKeyData, + errWrapped: ErrReadKeyData, errMessage: "cannot decode key: cannot read key data: EOF", }, "children bitmap read error": { @@ -90,10 +87,10 @@ func Test_Decode(t *testing.T) { header: 129, // node type 2 and key length 1 branch: &Branch{ Key: []byte{9}, - Children: [16]node.Node{ + Children: [16]Node{ nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - &leaf.Leaf{ + &Leaf{ Hash: []byte{1, 2, 3, 4, 5}, }, }, @@ -125,10 +122,10 @@ func Test_Decode(t *testing.T) { branch: &Branch{ Key: []byte{9}, Value: []byte{7, 8, 9}, - Children: [16]node.Node{ + Children: [16]Node{ nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - &leaf.Leaf{ + &Leaf{ Hash: []byte{1, 2, 3, 4, 5}, }, }, @@ -142,7 +139,7 @@ func Test_Decode(t *testing.T) { t.Run(name, func(t *testing.T) { t.Parallel() - branch, err := Decode(testCase.reader, testCase.header) + branch, err := DecodeBranch(testCase.reader, testCase.header) assert.ErrorIs(t, err, testCase.errWrapped) if err != nil { @@ -152,3 +149,79 @@ func Test_Decode(t *testing.T) { }) } } + +func Test_DecodeLeaf(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + reader io.Reader + header byte + leaf *Leaf + 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 + }), + header: 65, // node type 1 and key length 1 + errWrapped: ErrReadKeyData, + errMessage: "cannot decode key: cannot read key data: EOF", + }, + "value decoding error": { + reader: bytes.NewBuffer([]byte{ + 9, // key data + // missing value data + }), + header: 65, // node type 1 and key length 1 + errWrapped: ErrDecodeValue, + errMessage: "cannot decode value: EOF", + }, + "zero value": { + reader: bytes.NewBuffer([]byte{ + 9, // key data + 0, // missing value data + }), + header: 65, // node type 1 and key length 1 + leaf: &Leaf{ + Key: []byte{9}, + Dirty: true, + }, + }, + "success": { + reader: bytes.NewBuffer( + concatByteSlices([][]byte{ + {9}, // key data + scaleEncodeBytes(t, 1, 2, 3, 4, 5), // value data + }), + ), + header: 65, // node type 1 and key length 1 + leaf: &Leaf{ + Key: []byte{9}, + Value: []byte{1, 2, 3, 4, 5}, + Dirty: true, + }, + }, + } + + for name, testCase := range testCases { + testCase := testCase + t.Run(name, func(t *testing.T) { + t.Parallel() + + leaf, err := DecodeLeaf(testCase.reader, testCase.header) + + assert.ErrorIs(t, err, testCase.errWrapped) + if err != nil { + assert.EqualError(t, err, testCase.errMessage) + } + assert.Equal(t, testCase.leaf, leaf) + }) + } +} diff --git a/lib/trie/branch/dirty.go b/lib/trie/node/dirty.go similarity index 57% rename from lib/trie/branch/dirty.go rename to lib/trie/node/dirty.go index 930c01fa918..7922139b18d 100644 --- a/lib/trie/branch/dirty.go +++ b/lib/trie/node/dirty.go @@ -1,7 +1,7 @@ // Copyright 2021 ChainSafe Systems (ON) // SPDX-License-Identifier: LGPL-3.0-only -package branch +package node // IsDirty returns the dirty status of the branch. func (b *Branch) IsDirty() bool { @@ -12,3 +12,13 @@ func (b *Branch) IsDirty() bool { func (b *Branch) SetDirty(dirty bool) { b.Dirty = dirty } + +// 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 +} diff --git a/lib/trie/encodedecode_test/branch_test.go b/lib/trie/node/encode_decode_test.go similarity index 66% rename from lib/trie/encodedecode_test/branch_test.go rename to lib/trie/node/encode_decode_test.go index 2e48656888d..cc380060d6f 100644 --- a/lib/trie/encodedecode_test/branch_test.go +++ b/lib/trie/node/encode_decode_test.go @@ -1,15 +1,12 @@ // Copyright 2021 ChainSafe Systems (ON) // SPDX-License-Identifier: LGPL-3.0-only -package encodedecode_test +package node import ( "bytes" "testing" - "github.com/ChainSafe/gossamer/lib/trie/branch" - "github.com/ChainSafe/gossamer/lib/trie/leaf" - "github.com/ChainSafe/gossamer/lib/trie/node" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -18,48 +15,48 @@ func Test_Branch_Encode_Decode(t *testing.T) { t.Parallel() testCases := map[string]struct { - branchToEncode *branch.Branch - branchDecoded *branch.Branch + branchToEncode *Branch + branchDecoded *Branch }{ "empty branch": { - branchToEncode: new(branch.Branch), - branchDecoded: &branch.Branch{ + branchToEncode: new(Branch), + branchDecoded: &Branch{ Key: []byte{}, Dirty: true, }, }, "branch with key 5": { - branchToEncode: &branch.Branch{ + branchToEncode: &Branch{ Key: []byte{5}, }, - branchDecoded: &branch.Branch{ + branchDecoded: &Branch{ Key: []byte{5}, Dirty: true, }, }, "branch with two bytes key": { - branchToEncode: &branch.Branch{ + branchToEncode: &Branch{ Key: []byte{0xf, 0xa}, // note: each byte cannot be larger than 0xf }, - branchDecoded: &branch.Branch{ + branchDecoded: &Branch{ Key: []byte{0xf, 0xa}, Dirty: true, }, }, "branch with child": { - branchToEncode: &branch.Branch{ + branchToEncode: &Branch{ Key: []byte{5}, - Children: [16]node.Node{ - &leaf.Leaf{ + Children: [16]Node{ + &Leaf{ Key: []byte{9}, Value: []byte{10}, }, }, }, - branchDecoded: &branch.Branch{ + branchDecoded: &Branch{ Key: []byte{5}, - Children: [16]node.Node{ - &leaf.Leaf{ + Children: [16]Node{ + &Leaf{ Hash: []byte{0x41, 0x9, 0x4, 0xa}, }, }, @@ -83,7 +80,7 @@ func Test_Branch_Encode_Decode(t *testing.T) { require.NoError(t, err) header := oneBuffer[0] - resultBranch, err := branch.Decode(buffer, header) + resultBranch, err := DecodeBranch(buffer, header) require.NoError(t, err) assert.Equal(t, testCase.branchDecoded, resultBranch) diff --git a/lib/trie/encode/doc.go b/lib/trie/node/encode_doc.go similarity index 92% rename from lib/trie/encode/doc.go rename to lib/trie/node/encode_doc.go index e2fc9fd64da..6cb2699d59d 100644 --- a/lib/trie/encode/doc.go +++ b/lib/trie/node/encode_doc.go @@ -1,7 +1,4 @@ -// Copyright 2021 ChainSafe Systems (ON) -// SPDX-License-Identifier: LGPL-3.0-only - -package encode +package node //nolint:lll // Modified Merkle-Patricia Trie diff --git a/lib/trie/node/encode_test.go b/lib/trie/node/encode_test.go new file mode 100644 index 00000000000..ae8593ba237 --- /dev/null +++ b/lib/trie/node/encode_test.go @@ -0,0 +1,11 @@ +package node + +import "errors" + +type writeCall struct { + written []byte + n int + err error +} + +var errTest = errors.New("test error") diff --git a/lib/trie/branch/generation.go b/lib/trie/node/generation.go similarity index 56% rename from lib/trie/branch/generation.go rename to lib/trie/node/generation.go index a5d8f4e5103..ac7adc6aca6 100644 --- a/lib/trie/branch/generation.go +++ b/lib/trie/node/generation.go @@ -1,7 +1,7 @@ // Copyright 2021 ChainSafe Systems (ON) // SPDX-License-Identifier: LGPL-3.0-only -package branch +package node // SetGeneration sets the generation given to the branch. func (b *Branch) SetGeneration(generation uint64) { @@ -12,3 +12,13 @@ func (b *Branch) SetGeneration(generation uint64) { func (b *Branch) GetGeneration() 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() uint64 { + return l.Generation +} diff --git a/lib/trie/branch/hash.go b/lib/trie/node/hash.go similarity index 99% rename from lib/trie/branch/hash.go rename to lib/trie/node/hash.go index d826dc7ba8a..14086a732cc 100644 --- a/lib/trie/branch/hash.go +++ b/lib/trie/node/hash.go @@ -1,7 +1,7 @@ // Copyright 2021 ChainSafe Systems (ON) // SPDX-License-Identifier: LGPL-3.0-only -package branch +package node import ( "bytes" diff --git a/lib/trie/branch/header.go b/lib/trie/node/header.go similarity index 54% rename from lib/trie/branch/header.go rename to lib/trie/node/header.go index bbc7a683b39..b5d28a64153 100644 --- a/lib/trie/branch/header.go +++ b/lib/trie/node/header.go @@ -1,12 +1,10 @@ // Copyright 2021 ChainSafe Systems (ON) // SPDX-License-Identifier: LGPL-3.0-only -package branch +package node import ( "io" - - "github.com/ChainSafe/gossamer/lib/trie/encode" ) // encodeHeader creates the encoded header for the branch. @@ -25,7 +23,7 @@ func (b *Branch) encodeHeader(writer io.Writer) (err error) { return err } - err = encode.KeyLength(len(b.Key), writer) + err = encodeKeyLength(len(b.Key), writer) if err != nil { return err } @@ -39,3 +37,27 @@ func (b *Branch) encodeHeader(writer io.Writer) (err error) { return nil } + +// encodeHeader creates the encoded header for the leaf. +func (l *Leaf) encodeHeader(writer io.Writer) (err error) { + var header byte = 1 << 6 + + if len(l.Key) < 63 { + header = header | byte(len(l.Key)) + _, err = writer.Write([]byte{header}) + return err + } + + header = header | 0x3f + _, err = writer.Write([]byte{header}) + if err != nil { + return err + } + + err = encodeKeyLength(len(l.Key), writer) + if err != nil { + return err + } + + return nil +} diff --git a/lib/trie/branch/header_test.go b/lib/trie/node/header_test.go similarity index 51% rename from lib/trie/branch/header_test.go rename to lib/trie/node/header_test.go index 22095185490..78f40344ca8 100644 --- a/lib/trie/branch/header_test.go +++ b/lib/trie/node/header_test.go @@ -1,13 +1,12 @@ // Copyright 2021 ChainSafe Systems (ON) // SPDX-License-Identifier: LGPL-3.0-only -package branch +package node import ( "testing" - "github.com/ChainSafe/gossamer/lib/trie/encode" - gomock "github.com/golang/mock/gomock" + "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" ) @@ -73,7 +72,7 @@ func Test_Branch_encodeHeader(t *testing.T) { writes: []writeCall{ {written: []byte{0xbf}}, }, - errWrapped: encode.ErrPartialKeyTooBig, + errWrapped: ErrPartialKeyTooBig, errMessage: "partial key length cannot be larger than or equal to 2^16: 65535", }, "small key length write error": { @@ -130,3 +129,117 @@ func Test_Branch_encodeHeader(t *testing.T) { }) } } + +func Test_Leaf_encodeHeader(t *testing.T) { + testCases := map[string]struct { + leaf *Leaf + writes []writeCall + errWrapped error + errMessage string + }{ + "no key": { + leaf: &Leaf{}, + writes: []writeCall{ + {written: []byte{0x40}}, + }, + }, + "key of length 30": { + leaf: &Leaf{ + Key: make([]byte, 30), + }, + writes: []writeCall{ + {written: []byte{0x5e}}, + }, + }, + "short key write error": { + leaf: &Leaf{ + Key: make([]byte, 30), + }, + writes: []writeCall{ + { + written: []byte{0x5e}, + err: errTest, + }, + }, + errWrapped: errTest, + errMessage: errTest.Error(), + }, + "key of length 62": { + leaf: &Leaf{ + Key: make([]byte, 62), + }, + writes: []writeCall{ + {written: []byte{0x7e}}, + }, + }, + "key of length 63": { + leaf: &Leaf{ + Key: make([]byte, 63), + }, + writes: []writeCall{ + {written: []byte{0x7f}}, + {written: []byte{0x0}}, + }, + }, + "key of length 64": { + leaf: &Leaf{ + Key: make([]byte, 64), + }, + writes: []writeCall{ + {written: []byte{0x7f}}, + {written: []byte{0x1}}, + }, + }, + "long key first byte write error": { + leaf: &Leaf{ + Key: make([]byte, 63), + }, + writes: []writeCall{ + { + written: []byte{0x7f}, + err: errTest, + }, + }, + errWrapped: errTest, + errMessage: errTest.Error(), + }, + "key too big": { + leaf: &Leaf{ + Key: make([]byte, 65535+63), + }, + writes: []writeCall{ + {written: []byte{0x7f}}, + }, + errWrapped: ErrPartialKeyTooBig, + errMessage: "partial key length cannot be larger than or equal to 2^16: 65535", + }, + } + + 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.leaf.encodeHeader(writer) + + assert.ErrorIs(t, err, testCase.errWrapped) + if testCase.errWrapped != nil { + assert.EqualError(t, err, testCase.errMessage) + } + }) + } +} diff --git a/lib/trie/decode/key.go b/lib/trie/node/key.go similarity index 55% rename from lib/trie/decode/key.go rename to lib/trie/node/key.go index 4ab24adaff0..2248efd9636 100644 --- a/lib/trie/decode/key.go +++ b/lib/trie/node/key.go @@ -1,7 +1,7 @@ // Copyright 2021 ChainSafe Systems (ON) // SPDX-License-Identifier: LGPL-3.0-only -package decode +package node import ( "bytes" @@ -9,9 +9,24 @@ import ( "fmt" "io" + "github.com/ChainSafe/gossamer/lib/trie/codec" "github.com/ChainSafe/gossamer/lib/trie/pools" ) +// 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 ( @@ -20,8 +35,36 @@ var ( ErrReadKeyData = errors.New("cannot read key data") ) -// Key decodes a key from a reader. -func Key(reader io.Reader, keyLength byte) (b []byte, err error) { +// encodeKeyLength encodes the key length. +func encodeKeyLength(keyLength int, writer io.Writer) (err error) { + keyLength -= 63 + + if keyLength >= int(maxPartialKeySize) { + return fmt.Errorf("%w: %d", + ErrPartialKeyTooBig, keyLength) + } + + for i := uint16(0); i < maxPartialKeySize; i++ { + if keyLength < 255 { + _, err = writer.Write([]byte{byte(keyLength)}) + if err != nil { + return err + } + break + } + _, err = writer.Write([]byte{255}) + if err != nil { + return err + } + + keyLength -= 255 + } + + return nil +} + +// decodeKey decodes a key from a reader. +func decodeKey(reader io.Reader, keyLength byte) (b []byte, err error) { publicKeyLength := int(keyLength) if keyLength == 0x3f { @@ -62,24 +105,5 @@ func Key(reader io.Reader, keyLength byte) (b []byte, err error) { ErrReadKeyData, n, len(key)) } - return KeyLEToNibbles(key)[publicKeyLength%2:], nil -} - -// KeyLEToNibbles converts a Little Endian byte slice into nibbles. -// It assumes bytes are already in Little Endian and does not rearrange nibbles. -func KeyLEToNibbles(in []byte) (nibbles []byte) { - if len(in) == 0 { - return []byte{} - } else if len(in) == 1 && in[0] == 0 { - return []byte{0, 0} - } - - l := len(in) * 2 - nibbles = make([]byte, l) - for i, b := range in { - nibbles[2*i] = b / 16 - nibbles[2*i+1] = b % 16 - } - - return nibbles + return codec.KeyLEToNibbles(key)[publicKeyLength%2:], nil } diff --git a/lib/trie/encode/key_test.go b/lib/trie/node/key_test.go similarity index 59% rename from lib/trie/encode/key_test.go rename to lib/trie/node/key_test.go index 058be5643e2..088ab38eb48 100644 --- a/lib/trie/encode/key_test.go +++ b/lib/trie/node/key_test.go @@ -1,11 +1,8 @@ -// Copyright 2021 ChainSafe Systems (ON) -// SPDX-License-Identifier: LGPL-3.0-only - -package encode +package node import ( "bytes" - "errors" + "io" "testing" "github.com/golang/mock/gomock" @@ -13,17 +10,15 @@ import ( "github.com/stretchr/testify/require" ) -type writeCall struct { - written []byte - n int - err error +func repeatBytes(n int, b byte) (slice []byte) { + slice = make([]byte, n) + for i := range slice { + slice[i] = b + } + return slice } -var errTest = errors.New("test error") - -//go:generate mockgen -destination=writer_mock_test.go -package $GOPACKAGE io Writer - -func Test_KeyLength(t *testing.T) { +func Test_encodeKeyLength(t *testing.T) { t.Parallel() testCases := map[string]struct { @@ -112,7 +107,7 @@ func Test_KeyLength(t *testing.T) { previousCall = call } - err := KeyLength(testCase.keyLength, writer) + err := encodeKeyLength(testCase.keyLength, writer) assert.ErrorIs(t, err, testCase.errWrapped) if testCase.errWrapped != nil { @@ -139,46 +134,66 @@ func Test_KeyLength(t *testing.T) { buffer := bytes.NewBuffer(nil) buffer.Grow(expectedEncodingLength) - err := KeyLength(keyLength, buffer) + err := encodeKeyLength(keyLength, buffer) require.NoError(t, err) assert.Equal(t, expectedBytes, buffer.Bytes()) }) } -func Test_NibblesToKeyLE(t *testing.T) { +func Test_decodeKey(t *testing.T) { t.Parallel() testCases := map[string]struct { - nibbles []byte - keyLE []byte + reader io.Reader + keyLength byte + b []byte + errWrapped error + errMessage string }{ - "nil nibbles": { - keyLE: []byte{}, + "zero key length": { + b: []byte{}, }, - "empty nibbles": { - nibbles: []byte{}, - keyLE: []byte{}, + "short key length": { + reader: bytes.NewBuffer([]byte{1, 2, 3}), + keyLength: 5, + b: []byte{0x1, 0x0, 0x2, 0x0, 0x3}, }, - "0xF 0xF": { - nibbles: []byte{0xF, 0xF}, - keyLE: []byte{0xFF}, + "key read error": { + reader: bytes.NewBuffer(nil), + keyLength: 5, + errWrapped: ErrReadKeyData, + errMessage: "cannot read key data: EOF", }, - "0x3 0xa 0x0 0x5": { - nibbles: []byte{0x3, 0xa, 0x0, 0x5}, - keyLE: []byte{0x3a, 0x05}, + "long key length": { + reader: bytes.NewBuffer( + append( + []byte{ + 6, // key length + }, + repeatBytes(64, 7)..., // key data + )), + keyLength: 0x3f, + b: []byte{ + 0x7, 0x0, 0x7, 0x0, 0x7, 0x0, 0x7, 0x0, 0x7, 0x0, + 0x7, 0x0, 0x7, 0x0, 0x7, 0x0, 0x7, 0x0, 0x7, 0x0, + 0x7, 0x0, 0x7, 0x0, 0x7, 0x0, 0x7, 0x0, 0x7, 0x0, + 0x7, 0x0, 0x7, 0x0, 0x7, 0x0, 0x7, 0x0, 0x7, 0x0, + 0x7, 0x0, 0x7, 0x0, 0x7, 0x0, 0x7, 0x0, 0x7, 0x0, + 0x7, 0x0, 0x7, 0x0, 0x7, 0x0, 0x7, 0x0, 0x7, 0x0, + 0x7, 0x0, 0x7, 0x0, 0x7, 0x0, 0x7, 0x0, 0x7}, }, - "0xa 0xa 0xf 0xf 0x0 0x1": { - nibbles: []byte{0xa, 0xa, 0xf, 0xf, 0x0, 0x1}, - keyLE: []byte{0xaa, 0xff, 0x01}, + "key length read error": { + reader: bytes.NewBuffer(nil), + keyLength: 0x3f, + errWrapped: ErrReadKeyLength, + errMessage: "cannot read key length: EOF", }, - "0xa 0xa 0xf 0xf 0x0 0x1 0xc 0x2": { - nibbles: []byte{0xa, 0xa, 0xf, 0xf, 0x0, 0x1, 0xc, 0x2}, - keyLE: []byte{0xaa, 0xff, 0x01, 0xc2}, - }, - "0xa 0xa 0xf 0xf 0x0 0x1 0xc": { - nibbles: []byte{0xa, 0xa, 0xf, 0xf, 0x0, 0x1, 0xc}, - keyLE: []byte{0xa, 0xaf, 0xf0, 0x1c}, + "key length too big": { + reader: bytes.NewBuffer(repeatBytes(257, 0xff)), + keyLength: 0x3f, + errWrapped: ErrPartialKeyTooBig, + errMessage: "partial key length cannot be larger than or equal to 2^16: 65598", }, } @@ -187,9 +202,13 @@ func Test_NibblesToKeyLE(t *testing.T) { t.Run(name, func(t *testing.T) { t.Parallel() - keyLE := NibblesToKeyLE(testCase.nibbles) + b, err := decodeKey(testCase.reader, testCase.keyLength) - assert.Equal(t, testCase.keyLE, keyLE) + assert.ErrorIs(t, err, testCase.errWrapped) + if err != nil { + assert.EqualError(t, err, testCase.errMessage) + } + assert.Equal(t, testCase.b, b) }) } } diff --git a/lib/trie/leaf/leaf.go b/lib/trie/node/leaf.go similarity index 87% rename from lib/trie/leaf/leaf.go rename to lib/trie/node/leaf.go index 7e86ad9c952..d77fe3339fa 100644 --- a/lib/trie/leaf/leaf.go +++ b/lib/trie/node/leaf.go @@ -1,17 +1,16 @@ // Copyright 2021 ChainSafe Systems (ON) // SPDX-License-Identifier: LGPL-3.0-only -package leaf +package node import ( "fmt" "sync" "github.com/ChainSafe/gossamer/lib/common" - "github.com/ChainSafe/gossamer/lib/trie/node" ) -var _ node.Node = (*Leaf)(nil) +var _ Node = (*Leaf)(nil) // Leaf is a leaf in the trie. type Leaf struct { diff --git a/lib/trie/leaf/encode.go b/lib/trie/node/leaf_encode.go similarity index 96% rename from lib/trie/leaf/encode.go rename to lib/trie/node/leaf_encode.go index 477d6dd78d8..8caafb10e51 100644 --- a/lib/trie/leaf/encode.go +++ b/lib/trie/node/leaf_encode.go @@ -1,7 +1,7 @@ // Copyright 2021 ChainSafe Systems (ON) // SPDX-License-Identifier: LGPL-3.0-only -package leaf +package node import ( "bytes" @@ -10,7 +10,7 @@ import ( "io" "github.com/ChainSafe/gossamer/lib/common" - "github.com/ChainSafe/gossamer/lib/trie/encode" + "github.com/ChainSafe/gossamer/lib/trie/codec" "github.com/ChainSafe/gossamer/lib/trie/pools" "github.com/ChainSafe/gossamer/pkg/scale" ) @@ -85,7 +85,7 @@ func (l *Leaf) EncodeAndHash() (encoding, hash []byte, err error) { // 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 encode.Buffer) (err error) { +func (l *Leaf) Encode(buffer Buffer) (err error) { l.encodingMu.RLock() if !l.Dirty && l.Encoding != nil { _, err = buffer.Write(l.Encoding) @@ -102,7 +102,7 @@ func (l *Leaf) Encode(buffer encode.Buffer) (err error) { return fmt.Errorf("cannot encode header: %w", err) } - keyLE := encode.NibblesToKeyLE(l.Key) + keyLE := codec.NibblesToKeyLE(l.Key) _, err = buffer.Write(keyLE) if err != nil { return fmt.Errorf("cannot write LE key to buffer: %w", err) diff --git a/lib/trie/leaf/encode_test.go b/lib/trie/node/leaf_encode_test.go similarity index 96% rename from lib/trie/leaf/encode_test.go rename to lib/trie/node/leaf_encode_test.go index 51646719b2d..2edcb0e289f 100644 --- a/lib/trie/leaf/encode_test.go +++ b/lib/trie/node/leaf_encode_test.go @@ -1,26 +1,16 @@ // Copyright 2021 ChainSafe Systems (ON) // SPDX-License-Identifier: LGPL-3.0-only -package leaf +package node import ( - "errors" "testing" - "github.com/ChainSafe/gossamer/lib/trie/encode" "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) -type writeCall struct { - written []byte - n int - err error -} - -var errTest = errors.New("test error") - //go:generate mockgen -destination=buffer_mock_test.go -package $GOPACKAGE github.com/ChainSafe/gossamer/lib/trie/encode Buffer func Test_Leaf_Encode(t *testing.T) { @@ -70,7 +60,7 @@ func Test_Leaf_Encode(t *testing.T) { written: []byte{127}, }, }, - wrappedErr: encode.ErrPartialKeyTooBig, + 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": { diff --git a/lib/trie/node/interface.go b/lib/trie/node/node.go similarity index 77% rename from lib/trie/node/interface.go rename to lib/trie/node/node.go index 1e52a39204b..63fe57443f0 100644 --- a/lib/trie/node/interface.go +++ b/lib/trie/node/node.go @@ -3,13 +3,9 @@ package node -import ( - "github.com/ChainSafe/gossamer/lib/trie/encode" -) - // Node is node in the trie and can be a leaf or a branch. type Node interface { - Encode(buffer encode.Buffer) (err error) // TODO change to io.Writer + Encode(buffer Buffer) (err error) // TODO change to io.Writer EncodeAndHash() ([]byte, []byte, error) ScaleEncodeHash() (b []byte, err error) IsDirty() bool diff --git a/lib/trie/leaf/writer_mock_test.go b/lib/trie/node/writer_mock_test.go similarity index 95% rename from lib/trie/leaf/writer_mock_test.go rename to lib/trie/node/writer_mock_test.go index 04cb474a72c..9665f01c850 100644 --- a/lib/trie/leaf/writer_mock_test.go +++ b/lib/trie/node/writer_mock_test.go @@ -1,8 +1,8 @@ // Code generated by MockGen. DO NOT EDIT. // Source: io (interfaces: Writer) -// Package leaf is a generated GoMock package. -package leaf +// Package node is a generated GoMock package. +package node import ( reflect "reflect" diff --git a/lib/trie/print.go b/lib/trie/print.go index ce3f85a979f..4cbec8c39cb 100644 --- a/lib/trie/print.go +++ b/lib/trie/print.go @@ -8,8 +8,6 @@ import ( "fmt" "github.com/ChainSafe/gossamer/lib/common" - "github.com/ChainSafe/gossamer/lib/trie/branch" - "github.com/ChainSafe/gossamer/lib/trie/leaf" "github.com/ChainSafe/gossamer/lib/trie/node" "github.com/ChainSafe/gossamer/lib/trie/pools" @@ -29,7 +27,7 @@ func (t *Trie) String() string { func (t *Trie) string(tree gotree.Tree, curr node.Node, idx int) { switch c := curr.(type) { - case *branch.Branch: + case *node.Branch: buffer := pools.EncodingBuffers.Get().(*bytes.Buffer) buffer.Reset() @@ -51,7 +49,7 @@ func (t *Trie) string(tree gotree.Tree, curr node.Node, idx int) { t.string(sub, child, i) } } - case *leaf.Leaf: + case *node.Leaf: buffer := pools.EncodingBuffers.Get().(*bytes.Buffer) buffer.Reset() diff --git a/lib/trie/proof.go b/lib/trie/proof.go index 094fe48f7e9..e79c47e28e6 100644 --- a/lib/trie/proof.go +++ b/lib/trie/proof.go @@ -8,7 +8,7 @@ import ( "github.com/ChainSafe/chaindb" "github.com/ChainSafe/gossamer/lib/common" - "github.com/ChainSafe/gossamer/lib/trie/decode" + "github.com/ChainSafe/gossamer/lib/trie/codec" "github.com/ChainSafe/gossamer/lib/trie/record" ) @@ -39,7 +39,7 @@ func GenerateProof(root []byte, keys [][]byte, db chaindb.Database) ([][]byte, e } for _, k := range keys { - nk := decode.KeyLEToNibbles(k) + nk := codec.KeyLEToNibbles(k) recorder := record.NewRecorder() err := findAndRecord(proofTrie, nk, recorder) diff --git a/lib/trie/trie.go b/lib/trie/trie.go index 19953999743..4595d7cbff3 100644 --- a/lib/trie/trie.go +++ b/lib/trie/trie.go @@ -8,10 +8,7 @@ import ( "fmt" "github.com/ChainSafe/gossamer/lib/common" - "github.com/ChainSafe/gossamer/lib/trie/branch" - "github.com/ChainSafe/gossamer/lib/trie/decode" - "github.com/ChainSafe/gossamer/lib/trie/encode" - "github.com/ChainSafe/gossamer/lib/trie/leaf" + "github.com/ChainSafe/gossamer/lib/trie/codec" "github.com/ChainSafe/gossamer/lib/trie/node" "github.com/ChainSafe/gossamer/lib/trie/pools" ) @@ -151,15 +148,15 @@ func (t *Trie) Entries() map[string][]byte { func (t *Trie) entries(current node.Node, prefix []byte, kv map[string][]byte) map[string][]byte { switch c := current.(type) { - case *branch.Branch: + case *node.Branch: if c.Value != nil { - kv[string(encode.NibblesToKeyLE(append(prefix, c.Key...)))] = c.Value + kv[string(codec.NibblesToKeyLE(append(prefix, c.Key...)))] = c.Value } for i, child := range c.Children { t.entries(child, append(prefix, append(c.Key, byte(i))...), kv) } - case *leaf.Leaf: - kv[string(encode.NibblesToKeyLE(append(prefix, c.Key...)))] = c.Value + case *node.Leaf: + kv[string(codec.NibblesToKeyLE(append(prefix, c.Key...)))] = c.Value return kv } @@ -168,19 +165,19 @@ func (t *Trie) entries(current node.Node, prefix []byte, kv map[string][]byte) m // NextKey returns the next key in the trie in lexicographic order. It returns nil if there is no next key func (t *Trie) NextKey(key []byte) []byte { - k := decode.KeyLEToNibbles(key) + k := codec.KeyLEToNibbles(key) next := t.nextKey(t.root, nil, k) if next == nil { return nil } - return encode.NibblesToKeyLE(next) + return codec.NibblesToKeyLE(next) } func (t *Trie) nextKey(curr node.Node, prefix, key []byte) []byte { switch c := curr.(type) { - case *branch.Branch: + case *node.Branch: fullKey := append(prefix, c.Key...) var cmp int if len(key) < len(fullKey) { @@ -229,7 +226,7 @@ func (t *Trie) nextKey(curr node.Node, prefix, key []byte) []byte { } } } - case *leaf.Leaf: + case *node.Leaf: fullKey := append(prefix, c.Key...) var cmp int if len(key) < len(fullKey) { @@ -259,15 +256,15 @@ func (t *Trie) Put(key, value []byte) { } func (t *Trie) tryPut(key, value []byte) { - k := decode.KeyLEToNibbles(key) + k := codec.KeyLEToNibbles(key) - t.root = t.insert(t.root, k, &leaf.Leaf{Key: nil, Value: value, Dirty: true, Generation: t.generation}) + t.root = t.insert(t.root, k, &node.Leaf{Key: nil, Value: value, Dirty: true, Generation: t.generation}) } // insert attempts to insert a key with value into the trie func (t *Trie) insert(parent node.Node, key []byte, value node.Node) node.Node { switch p := t.maybeUpdateGeneration(parent).(type) { - case *branch.Branch: + case *node.Branch: n := t.updateBranch(p, key, value) if p != nil && n != nil && n.IsDirty() { @@ -277,12 +274,12 @@ func (t *Trie) insert(parent node.Node, key []byte, value node.Node) node.Node { case nil: value.SetKey(key) return value - case *leaf.Leaf: + case *node.Leaf: // if a value already exists in the trie at this key, overwrite it with the new value // if the values are the same, don't mark node dirty if p.Value != nil && bytes.Equal(p.Key, key) { - if !bytes.Equal(value.(*leaf.Leaf).Value, p.Value) { - p.Value = value.(*leaf.Leaf).Value + if !bytes.Equal(value.(*node.Leaf).Value, p.Value) { + p.Value = value.(*node.Leaf).Value p.Dirty = true } return p @@ -291,12 +288,12 @@ func (t *Trie) insert(parent node.Node, key []byte, value node.Node) node.Node { length := lenCommonPrefix(key, p.Key) // need to convert this leaf into a branch - br := &branch.Branch{Key: key[:length], Dirty: true, Generation: t.generation} + br := &node.Branch{Key: key[:length], Dirty: true, Generation: t.generation} parentKey := p.Key // value goes at this branch if len(key) == length { - br.Value = value.(*leaf.Leaf).Value + br.Value = value.(*node.Leaf).Value br.SetDirty(true) // if we are not replacing previous leaf, then add it as a child to the new branch @@ -333,7 +330,7 @@ func (t *Trie) insert(parent node.Node, key []byte, value node.Node) node.Node { // updateBranch attempts to add the value node to a branch // inserts the value node as the branch's child at the index that's // the first nibble of the key -func (t *Trie) updateBranch(p *branch.Branch, key []byte, value node.Node) (n node.Node) { +func (t *Trie) updateBranch(p *node.Branch, key []byte, value node.Node) (n node.Node) { length := lenCommonPrefix(key, p.Key) // whole parent key matches @@ -342,16 +339,16 @@ func (t *Trie) updateBranch(p *branch.Branch, key []byte, value node.Node) (n no if bytes.Equal(key, p.Key) { p.SetDirty(true) switch v := value.(type) { - case *branch.Branch: + case *node.Branch: p.Value = v.Value - case *leaf.Leaf: + case *node.Leaf: p.Value = v.Value } return p } switch c := p.Children[key[length]].(type) { - case *branch.Branch, *leaf.Leaf: + case *node.Branch, *node.Leaf: n = t.insert(c, key[length+1:], value) p.Children[key[length]] = n n.SetDirty(true) @@ -359,7 +356,7 @@ func (t *Trie) updateBranch(p *branch.Branch, key []byte, value node.Node) (n no return p case nil: // otherwise, add node as child of this branch - value.(*leaf.Leaf).Key = key[length+1:] + value.(*node.Leaf).Key = key[length+1:] p.Children[key[length]] = value p.SetDirty(true) return p @@ -370,13 +367,13 @@ func (t *Trie) updateBranch(p *branch.Branch, key []byte, value node.Node) (n no // we need to branch out at the point where the keys diverge // update partial keys, new branch has key up to matching length - br := &branch.Branch{Key: key[:length], Dirty: true, Generation: t.generation} + br := &node.Branch{Key: key[:length], Dirty: true, Generation: t.generation} parentIndex := p.Key[length] br.Children[parentIndex] = t.insert(nil, p.Key[length+1:], p) if len(key) <= length { - br.Value = value.(*leaf.Leaf).Value + br.Value = value.(*node.Leaf).Value } else { br.Children[key[length]] = t.insert(nil, key[length+1:], value) } @@ -406,7 +403,7 @@ func (t *Trie) LoadFromMap(data map[string]string) error { func (t *Trie) GetKeysWithPrefix(prefix []byte) [][]byte { var p []byte if len(prefix) != 0 { - p = decode.KeyLEToNibbles(prefix) + p = codec.KeyLEToNibbles(prefix) if p[len(p)-1] == 0 { p = p[:len(p)-1] } @@ -417,7 +414,7 @@ func (t *Trie) GetKeysWithPrefix(prefix []byte) [][]byte { func (t *Trie) getKeysWithPrefix(parent node.Node, prefix, key []byte, keys [][]byte) [][]byte { switch p := parent.(type) { - case *branch.Branch: + case *node.Branch: length := lenCommonPrefix(p.Key, key) if bytes.Equal(p.Key[:length], key) || len(key) == 0 { @@ -433,10 +430,10 @@ func (t *Trie) getKeysWithPrefix(parent node.Node, prefix, key []byte, keys [][] key = key[len(p.Key):] keys = t.getKeysWithPrefix(p.Children[key[0]], append(append(prefix, p.Key...), key[0]), key[1:], keys) - case *leaf.Leaf: + case *node.Leaf: length := lenCommonPrefix(p.Key, key) if bytes.Equal(p.Key[:length], key) || len(key) == 0 { - keys = append(keys, encode.NibblesToKeyLE(append(prefix, p.Key...))) + keys = append(keys, codec.NibblesToKeyLE(append(prefix, p.Key...))) } case nil: return keys @@ -448,16 +445,16 @@ func (t *Trie) getKeysWithPrefix(parent node.Node, prefix, key []byte, keys [][] // it uses the prefix to determine the entire key func (t *Trie) addAllKeys(parent node.Node, prefix []byte, keys [][]byte) [][]byte { switch p := parent.(type) { - case *branch.Branch: + case *node.Branch: if p.Value != nil { - keys = append(keys, encode.NibblesToKeyLE(append(prefix, p.Key...))) + keys = append(keys, codec.NibblesToKeyLE(append(prefix, p.Key...))) } for i, child := range p.Children { keys = t.addAllKeys(child, append(append(prefix, p.Key...), byte(i)), keys) } - case *leaf.Leaf: - keys = append(keys, encode.NibblesToKeyLE(append(prefix, p.Key...))) + case *node.Leaf: + keys = append(keys, codec.NibblesToKeyLE(append(prefix, p.Key...))) case nil: return keys } @@ -475,23 +472,23 @@ func (t *Trie) Get(key []byte) []byte { return l.Value } -func (t *Trie) tryGet(key []byte) *leaf.Leaf { - k := decode.KeyLEToNibbles(key) +func (t *Trie) tryGet(key []byte) *node.Leaf { + k := codec.KeyLEToNibbles(key) return t.retrieve(t.root, k) } -func (t *Trie) retrieve(parent node.Node, key []byte) *leaf.Leaf { +func (t *Trie) retrieve(parent node.Node, key []byte) *node.Leaf { var ( - value *leaf.Leaf + value *node.Leaf ) switch p := parent.(type) { - case *branch.Branch: + case *node.Branch: length := lenCommonPrefix(p.Key, key) // found the value at this node if bytes.Equal(p.Key, key) || len(key) == 0 { - return &leaf.Leaf{Key: p.Key, Value: p.Value, Dirty: false} + return &node.Leaf{Key: p.Key, Value: p.Value, Dirty: false} } // did not find value @@ -500,7 +497,7 @@ func (t *Trie) retrieve(parent node.Node, key []byte) *leaf.Leaf { } value = t.retrieve(p.Children[key[length]], key[length+1:]) - case *leaf.Leaf: + case *node.Leaf: if bytes.Equal(p.Key, key) { value = p } @@ -516,7 +513,7 @@ func (t *Trie) ClearPrefixLimit(prefix []byte, limit uint32) (uint32, bool) { return 0, false } - p := decode.KeyLEToNibbles(prefix) + p := codec.KeyLEToNibbles(prefix) if len(p) > 0 && p[len(p)-1] == 0 { p = p[:len(p)-1] } @@ -533,7 +530,7 @@ func (t *Trie) clearPrefixLimit(cn node.Node, prefix []byte, limit *uint32) (nod curr := t.maybeUpdateGeneration(cn) switch c := curr.(type) { - case *branch.Branch: + case *node.Branch: length := lenCommonPrefix(c.Key, prefix) if length == len(prefix) { n, _ := t.deleteNodes(c, []byte{}, limit) @@ -571,7 +568,7 @@ func (t *Trie) clearPrefixLimit(cn node.Node, prefix []byte, limit *uint32) (nod } return curr, curr.IsDirty(), allDeleted - case *leaf.Leaf: + case *node.Leaf: length := lenCommonPrefix(c.Key, prefix) if length == len(prefix) { *limit-- @@ -591,13 +588,13 @@ func (t *Trie) deleteNodes(cn node.Node, prefix []byte, limit *uint32) (node.Nod curr := t.maybeUpdateGeneration(cn) switch c := curr.(type) { - case *leaf.Leaf: + case *node.Leaf: if *limit == 0 { return c, false } *limit-- return nil, true - case *branch.Branch: + case *node.Branch: if len(c.Key) != 0 { prefix = append(prefix, c.Key...) } @@ -645,7 +642,7 @@ func (t *Trie) ClearPrefix(prefix []byte) { return } - p := decode.KeyLEToNibbles(prefix) + p := codec.KeyLEToNibbles(prefix) if len(p) > 0 && p[len(p)-1] == 0 { p = p[:len(p)-1] } @@ -656,7 +653,7 @@ func (t *Trie) ClearPrefix(prefix []byte) { func (t *Trie) clearPrefix(cn node.Node, prefix []byte) (node.Node, bool) { curr := t.maybeUpdateGeneration(cn) switch c := curr.(type) { - case *branch.Branch: + case *node.Branch: length := lenCommonPrefix(c.Key, prefix) if length == len(prefix) { @@ -690,7 +687,7 @@ func (t *Trie) clearPrefix(cn node.Node, prefix []byte) (node.Node, bool) { } return curr, curr.IsDirty() - case *leaf.Leaf: + case *node.Leaf: length := lenCommonPrefix(c.Key, prefix) if length == len(prefix) { return nil, true @@ -705,14 +702,14 @@ func (t *Trie) clearPrefix(cn node.Node, prefix []byte) (node.Node, bool) { // Delete removes any existing value for key from the trie. func (t *Trie) Delete(key []byte) { - k := decode.KeyLEToNibbles(key) + k := codec.KeyLEToNibbles(key) t.root, _ = t.delete(t.root, k) } func (t *Trie) delete(parent node.Node, key []byte) (node.Node, bool) { // Store the current node and return it, if the trie is not updated. switch p := t.maybeUpdateGeneration(parent).(type) { - case *branch.Branch: + case *node.Branch: length := lenCommonPrefix(p.Key, key) if bytes.Equal(p.Key, key) || len(key) == 0 { @@ -732,7 +729,7 @@ func (t *Trie) delete(parent node.Node, key []byte) (node.Node, bool) { p.SetDirty(true) n = handleDeletion(p, key) return n, true - case *leaf.Leaf: + case *node.Leaf: if bytes.Equal(key, p.Key) || len(key) == 0 { // Key exists. Delete it. return nil, true @@ -749,14 +746,14 @@ func (t *Trie) delete(parent node.Node, key []byte) (node.Node, bool) { // handleDeletion is called when a value is deleted from a branch // if the updated branch only has 1 child, it should be combined with that child // if the updated branch only has a value, it should be turned into a leaf -func handleDeletion(p *branch.Branch, key []byte) node.Node { +func handleDeletion(p *node.Branch, key []byte) node.Node { var n node.Node = p length := lenCommonPrefix(p.Key, key) bitmap := p.ChildrenBitmap() // if branch has no children, just a value, turn it into a leaf if bitmap == 0 && p.Value != nil { - n = &leaf.Leaf{Key: key[:length], Value: p.Value, Dirty: true} + n = &node.Leaf{Key: key[:length], Value: p.Value, Dirty: true} } else if p.NumChildren() == 1 && p.Value == nil { // there is only 1 child and no value, combine the child branch with this branch // find index of child @@ -770,10 +767,10 @@ func handleDeletion(p *branch.Branch, key []byte) node.Node { child := p.Children[i] switch c := child.(type) { - case *leaf.Leaf: - n = &leaf.Leaf{Key: append(append(p.Key, []byte{byte(i)}...), c.Key...), Value: c.Value} - case *branch.Branch: - br := new(branch.Branch) + case *node.Leaf: + n = &node.Leaf{Key: append(append(p.Key, []byte{byte(i)}...), c.Key...), Value: c.Value} + case *node.Branch: + br := new(node.Branch) br.Key = append(p.Key, append([]byte{byte(i)}, c.Key...)...) // adopt the grandchildren diff --git a/lib/trie/trie_test.go b/lib/trie/trie_test.go index e8c2f885bc8..4c8c0acab1e 100644 --- a/lib/trie/trie_test.go +++ b/lib/trie/trie_test.go @@ -21,8 +21,8 @@ import ( "github.com/stretchr/testify/require" "github.com/ChainSafe/gossamer/lib/common" - "github.com/ChainSafe/gossamer/lib/trie/decode" - "github.com/ChainSafe/gossamer/lib/trie/leaf" + "github.com/ChainSafe/gossamer/lib/trie/codec" + "github.com/ChainSafe/gossamer/lib/trie/node" ) type commonPrefixTest struct { @@ -70,7 +70,7 @@ func TestNewEmptyTrie(t *testing.T) { } func TestNewTrie(t *testing.T) { - trie := NewTrie(&leaf.Leaf{Key: []byte{0}, Value: []byte{17}}) + trie := NewTrie(&node.Leaf{Key: []byte{0}, Value: []byte{17}}) if trie == nil { t.Error("did not initialise trie") } @@ -875,7 +875,7 @@ func TestClearPrefix(t *testing.T) { require.Equal(t, dcTrieHash, ssTrieHash) ssTrie.ClearPrefix(prefix) - prefixNibbles := decode.KeyLEToNibbles(prefix) + prefixNibbles := codec.KeyLEToNibbles(prefix) if len(prefixNibbles) > 0 && prefixNibbles[len(prefixNibbles)-1] == 0 { prefixNibbles = prefixNibbles[:len(prefixNibbles)-1] } @@ -883,7 +883,7 @@ func TestClearPrefix(t *testing.T) { for _, test := range tests { res := ssTrie.Get(test.key) - keyNibbles := decode.KeyLEToNibbles(test.key) + keyNibbles := codec.KeyLEToNibbles(test.key) length := lenCommonPrefix(keyNibbles, prefixNibbles) if length == len(prefixNibbles) { require.Nil(t, res) @@ -944,8 +944,8 @@ func TestClearPrefix_Small(t *testing.T) { } ssTrie.ClearPrefix([]byte("noo")) - require.Equal(t, ssTrie.root, &leaf.Leaf{ - Key: decode.KeyLEToNibbles([]byte("other")), + require.Equal(t, ssTrie.root, &node.Leaf{ + Key: codec.KeyLEToNibbles([]byte("other")), Value: []byte("other"), Dirty: true, }) @@ -1293,7 +1293,7 @@ func TestTrie_ClearPrefixLimit(t *testing.T) { } testFn := func(testCase []Test, prefix []byte) { - prefixNibbles := decode.KeyLEToNibbles(prefix) + prefixNibbles := codec.KeyLEToNibbles(prefix) if len(prefixNibbles) > 0 && prefixNibbles[len(prefixNibbles)-1] == 0 { prefixNibbles = prefixNibbles[:len(prefixNibbles)-1] } @@ -1312,7 +1312,7 @@ func TestTrie_ClearPrefixLimit(t *testing.T) { for _, test := range testCase { val := trieClearPrefix.Get(test.key) - keyNibbles := decode.KeyLEToNibbles(test.key) + keyNibbles := codec.KeyLEToNibbles(test.key) length := lenCommonPrefix(keyNibbles, prefixNibbles) if length == len(prefixNibbles) { @@ -1401,7 +1401,7 @@ func TestTrie_ClearPrefixLimitSnapshot(t *testing.T) { for _, testCase := range cases { for _, prefix := range prefixes { - prefixNibbles := decode.KeyLEToNibbles(prefix) + prefixNibbles := codec.KeyLEToNibbles(prefix) if len(prefixNibbles) > 0 && prefixNibbles[len(prefixNibbles)-1] == 0 { prefixNibbles = prefixNibbles[:len(prefixNibbles)-1] } @@ -1441,7 +1441,7 @@ func TestTrie_ClearPrefixLimitSnapshot(t *testing.T) { for _, test := range testCase { val := ssTrie.Get(test.key) - keyNibbles := decode.KeyLEToNibbles(test.key) + keyNibbles := codec.KeyLEToNibbles(test.key) length := lenCommonPrefix(keyNibbles, prefixNibbles) if length == len(prefixNibbles) {