Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(lib/trie): refactor encoding and hash related code in trie package #2077

Merged
merged 50 commits into from
Dec 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
ccf0218
export all trie node methods
qdm12 Nov 23, 2021
c94b882
Export `node` interface
qdm12 Nov 23, 2021
a10fd38
export `branch`
qdm12 Nov 23, 2021
b6fc29e
export `leaf` struct
qdm12 Nov 23, 2021
5bc7d18
Add exported comments
qdm12 Nov 23, 2021
59ab003
Refactor encoding and hash related code with tests
qdm12 Nov 29, 2021
d87deba
Address TODOs
qdm12 Nov 29, 2021
77e4203
Remove no longer needed mocks
qdm12 Nov 29, 2021
d5f06a4
Fix encode decode tests
qdm12 Nov 29, 2021
07c52c2
Remove old parallel related code
qdm12 Nov 30, 2021
4a7cb4c
Fix eventual bad type assertion
qdm12 Nov 30, 2021
0063885
Remove commented Decode method in node interface
qdm12 Nov 30, 2021
a3ee3a7
chore(lib/trie): `lib/trie/recorder` sub-package (#2082)
qdm12 Nov 30, 2021
e77adb2
Remove ReadNextByte and use sync.Pool
qdm12 Dec 1, 2021
8697d2d
Rename `ExtraPartialKeyLength` to `KeyLength`
qdm12 Dec 2, 2021
b03ecff
Remove unneded `NibblesToKey`
qdm12 Dec 2, 2021
207a5c3
Rename `Header()` to `encodeHeader()`
qdm12 Dec 6, 2021
37425a7
encode headers directly to buffer
qdm12 Dec 7, 2021
301188d
Merge packages in `lib/trie/node`
qdm12 Dec 7, 2021
d35fc0f
`lib/trie/node` -> `internal/trie/node`
qdm12 Dec 7, 2021
9df5d2c
Simplify and fix mock generations
qdm12 Dec 7, 2021
d65d600
Add licenses
qdm12 Dec 7, 2021
a443ac5
Use `Node` instead of `node.Node` in `lib/trie`
qdm12 Dec 7, 2021
d1e21cc
`lib/trie/recorder` -> `internal/trie/recorder`
qdm12 Dec 7, 2021
4721f25
`lib/trie/pools` -> `internal/trie/pools`
qdm12 Dec 7, 2021
8cd2ebb
`lib/trie/codec` -> `internal/trie/codec`
qdm12 Dec 7, 2021
1c53578
Node interface named returns
qdm12 Dec 7, 2021
79c2ecb
Use `bytes.NewReader` for readers
qdm12 Dec 7, 2021
5cce780
Add `GetValue` method to node interface
qdm12 Dec 8, 2021
5b800f1
Add `GetKey` method to node interface
qdm12 Dec 8, 2021
93ee8b6
`node.Decode` function
qdm12 Dec 9, 2021
c054516
Apply suggestions from @kishansagathiya's code review
qdm12 Dec 13, 2021
87f058d
Improve ScaleEncodeHash error wrapping
qdm12 Dec 13, 2021
9fd4036
Shorten children bitmap bitwise or
qdm12 Dec 13, 2021
2feebe7
Add comments for `Dirty` and `Generation`
qdm12 Dec 13, 2021
8c213df
Unexport `Generation` node field
qdm12 Dec 13, 2021
db578d4
Unexport `Dirty` field for node
qdm12 Dec 13, 2021
a4da41a
Unexport node's `Hash` as `hashDigest`
qdm12 Dec 13, 2021
89bf885
Trie string does not cache encoding in nodes
qdm12 Dec 13, 2021
23560c6
Unexport node's `Encoding` field
qdm12 Dec 13, 2021
1837d85
Fix `NewBranch` to `NewLeaf`
qdm12 Dec 14, 2021
3129e79
Add node type comments in tests
qdm12 Dec 14, 2021
7503f21
Use `node.Type` for Type constants
qdm12 Dec 14, 2021
e17c08e
`keyLenOffset` constant 0x3f
qdm12 Dec 14, 2021
865aae6
Fix comment (@noot suggestion)
qdm12 Dec 15, 2021
8c31788
Updated generation comment
qdm12 Dec 15, 2021
831213c
fix copy functions to copy nil slices correctly
qdm12 Dec 16, 2021
8a315af
Move leaf hash methods to hash.go
qdm12 Dec 16, 2021
9c73b48
fix naming in `decodeKey` function
qdm12 Dec 16, 2021
61baed1
95.7% test coverage for `internal/trie/node`
qdm12 Dec 16, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions internal/trie/codec/nibbles.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright 2021 ChainSafe Systems (ON)
// SPDX-License-Identifier: LGPL-3.0-only

package codec

// NibblesToKeyLE converts a slice of nibbles with length k into a
// Little Endian byte slice.
// It assumes nibbles are already in Little Endian and does not rearrange nibbles.
// If the length of the input is odd, the result is
// [ 0000 in[0] | in[1] in[2] | ... | in[k-2] in[k-1] ]
// Otherwise, the result is
// [ in[0] in[1] | ... | in[k-2] in[k-1] ]
func NibblesToKeyLE(nibbles []byte) []byte {
if len(nibbles)%2 == 0 {
keyLE := make([]byte, len(nibbles)/2)
for i := 0; i < len(nibbles); i += 2 {
keyLE[i/2] = (nibbles[i] << 4 & 0xf0) | (nibbles[i+1] & 0xf)
}
return keyLE
}

keyLE := make([]byte, len(nibbles)/2+1)
keyLE[0] = nibbles[0]
for i := 2; i < len(nibbles); i += 2 {
keyLE[i/2] = (nibbles[i-1] << 4 & 0xf0) | (nibbles[i] & 0xf)
}

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}
}
qdm12 marked this conversation as resolved.
Show resolved Hide resolved

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
}
142 changes: 142 additions & 0 deletions internal/trie/codec/nibbles_test.go
Original file line number Diff line number Diff line change
@@ -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)
})
}
}
50 changes: 50 additions & 0 deletions internal/trie/node/branch.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Copyright 2021 ChainSafe Systems (ON)
// SPDX-License-Identifier: LGPL-3.0-only

package node

import (
"fmt"
"sync"

"github.com/ChainSafe/gossamer/lib/common"
)

var _ Node = (*Branch)(nil)

// Branch is a branch in the trie.
type Branch struct {
Key []byte // partial key
Children [16]Node
Value []byte
// dirty is true when the branch differs
// from the node stored in the database.
dirty bool
hashDigest []byte
encoding []byte
// generation is incremented on every trie Snapshot() call.
// Each node also contain a certain generation number,
// which is updated to match the trie generation once they are
// inserted, moved or iterated over.
generation uint64
sync.RWMutex
}

// NewBranch creates a new branch using the arguments given.
func NewBranch(key, value []byte, dirty bool, generation uint64) *Branch {
return &Branch{
Key: key,
Value: value,
dirty: dirty,
generation: generation,
}
}

func (b *Branch) String() string {
if len(b.Value) > 1024 {
return fmt.Sprintf("branch key=0x%x childrenBitmap=%b value (hashed)=0x%x dirty=%t",
b.Key, b.ChildrenBitmap(), common.MustBlake2bHash(b.Value), b.dirty)
}
return fmt.Sprintf("branch key=0x%x childrenBitmap=%b value=0x%x dirty=%t",
b.Key, b.ChildrenBitmap(), b.Value, b.dirty)
}
Loading