From 1700e64a05a7868265bc7b3804f361244e82bb52 Mon Sep 17 00:00:00 2001 From: Quentin McGaw Date: Tue, 18 Oct 2022 07:35:02 +0000 Subject: [PATCH] chore(lib/trie): add `internal/trie/tracking` --- internal/trie/tracking/deltas.go | 56 +++ internal/trie/tracking/deltas_test.go | 182 ++++++++++ internal/trie/tracking/helpers_test.go | 37 ++ internal/trie/tracking/interfaces.go | 11 + lib/trie/database.go | 39 ++- lib/trie/helpers_test.go | 10 + lib/trie/interfaces.go | 32 ++ lib/trie/trie.go | 199 ++++++----- lib/trie/trie_endtoend_test.go | 13 +- lib/trie/trie_test.go | 449 ++++++++++++------------- 10 files changed, 685 insertions(+), 343 deletions(-) create mode 100644 internal/trie/tracking/deltas.go create mode 100644 internal/trie/tracking/deltas_test.go create mode 100644 internal/trie/tracking/helpers_test.go create mode 100644 internal/trie/tracking/interfaces.go create mode 100644 lib/trie/interfaces.go diff --git a/internal/trie/tracking/deltas.go b/internal/trie/tracking/deltas.go new file mode 100644 index 00000000000..4e80e37a20f --- /dev/null +++ b/internal/trie/tracking/deltas.go @@ -0,0 +1,56 @@ +// Copyright 2022 ChainSafe Systems (ON) +// SPDX-License-Identifier: LGPL-3.0-only + +package tracking + +import "github.com/ChainSafe/gossamer/lib/common" + +// Deltas tracks the trie deltas, for example deleted node hashes. +type Deltas struct { + deletedNodeHashes map[common.Hash]struct{} +} + +// New returns a new Deltas struct. +func New() *Deltas { + return &Deltas{ + deletedNodeHashes: make(map[common.Hash]struct{}), + } +} + +// RecordDeleted records a node hash as deleted. +func (d *Deltas) RecordDeleted(nodeHash common.Hash) { + d.deletedNodeHashes[nodeHash] = struct{}{} +} + +// Deleted returns a set (map) of all the recorded deleted +// node hashes. Note the map returned is not deep copied for +// performance reasons and so it's not safe for mutation. +func (d *Deltas) Deleted() (nodeHashes map[common.Hash]struct{}) { + return d.deletedNodeHashes +} + +// MergeWith merges the deltas given as argument in the receiving +// deltas struct. +func (d *Deltas) MergeWith(deltas DeletedGetter) { + for nodeHash := range deltas.Deleted() { + d.RecordDeleted(nodeHash) + } +} + +// DeepCopy returns a deep copy of the deltas. +func (d *Deltas) DeepCopy() (deepCopy *Deltas) { + if d == nil { + return nil + } + + deepCopy = &Deltas{} + + if d.deletedNodeHashes != nil { + deepCopy.deletedNodeHashes = make(map[common.Hash]struct{}, len(d.deletedNodeHashes)) + for nodeHash := range d.deletedNodeHashes { + deepCopy.deletedNodeHashes[nodeHash] = struct{}{} + } + } + + return deepCopy +} diff --git a/internal/trie/tracking/deltas_test.go b/internal/trie/tracking/deltas_test.go new file mode 100644 index 00000000000..59885dfac86 --- /dev/null +++ b/internal/trie/tracking/deltas_test.go @@ -0,0 +1,182 @@ +// Copyright 2022 ChainSafe Systems (ON) +// SPDX-License-Identifier: LGPL-3.0-only + +package tracking + +import ( + "testing" + + "github.com/ChainSafe/gossamer/lib/common" + "github.com/stretchr/testify/assert" +) + +func Test_New(t *testing.T) { + t.Parallel() + + deltas := New() + + expectedDeltas := &Deltas{ + deletedNodeHashes: make(map[common.Hash]struct{}), + } + assert.Equal(t, expectedDeltas, deltas) +} + +func Test_Deltas_RecordDeleted(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + deltas Deltas + nodeHash common.Hash + expectedDeltas Deltas + }{ + "set in empty deltas": { + deltas: Deltas{ + deletedNodeHashes: map[common.Hash]struct{}{}, + }, + nodeHash: common.Hash{1}, + expectedDeltas: Deltas{ + deletedNodeHashes: map[common.Hash]struct{}{{1}: {}}, + }, + }, + "set in non empty deltas": { + deltas: Deltas{ + deletedNodeHashes: map[common.Hash]struct{}{{1}: {}}, + }, + nodeHash: common.Hash{2}, + expectedDeltas: Deltas{ + deletedNodeHashes: map[common.Hash]struct{}{ + {1}: {}, {2}: {}, + }, + }, + }, + "override in deltas": { + deltas: Deltas{ + deletedNodeHashes: map[common.Hash]struct{}{{1}: {}}, + }, + nodeHash: common.Hash{1}, + expectedDeltas: Deltas{ + deletedNodeHashes: map[common.Hash]struct{}{{1}: {}}, + }, + }, + } + + for name, testCase := range testCases { + testCase := testCase + t.Run(name, func(t *testing.T) { + t.Parallel() + + testCase.deltas.RecordDeleted(testCase.nodeHash) + assert.Equal(t, testCase.expectedDeltas, testCase.deltas) + }) + } +} + +func Test_Deltas_Deleted(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + deltas Deltas + nodeHashes map[common.Hash]struct{} + }{ + "empty deltas": {}, + "non empty deltas": { + deltas: Deltas{ + deletedNodeHashes: map[common.Hash]struct{}{{1}: {}}, + }, + nodeHashes: map[common.Hash]struct{}{{1}: {}}, + }, + } + + for name, testCase := range testCases { + testCase := testCase + t.Run(name, func(t *testing.T) { + t.Parallel() + + nodeHashes := testCase.deltas.Deleted() + assert.Equal(t, testCase.nodeHashes, nodeHashes) + }) + } +} + +func Test_Deltas_MergeWith(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + deltas Deltas + deltasArg DeletedGetter + expectedDeltas Deltas + }{ + "merge empty deltas": { + deltas: Deltas{ + deletedNodeHashes: map[common.Hash]struct{}{{1}: {}}, + }, + deltasArg: &Deltas{}, + expectedDeltas: Deltas{ + deletedNodeHashes: map[common.Hash]struct{}{{1}: {}}, + }, + }, + "merge deltas": { + deltas: Deltas{ + deletedNodeHashes: map[common.Hash]struct{}{{1}: {}}, + }, + deltasArg: &Deltas{ + deletedNodeHashes: map[common.Hash]struct{}{ + {1}: {}, {2}: {}, + }, + }, + expectedDeltas: Deltas{ + deletedNodeHashes: map[common.Hash]struct{}{ + {1}: {}, {2}: {}, + }, + }, + }, + } + + for name, testCase := range testCases { + testCase := testCase + t.Run(name, func(t *testing.T) { + t.Parallel() + + testCase.deltas.MergeWith(testCase.deltasArg) + assert.Equal(t, testCase.expectedDeltas, testCase.deltas) + }) + } +} + +func Test_Deltas_DeepCopy(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + deltasOriginal *Deltas + deltasCopy *Deltas + }{ + "nil deltas": {}, + "empty deltas": { + deltasOriginal: &Deltas{}, + deltasCopy: &Deltas{}, + }, + "filled deltas": { + deltasOriginal: &Deltas{ + deletedNodeHashes: map[common.Hash]struct{}{{1}: {}}, + }, + deltasCopy: &Deltas{ + deletedNodeHashes: map[common.Hash]struct{}{{1}: {}}, + }, + }, + } + + for name, testCase := range testCases { + testCase := testCase + t.Run(name, func(t *testing.T) { + t.Parallel() + + deepCopy := testCase.deltasOriginal.DeepCopy() + + assert.Equal(t, testCase.deltasCopy, deepCopy) + assertPointersNotEqual(t, testCase.deltasOriginal, deepCopy) + if testCase.deltasOriginal != nil { + assertPointersNotEqual(t, testCase.deltasOriginal.deletedNodeHashes, deepCopy.deletedNodeHashes) + } + }) + } +} diff --git a/internal/trie/tracking/helpers_test.go b/internal/trie/tracking/helpers_test.go new file mode 100644 index 00000000000..d4318aa1ab8 --- /dev/null +++ b/internal/trie/tracking/helpers_test.go @@ -0,0 +1,37 @@ +// Copyright 2022 ChainSafe Systems (ON) +// SPDX-License-Identifier: LGPL-3.0-only + +package tracking + +import ( + "reflect" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func getPointer(x interface{}) (pointer uintptr, ok bool) { + func() { + defer func() { + ok = recover() == nil + }() + valueOfX := reflect.ValueOf(x) + pointer = valueOfX.Pointer() + }() + return pointer, ok +} + +func assertPointersNotEqual(t *testing.T, a, b interface{}) { + t.Helper() + pointerA, okA := getPointer(a) + pointerB, okB := getPointer(b) + require.Equal(t, okA, okB) + + switch { + case pointerA == 0 && pointerB == 0: // nil and nil + case okA: + assert.NotEqual(t, pointerA, pointerB) + default: // values like `int` + } +} diff --git a/internal/trie/tracking/interfaces.go b/internal/trie/tracking/interfaces.go new file mode 100644 index 00000000000..0e4ea324fd0 --- /dev/null +++ b/internal/trie/tracking/interfaces.go @@ -0,0 +1,11 @@ +// Copyright 2022 ChainSafe Systems (ON) +// SPDX-License-Identifier: LGPL-3.0-only + +package tracking + +import "github.com/ChainSafe/gossamer/lib/common" + +// DeletedGetter gets deleted node hashes. +type DeletedGetter interface { + Deleted() (nodeHashes map[common.Hash]struct{}) +} diff --git a/lib/trie/database.go b/lib/trie/database.go index c2ca4bde702..1fbfa6c8530 100644 --- a/lib/trie/database.go +++ b/lib/trie/database.go @@ -217,6 +217,37 @@ func PopulateNodeHashes(n *Node, nodeHashes map[string]struct{}) { } } +// recordAllDeleted records the node hashes of the given node and all its descendants. +// Note it does not record inlined nodes. +// It is assumed the node and its descendant nodes have their Merkle value already +// computed, or the function will panic. +func recordAllDeleted(n *Node, recorder DeltaRecorder) { + if n == nil { + return + } + + if len(n.MerkleValue) == 0 { + panic(fmt.Sprintf("node with key 0x%x has no Merkle value computed", n.Key)) + } + + isInlined := len(n.MerkleValue) < 32 + if isInlined { + return + } + + nodeHash := common.NewHash(n.MerkleValue) + recorder.RecordDeleted(nodeHash) + + if n.Kind() == node.Leaf { + return + } + + branch := n + for _, child := range branch.Children { + recordAllDeleted(child, recorder) + } +} + // GetFromDB retrieves a value at the given key from the trie using the database. // It recursively descends into the trie using the database starting // from the root node until it reaches the node with the given key. @@ -419,9 +450,11 @@ func (t *Trie) getInsertedNodeHashesAtNode(n *Node, merkleValues map[string]stru // node that was deleted from the trie since the last snapshot was made. // The returned set is a copy of the internal set to prevent data corruption. func (t *Trie) GetDeletedMerkleValues() (merkleValues map[string]struct{}) { - merkleValues = make(map[string]struct{}, len(t.deletedMerkleValues)) - for k := range t.deletedMerkleValues { - merkleValues[k] = struct{}{} + deletedNodeHashes := t.deltas.Deleted() + // TODO return deletedNodeHashes directly after changing MerkleValue -> NodeHash + merkleValues = make(map[string]struct{}, len(deletedNodeHashes)) + for nodeHash := range deletedNodeHashes { + merkleValues[string(nodeHash[:])] = struct{}{} } return merkleValues } diff --git a/lib/trie/helpers_test.go b/lib/trie/helpers_test.go index 8e5b3baa169..d1d098ac771 100644 --- a/lib/trie/helpers_test.go +++ b/lib/trie/helpers_test.go @@ -10,6 +10,8 @@ import ( "time" "github.com/ChainSafe/gossamer/internal/trie/node" + "github.com/ChainSafe/gossamer/internal/trie/tracking" + "github.com/ChainSafe/gossamer/lib/common" "github.com/stretchr/testify/require" ) @@ -144,3 +146,11 @@ func checkMerkleValuesAreSet(t *testing.T, n *Node) { checkMerkleValuesAreSet(t, child) } } + +func newDeltas(deleted []common.Hash) (deltas *tracking.Deltas) { + deltas = tracking.New() + for _, hash := range deleted { + deltas.RecordDeleted(hash) + } + return deltas +} diff --git a/lib/trie/interfaces.go b/lib/trie/interfaces.go new file mode 100644 index 00000000000..452b46dbcc8 --- /dev/null +++ b/lib/trie/interfaces.go @@ -0,0 +1,32 @@ +// Copyright 2022 ChainSafe Systems (ON) +// SPDX-License-Identifier: LGPL-3.0-only + +package trie + +import ( + "github.com/ChainSafe/gossamer/internal/trie/tracking" + "github.com/ChainSafe/gossamer/lib/common" +) + +// Deltas is the interface for the trie local deltas since +// the last snapshot. +type Deltas interface { + DeltaMerger + DeltaDeletedGetter +} + +// DeltaMerger merges the given deltas into the current +// deltas. +type DeltaMerger interface { + MergeWith(deltas tracking.DeletedGetter) +} + +// DeltaDeletedGetter returns the deleted node hashes recorded so far. +type DeltaDeletedGetter interface { + Deleted() (nodeHashes map[common.Hash]struct{}) +} + +// DeltaRecorder records deltas done in a ongoing trie operation. +type DeltaRecorder interface { + RecordDeleted(nodeHash common.Hash) +} diff --git a/lib/trie/trie.go b/lib/trie/trie.go index d1cdf33dfb7..9c4df59c4a1 100644 --- a/lib/trie/trie.go +++ b/lib/trie/trie.go @@ -10,6 +10,7 @@ import ( "github.com/ChainSafe/gossamer/internal/trie/codec" "github.com/ChainSafe/gossamer/internal/trie/node" "github.com/ChainSafe/gossamer/internal/trie/pools" + "github.com/ChainSafe/gossamer/internal/trie/tracking" "github.com/ChainSafe/gossamer/lib/common" ) @@ -21,11 +22,12 @@ type Trie struct { generation uint64 root *Node childTries map[common.Hash]*Trie - // deletedMerkleValues are the node Merkle values that were deleted - // from this trie since the last snapshot. These are used by the online - // pruner to detect with database keys (trie node Merkle values) can + // deltas stores trie deltas since the last trie snapshot. + // For example node hashes that were deleted since + // the last snapshot. These are used by the online + // pruner to detect with database keys (trie node hashes) can // be deleted. - deletedMerkleValues map[string]struct{} + deltas Deltas } // NewEmptyTrie creates a trie with a nil root @@ -36,10 +38,10 @@ func NewEmptyTrie() *Trie { // NewTrie creates a trie with an existing root node func NewTrie(root *Node) *Trie { return &Trie{ - root: root, - childTries: make(map[common.Hash]*Trie), - generation: 0, // Initially zero but increases after every snapshot. - deletedMerkleValues: make(map[string]struct{}), + root: root, + childTries: make(map[common.Hash]*Trie), + generation: 0, // Initially zero but increases after every snapshot. + deltas: tracking.New(), } } @@ -54,45 +56,42 @@ func (t *Trie) Snapshot() (newTrie *Trie) { rootCopySettings.CopyCached = true for rootHash, childTrie := range t.childTries { childTries[rootHash] = &Trie{ - generation: childTrie.generation + 1, - root: childTrie.root.Copy(rootCopySettings), - deletedMerkleValues: make(map[string]struct{}), + generation: childTrie.generation + 1, + root: childTrie.root.Copy(rootCopySettings), + deltas: tracking.New(), } } return &Trie{ - generation: t.generation + 1, - root: t.root, - childTries: childTries, - deletedMerkleValues: make(map[string]struct{}), + generation: t.generation + 1, + root: t.root, + childTries: childTries, + deltas: tracking.New(), } } // handleTrackedDeltas sets the pending deleted Merkle values in // the trie deleted merkle values set if and only if success is true. -func (t *Trie) handleTrackedDeltas(success bool, pendingDeletedMerkleValues map[string]struct{}) { +func (t *Trie) handleTrackedDeltas(success bool, pendingDeltas DeltaDeletedGetter) { if !success || t.generation == 0 { // Do not persist tracked deleted node hashes if the operation failed or // if the trie generation is zero (first block, no trie snapshot done yet). return } - for merkleValue := range pendingDeletedMerkleValues { - t.deletedMerkleValues[merkleValue] = struct{}{} - } + t.deltas.MergeWith(pendingDeltas) } func (t *Trie) prepForMutation(currentNode *Node, copySettings node.CopySettings, - pendingDeletedMerkleValues map[string]struct{}) ( + pendingDeltas DeltaRecorder) ( newNode *Node, err error) { if currentNode.Generation == t.generation { // no need to track deleted node, deep copy the node and // update the node generation. newNode = currentNode } else { - err = t.registerDeletedMerkleValue(currentNode, - pendingDeletedMerkleValues) + err = t.registerDeletedMerkleValue(currentNode, pendingDeltas) if err != nil { return nil, fmt.Errorf("registering deleted node: %w", err) } @@ -104,7 +103,7 @@ func (t *Trie) prepForMutation(currentNode *Node, } func (t *Trie) registerDeletedMerkleValue(node *Node, - pendingDeletedMerkleValues map[string]struct{}) (err error) { + pendingDeltas DeltaRecorder) (err error) { isRoot := node == t.root err = ensureMerkleValueIsCalculated(node, isRoot) if err != nil { @@ -121,7 +120,8 @@ func (t *Trie) registerDeletedMerkleValue(node *Node, if !node.Dirty { // Only register deleted nodes that were not previously modified // since the last trie snapshot. - pendingDeletedMerkleValues[string(node.MerkleValue)] = struct{}{} + nodeHash := common.NewHash(node.MerkleValue) + pendingDeltas.RecordDeleted(nodeHash) } return nil @@ -141,11 +141,10 @@ func (t *Trie) DeepCopy() (trieCopy *Trie) { generation: t.generation, } - if t.deletedMerkleValues != nil { - trieCopy.deletedMerkleValues = make(map[string]struct{}, len(t.deletedMerkleValues)) - for k := range t.deletedMerkleValues { - trieCopy.deletedMerkleValues[k] = struct{}{} - } + if t.deltas != nil { + // Because DeepCopy() is only used in tests (in this and other packages), + // it's fine to type assert deltas to access its DeepCopy method. + trieCopy.deltas = t.deltas.(*tracking.Deltas).DeepCopy() } if t.childTries != nil { @@ -335,18 +334,18 @@ func findNextKeyChild(children []*Node, startIndex byte, // Put inserts a value into the trie at the // key specified in little Endian format. func (t *Trie) Put(keyLE, value []byte) (err error) { - pendingDeletedMerkleValues := make(map[string]struct{}) + pendingDeltas := tracking.New() defer func() { const success = true - t.handleTrackedDeltas(success, pendingDeletedMerkleValues) + t.handleTrackedDeltas(success, pendingDeltas) }() - return t.insertKeyLE(keyLE, value, pendingDeletedMerkleValues) + return t.insertKeyLE(keyLE, value, pendingDeltas) } func (t *Trie) insertKeyLE(keyLE, value []byte, - deletedMerkleValues map[string]struct{}) (err error) { + pendingDeltas DeltaRecorder) (err error) { nibblesKey := codec.KeyLEToNibbles(keyLE) - root, _, _, err := t.insert(t.root, nibblesKey, value, deletedMerkleValues) + root, _, _, err := t.insert(t.root, nibblesKey, value, pendingDeltas) if err != nil { return err } @@ -357,7 +356,7 @@ func (t *Trie) insertKeyLE(keyLE, value []byte, // insert inserts a value in the trie at the key specified. // It may create one or more new nodes or update an existing node. func (t *Trie) insert(parent *Node, key, value []byte, - deletedMerkleValues map[string]struct{}) (newParent *Node, + pendingDeltas DeltaRecorder) (newParent *Node, mutated bool, nodesCreated uint32, err error) { if parent == nil { mutated = true @@ -374,7 +373,7 @@ func (t *Trie) insert(parent *Node, key, value []byte, if parent.Kind() == node.Branch { newParent, mutated, nodesCreated, err = t.insertInBranch( - parent, key, value, deletedMerkleValues) + parent, key, value, pendingDeltas) if err != nil { // `insertInBranch` may call `insert` so do not wrap the // error since this may be a deep recursive call. @@ -384,7 +383,7 @@ func (t *Trie) insert(parent *Node, key, value []byte, } newParent, mutated, nodesCreated, err = t.insertInLeaf( - parent, key, value, deletedMerkleValues) + parent, key, value, pendingDeltas) if err != nil { return nil, false, 0, fmt.Errorf("inserting in leaf: %w", err) } @@ -393,7 +392,7 @@ func (t *Trie) insert(parent *Node, key, value []byte, } func (t *Trie) insertInLeaf(parentLeaf *Node, key, value []byte, - deletedMerkleValues map[string]struct{}) ( + pendingDeltas DeltaRecorder) ( newParent *Node, mutated bool, nodesCreated uint32, err error) { if bytes.Equal(parentLeaf.Key, key) { nodesCreated = 0 @@ -404,7 +403,7 @@ func (t *Trie) insertInLeaf(parentLeaf *Node, key, value []byte, copySettings := node.DefaultCopySettings copySettings.CopyValue = false - parentLeaf, err = t.prepForMutation(parentLeaf, copySettings, deletedMerkleValues) + parentLeaf, err = t.prepForMutation(parentLeaf, copySettings, pendingDeltas) if err != nil { return nil, false, 0, fmt.Errorf("preparing leaf for mutation: %w", err) } @@ -436,7 +435,7 @@ func (t *Trie) insertInLeaf(parentLeaf *Node, key, value []byte, childIndex := parentLeafKey[commonPrefixLength] newParentLeafKey := parentLeaf.Key[commonPrefixLength+1:] if !bytes.Equal(parentLeaf.Key, newParentLeafKey) { - parentLeaf, err = t.prepForMutation(parentLeaf, copySettings, deletedMerkleValues) + parentLeaf, err = t.prepForMutation(parentLeaf, copySettings, pendingDeltas) if err != nil { return nil, false, 0, fmt.Errorf("preparing leaf for mutation: %w", err) } @@ -459,7 +458,7 @@ func (t *Trie) insertInLeaf(parentLeaf *Node, key, value []byte, childIndex := parentLeafKey[commonPrefixLength] newParentLeafKey := parentLeaf.Key[commonPrefixLength+1:] if !bytes.Equal(parentLeaf.Key, newParentLeafKey) { - parentLeaf, err = t.prepForMutation(parentLeaf, copySettings, deletedMerkleValues) + parentLeaf, err = t.prepForMutation(parentLeaf, copySettings, pendingDeltas) if err != nil { return nil, false, 0, fmt.Errorf("preparing leaf for mutation: %w", err) } @@ -483,7 +482,7 @@ func (t *Trie) insertInLeaf(parentLeaf *Node, key, value []byte, } func (t *Trie) insertInBranch(parentBranch *Node, key, value []byte, - deletedMerkleValues map[string]struct{}) ( + pendingDeltas DeltaRecorder) ( newParent *Node, mutated bool, nodesCreated uint32, err error) { copySettings := node.DefaultCopySettings @@ -492,7 +491,7 @@ func (t *Trie) insertInBranch(parentBranch *Node, key, value []byte, mutated = false return parentBranch, mutated, 0, nil } - parentBranch, err = t.prepForMutation(parentBranch, copySettings, deletedMerkleValues) + parentBranch, err = t.prepForMutation(parentBranch, copySettings, pendingDeltas) if err != nil { return nil, false, 0, fmt.Errorf("preparing branch for mutation: %w", err) } @@ -516,7 +515,7 @@ func (t *Trie) insertInBranch(parentBranch *Node, key, value []byte, Dirty: true, } nodesCreated = 1 - parentBranch, err = t.prepForMutation(parentBranch, copySettings, deletedMerkleValues) + parentBranch, err = t.prepForMutation(parentBranch, copySettings, pendingDeltas) if err != nil { return nil, false, 0, fmt.Errorf("preparing branch for mutation: %w", err) } @@ -526,7 +525,7 @@ func (t *Trie) insertInBranch(parentBranch *Node, key, value []byte, return parentBranch, mutated, nodesCreated, nil } - child, mutated, nodesCreated, err = t.insert(child, remainingKey, value, deletedMerkleValues) + child, mutated, nodesCreated, err = t.insert(child, remainingKey, value, pendingDeltas) if err != nil { // do not wrap error since `insert` may call `insertInBranch` recursively return nil, false, 0, err @@ -534,7 +533,7 @@ func (t *Trie) insertInBranch(parentBranch *Node, key, value []byte, return parentBranch, mutated, 0, nil } - parentBranch, err = t.prepForMutation(parentBranch, copySettings, deletedMerkleValues) + parentBranch, err = t.prepForMutation(parentBranch, copySettings, pendingDeltas) if err != nil { return nil, false, 0, fmt.Errorf("preparing branch for mutation: %w", err) } @@ -560,7 +559,7 @@ func (t *Trie) insertInBranch(parentBranch *Node, key, value []byte, remainingOldParentKey := parentBranch.Key[commonPrefixLength+1:] // Note: parentBranch.Key != remainingOldParentKey - parentBranch, err = t.prepForMutation(parentBranch, copySettings, deletedMerkleValues) + parentBranch, err = t.prepForMutation(parentBranch, copySettings, pendingDeltas) if err != nil { return nil, false, 0, fmt.Errorf("preparing branch for mutation: %w", err) } @@ -576,7 +575,7 @@ func (t *Trie) insertInBranch(parentBranch *Node, key, value []byte, remainingKey := key[commonPrefixLength+1:] var additionalNodesCreated uint32 newParentBranch.Children[childIndex], _, additionalNodesCreated, err = t.insert( - nil, remainingKey, value, deletedMerkleValues) + nil, remainingKey, value, pendingDeltas) if err != nil { // do not wrap error since `insert` may call `insertInBranch` recursively return nil, false, 0, err @@ -595,9 +594,9 @@ func (t *Trie) insertInBranch(parentBranch *Node, key, value []byte, func LoadFromMap(data map[string]string) (trie Trie, err error) { trie = *NewEmptyTrie() - pendingDeletedMerkleValues := make(map[string]struct{}) + pendingDeltas := tracking.New() defer func() { - trie.handleTrackedDeltas(err == nil, pendingDeletedMerkleValues) + trie.handleTrackedDeltas(err == nil, pendingDeltas) }() for key, value := range data { @@ -611,7 +610,7 @@ func LoadFromMap(data map[string]string) (trie Trie, err error) { return Trie{}, fmt.Errorf("cannot convert value hex to bytes: %w", err) } - err = trie.insertKeyLE(keyLEBytes, valueBytes, pendingDeletedMerkleValues) + err = trie.insertKeyLE(keyLEBytes, valueBytes, pendingDeltas) if err != nil { return Trie{}, fmt.Errorf("inserting key value pair in trie: %w", err) } @@ -770,10 +769,10 @@ func retrieveFromBranch(branch *Node, key []byte) (value []byte) { // within the limit. func (t *Trie) ClearPrefixLimit(prefixLE []byte, limit uint32) ( deleted uint32, allDeleted bool, err error) { - pendingDeletedMerkleValues := make(map[string]struct{}) + pendingDeltas := tracking.New() defer func() { const success = true - t.handleTrackedDeltas(success, pendingDeletedMerkleValues) + t.handleTrackedDeltas(success, pendingDeltas) }() if limit == 0 { @@ -784,7 +783,7 @@ func (t *Trie) ClearPrefixLimit(prefixLE []byte, limit uint32) ( prefix = bytes.TrimSuffix(prefix, []byte{0}) root, deleted, _, allDeleted, err := t.clearPrefixLimitAtNode( - t.root, prefix, limit, pendingDeletedMerkleValues) + t.root, prefix, limit, pendingDeltas) if err != nil { // Note: no need to wrap the error really since the private function has // the same name as the exported function `ClearPrefixLimit`. @@ -799,7 +798,7 @@ func (t *Trie) ClearPrefixLimit(prefixLE []byte, limit uint32) ( // It returns the updated node newParent, the number of deleted values valuesDeleted and the // allDeleted boolean indicating if there is no key left with the prefix. func (t *Trie) clearPrefixLimitAtNode(parent *Node, prefix []byte, - limit uint32, deletedMerkleValues map[string]struct{}) ( + limit uint32, pendingDeltas DeltaRecorder) ( newParent *Node, valuesDeleted, nodesRemoved uint32, allDeleted bool, err error) { if parent == nil { return nil, 0, 0, true, nil @@ -810,7 +809,7 @@ func (t *Trie) clearPrefixLimitAtNode(parent *Node, prefix []byte, // TODO check this is the same behaviour as in substrate const allDeleted = true if bytes.HasPrefix(parent.Key, prefix) { - err = t.registerDeletedMerkleValue(parent, deletedMerkleValues) + err = t.registerDeletedMerkleValue(parent, pendingDeltas) if err != nil { return nil, 0, 0, false, fmt.Errorf("registering deleted Merkle value: %w", err) @@ -824,17 +823,17 @@ func (t *Trie) clearPrefixLimitAtNode(parent *Node, prefix []byte, // Note: `clearPrefixLimitBranch` may call `clearPrefixLimitAtNode` so do not wrap // the error since that could be a deep recursive call. - return t.clearPrefixLimitBranch(parent, prefix, limit, deletedMerkleValues) + return t.clearPrefixLimitBranch(parent, prefix, limit, pendingDeltas) } func (t *Trie) clearPrefixLimitBranch(branch *Node, prefix []byte, limit uint32, - deletedMerkleValues map[string]struct{}) ( + pendingDeltas DeltaRecorder) ( newParent *Node, valuesDeleted, nodesRemoved uint32, allDeleted bool, err error) { newParent = branch if bytes.HasPrefix(branch.Key, prefix) { newParent, valuesDeleted, nodesRemoved, err = t.deleteNodesLimit( - branch, limit, deletedMerkleValues) + branch, limit, pendingDeltas) if err != nil { return nil, 0, 0, false, fmt.Errorf("deleting nodes: %w", err) } @@ -845,7 +844,7 @@ func (t *Trie) clearPrefixLimitBranch(branch *Node, prefix []byte, limit uint32, if len(prefix) == len(branch.Key)+1 && bytes.HasPrefix(branch.Key, prefix[:len(prefix)-1]) { // Prefix is one the children of the branch - return t.clearPrefixLimitChild(branch, prefix, limit, deletedMerkleValues) + return t.clearPrefixLimitChild(branch, prefix, limit, pendingDeltas) } noPrefixForNode := len(prefix) <= len(branch.Key) || @@ -861,7 +860,7 @@ func (t *Trie) clearPrefixLimitBranch(branch *Node, prefix []byte, limit uint32, child := branch.Children[childIndex] child, valuesDeleted, nodesRemoved, allDeleted, err = t.clearPrefixLimitAtNode( - child, childPrefix, limit, deletedMerkleValues) + child, childPrefix, limit, pendingDeltas) if err != nil { return nil, 0, 0, false, fmt.Errorf("clearing prefix limit at node: %w", err) } else if valuesDeleted == 0 { @@ -869,14 +868,14 @@ func (t *Trie) clearPrefixLimitBranch(branch *Node, prefix []byte, limit uint32, } copySettings := node.DefaultCopySettings - branch, err = t.prepForMutation(branch, copySettings, deletedMerkleValues) + branch, err = t.prepForMutation(branch, copySettings, pendingDeltas) if err != nil { return nil, 0, 0, false, fmt.Errorf("preparing branch for mutation: %w", err) } branch.Children[childIndex] = child branch.Descendants -= nodesRemoved - newParent, branchChildMerged, err := t.handleDeletion(branch, prefix, deletedMerkleValues) + newParent, branchChildMerged, err := t.handleDeletion(branch, prefix, pendingDeltas) if err != nil { return nil, 0, 0, false, fmt.Errorf("handling deletion: %w", err) } @@ -889,7 +888,7 @@ func (t *Trie) clearPrefixLimitBranch(branch *Node, prefix []byte, limit uint32, } func (t *Trie) clearPrefixLimitChild(branch *Node, prefix []byte, limit uint32, - deletedMerkleValues map[string]struct{}) ( + pendingDeltas DeltaRecorder) ( newParent *Node, valuesDeleted, nodesRemoved uint32, allDeleted bool, err error) { newParent = branch @@ -904,7 +903,7 @@ func (t *Trie) clearPrefixLimitChild(branch *Node, prefix []byte, limit uint32, } child, valuesDeleted, nodesRemoved, err = t.deleteNodesLimit( - child, limit, deletedMerkleValues) + child, limit, pendingDeltas) if err != nil { // Note: do not wrap error since this is recursive. return nil, 0, 0, false, err @@ -916,7 +915,7 @@ func (t *Trie) clearPrefixLimitChild(branch *Node, prefix []byte, limit uint32, } copySettings := node.DefaultCopySettings - branch, err = t.prepForMutation(branch, copySettings, deletedMerkleValues) + branch, err = t.prepForMutation(branch, copySettings, pendingDeltas) if err != nil { return nil, 0, 0, false, fmt.Errorf("preparing branch for mutation: %w", err) } @@ -924,7 +923,7 @@ func (t *Trie) clearPrefixLimitChild(branch *Node, prefix []byte, limit uint32, branch.Children[childIndex] = child branch.Descendants -= nodesRemoved - newParent, branchChildMerged, err := t.handleDeletion(branch, prefix, deletedMerkleValues) + newParent, branchChildMerged, err := t.handleDeletion(branch, prefix, pendingDeltas) if err != nil { return nil, 0, 0, false, fmt.Errorf("handling deletion: %w", err) } @@ -938,7 +937,7 @@ func (t *Trie) clearPrefixLimitChild(branch *Node, prefix []byte, limit uint32, } func (t *Trie) deleteNodesLimit(parent *Node, limit uint32, - deletedMerkleValues map[string]struct{}) ( + pendingDeltas DeltaRecorder) ( newParent *Node, valuesDeleted, nodesRemoved uint32, err error) { if limit == 0 { valuesDeleted, nodesRemoved = 0, 0 @@ -951,7 +950,7 @@ func (t *Trie) deleteNodesLimit(parent *Node, limit uint32, } if parent.Kind() == node.Leaf { - err = t.registerDeletedMerkleValue(parent, deletedMerkleValues) + err = t.registerDeletedMerkleValue(parent, pendingDeltas) if err != nil { return nil, 0, 0, fmt.Errorf("registering deleted merkle value: %w", err) } @@ -969,7 +968,7 @@ func (t *Trie) deleteNodesLimit(parent *Node, limit uint32, // Note: there is at least one non-nil child and the limit isn't zero, // therefore it is safe to prepare the branch for mutation. copySettings := node.DefaultCopySettings - branch, err = t.prepForMutation(branch, copySettings, deletedMerkleValues) + branch, err = t.prepForMutation(branch, copySettings, pendingDeltas) if err != nil { return nil, 0, 0, fmt.Errorf("preparing branch for mutation: %w", err) } @@ -982,7 +981,7 @@ func (t *Trie) deleteNodesLimit(parent *Node, limit uint32, } branch.Children[i], newDeleted, newNodesRemoved, err = t.deleteNodesLimit( - child, limit, deletedMerkleValues) + child, limit, pendingDeltas) if err != nil { // `deleteNodesLimit` is recursive, so do not wrap error. return nil, 0, 0, err @@ -996,7 +995,7 @@ func (t *Trie) deleteNodesLimit(parent *Node, limit uint32, nodesRemoved += newNodesRemoved branch.Descendants -= newNodesRemoved - newParent, branchChildMerged, err = t.handleDeletion(branch, branch.Key, deletedMerkleValues) + newParent, branchChildMerged, err = t.handleDeletion(branch, branch.Key, pendingDeltas) if err != nil { return nil, 0, 0, fmt.Errorf("handling deletion: %w", err) } @@ -1026,10 +1025,10 @@ func (t *Trie) deleteNodesLimit(parent *Node, limit uint32, // ClearPrefix deletes all nodes in the trie for which the key contains the // prefix given in little Endian format. func (t *Trie) ClearPrefix(prefixLE []byte) (err error) { - pendingDeletedMerkleValues := make(map[string]struct{}) + pendingDeltas := tracking.New() defer func() { const success = true - t.handleTrackedDeltas(success, pendingDeletedMerkleValues) + t.handleTrackedDeltas(success, pendingDeltas) }() if len(prefixLE) == 0 { @@ -1039,7 +1038,7 @@ func (t *Trie) ClearPrefix(prefixLE []byte) (err error) { return fmt.Errorf("ensuring Merkle values are calculated: %w", err) } - PopulateNodeHashes(t.root, pendingDeletedMerkleValues) + recordAllDeleted(t.root, pendingDeltas) t.root = nil return nil } @@ -1047,7 +1046,7 @@ func (t *Trie) ClearPrefix(prefixLE []byte) (err error) { prefix := codec.KeyLEToNibbles(prefixLE) prefix = bytes.TrimSuffix(prefix, []byte{0}) - root, _, err := t.clearPrefixAtNode(t.root, prefix, pendingDeletedMerkleValues) + root, _, err := t.clearPrefixAtNode(t.root, prefix, pendingDeltas) if err != nil { return fmt.Errorf("clearing prefix at root node: %w", err) } @@ -1057,7 +1056,7 @@ func (t *Trie) ClearPrefix(prefixLE []byte) (err error) { } func (t *Trie) clearPrefixAtNode(parent *Node, prefix []byte, - deletedMerkleValues map[string]struct{}) ( + pendingDeltas DeltaRecorder) ( newParent *Node, nodesRemoved uint32, err error) { if parent == nil { const nodesRemoved = 0 @@ -1072,7 +1071,7 @@ func (t *Trie) clearPrefixAtNode(parent *Node, prefix []byte, return parent, nodesRemoved, fmt.Errorf("ensuring Merkle values are calculated: %w", err) } - PopulateNodeHashes(parent, deletedMerkleValues) + recordAllDeleted(parent, pendingDeltas) nodesRemoved = 1 + parent.Descendants return nil, nodesRemoved, nil } @@ -1096,12 +1095,12 @@ func (t *Trie) clearPrefixAtNode(parent *Node, prefix []byte, nodesRemoved = 1 + child.Descendants copySettings := node.DefaultCopySettings - branch, err = t.prepForMutation(branch, copySettings, deletedMerkleValues) + branch, err = t.prepForMutation(branch, copySettings, pendingDeltas) if err != nil { return nil, 0, fmt.Errorf("preparing branch for mutation: %w", err) } - err = t.registerDeletedMerkleValue(child, deletedMerkleValues) + err = t.registerDeletedMerkleValue(child, pendingDeltas) if err != nil { return nil, 0, fmt.Errorf("registering deleted merkle value for child: %w", err) } @@ -1109,7 +1108,7 @@ func (t *Trie) clearPrefixAtNode(parent *Node, prefix []byte, branch.Children[childIndex] = nil branch.Descendants -= nodesRemoved var branchChildMerged bool - newParent, branchChildMerged, err = t.handleDeletion(branch, prefix, deletedMerkleValues) + newParent, branchChildMerged, err = t.handleDeletion(branch, prefix, pendingDeltas) if err != nil { return nil, 0, fmt.Errorf("handling deletion: %w", err) } @@ -1131,7 +1130,7 @@ func (t *Trie) clearPrefixAtNode(parent *Node, prefix []byte, childPrefix := prefix[len(branch.Key)+1:] child := branch.Children[childIndex] - child, nodesRemoved, err = t.clearPrefixAtNode(child, childPrefix, deletedMerkleValues) + child, nodesRemoved, err = t.clearPrefixAtNode(child, childPrefix, pendingDeltas) if err != nil { nodesRemoved = 0 // Note: do not wrap error since this is recursive @@ -1141,14 +1140,14 @@ func (t *Trie) clearPrefixAtNode(parent *Node, prefix []byte, } copySettings := node.DefaultCopySettings - branch, err = t.prepForMutation(branch, copySettings, deletedMerkleValues) + branch, err = t.prepForMutation(branch, copySettings, pendingDeltas) if err != nil { return nil, 0, fmt.Errorf("preparing branch for mutation: %w", err) } branch.Descendants -= nodesRemoved branch.Children[childIndex] = child - newParent, branchChildMerged, err := t.handleDeletion(branch, prefix, deletedMerkleValues) + newParent, branchChildMerged, err := t.handleDeletion(branch, prefix, pendingDeltas) if err != nil { return nil, 0, fmt.Errorf("handling deletion: %w", err) } @@ -1164,14 +1163,14 @@ func (t *Trie) clearPrefixAtNode(parent *Node, prefix []byte, // matching the key given in little Endian format. // If no node is found at this key, nothing is deleted. func (t *Trie) Delete(keyLE []byte) (err error) { - pendingDeletedMerkleValues := make(map[string]struct{}) + pendingDeltas := tracking.New() defer func() { const success = true - t.handleTrackedDeltas(success, pendingDeletedMerkleValues) + t.handleTrackedDeltas(success, pendingDeltas) }() key := codec.KeyLEToNibbles(keyLE) - root, _, _, err := t.deleteAtNode(t.root, key, pendingDeletedMerkleValues) + root, _, _, err := t.deleteAtNode(t.root, key, pendingDeltas) if err != nil { return fmt.Errorf("deleting key %x: %w", keyLE, err) } @@ -1180,7 +1179,7 @@ func (t *Trie) Delete(keyLE []byte) (err error) { } func (t *Trie) deleteAtNode(parent *Node, key []byte, - deletedMerkleValues map[string]struct{}) ( + pendingDeltas DeltaRecorder) ( newParent *Node, deleted bool, nodesRemoved uint32, err error) { if parent == nil { const nodesRemoved = 0 @@ -1188,7 +1187,7 @@ func (t *Trie) deleteAtNode(parent *Node, key []byte, } if parent.Kind() == node.Leaf { - newParent, err = t.deleteLeaf(parent, key, deletedMerkleValues) + newParent, err = t.deleteLeaf(parent, key, pendingDeltas) if err != nil { return nil, false, 0, fmt.Errorf("deleting leaf: %w", err) } @@ -1201,7 +1200,7 @@ func (t *Trie) deleteAtNode(parent *Node, key []byte, return parent, false, nodesRemoved, nil } - newParent, deleted, nodesRemoved, err = t.deleteBranch(parent, key, deletedMerkleValues) + newParent, deleted, nodesRemoved, err = t.deleteBranch(parent, key, pendingDeltas) if err != nil { return nil, false, 0, fmt.Errorf("deleting branch: %w", err) } @@ -1210,7 +1209,7 @@ func (t *Trie) deleteAtNode(parent *Node, key []byte, } func (t *Trie) deleteLeaf(parent *Node, key []byte, - deletedMerkleValues map[string]struct{}) ( + pendingDeltas DeltaRecorder) ( newParent *Node, err error) { if len(key) > 0 && !bytes.Equal(key, parent.Key) { return parent, nil @@ -1218,7 +1217,7 @@ func (t *Trie) deleteLeaf(parent *Node, key []byte, newParent = nil - err = t.registerDeletedMerkleValue(parent, deletedMerkleValues) + err = t.registerDeletedMerkleValue(parent, pendingDeltas) if err != nil { return nil, fmt.Errorf("registering deleted merkle value: %w", err) } @@ -1227,12 +1226,12 @@ func (t *Trie) deleteLeaf(parent *Node, key []byte, } func (t *Trie) deleteBranch(branch *Node, key []byte, - deletedMerkleValues map[string]struct{}) ( + pendingDeltas DeltaRecorder) ( newParent *Node, deleted bool, nodesRemoved uint32, err error) { if len(key) == 0 || bytes.Equal(branch.Key, key) { copySettings := node.DefaultCopySettings copySettings.CopyValue = false - branch, err = t.prepForMutation(branch, copySettings, deletedMerkleValues) + branch, err = t.prepForMutation(branch, copySettings, pendingDeltas) if err != nil { return nil, false, 0, fmt.Errorf("preparing branch for mutation: %w", err) } @@ -1242,7 +1241,7 @@ func (t *Trie) deleteBranch(branch *Node, key []byte, branch.SubValue = nil deleted = true var branchChildMerged bool - newParent, branchChildMerged, err = t.handleDeletion(branch, key, deletedMerkleValues) + newParent, branchChildMerged, err = t.handleDeletion(branch, key, pendingDeltas) if err != nil { return nil, false, 0, fmt.Errorf("handling deletion: %w", err) } @@ -1262,7 +1261,7 @@ func (t *Trie) deleteBranch(branch *Node, key []byte, childKey := key[commonPrefixLength+1:] child := branch.Children[childIndex] - newChild, deleted, nodesRemoved, err := t.deleteAtNode(child, childKey, deletedMerkleValues) + newChild, deleted, nodesRemoved, err := t.deleteAtNode(child, childKey, pendingDeltas) if err != nil { // deleteAtNode may call deleteBranch so don't wrap the error // since this may be a recursive call. @@ -1275,7 +1274,7 @@ func (t *Trie) deleteBranch(branch *Node, key []byte, } copySettings := node.DefaultCopySettings - branch, err = t.prepForMutation(branch, copySettings, deletedMerkleValues) + branch, err = t.prepForMutation(branch, copySettings, pendingDeltas) if err != nil { return nil, false, 0, fmt.Errorf("preparing branch for mutation: %w", err) } @@ -1283,7 +1282,7 @@ func (t *Trie) deleteBranch(branch *Node, key []byte, branch.Descendants -= nodesRemoved branch.Children[childIndex] = newChild - newParent, branchChildMerged, err := t.handleDeletion(branch, key, deletedMerkleValues) + newParent, branchChildMerged, err := t.handleDeletion(branch, key, pendingDeltas) if err != nil { return nil, false, 0, fmt.Errorf("handling deletion: %w", err) } @@ -1302,7 +1301,7 @@ func (t *Trie) deleteBranch(branch *Node, key []byte, // of one node in callers. // If the branch has a value and no child, it will be changed into a leaf. func (t *Trie) handleDeletion(branch *Node, key []byte, - deletedMerkleValues map[string]struct{}) ( + pendingDeltas DeltaRecorder) ( newNode *Node, branchChildMerged bool, err error) { childrenCount := 0 firstChildIndex := -1 @@ -1339,7 +1338,7 @@ func (t *Trie) handleDeletion(branch *Node, key []byte, const branchChildMerged = true childIndex := firstChildIndex child := branch.Children[firstChildIndex] - err = t.registerDeletedMerkleValue(child, deletedMerkleValues) + err = t.registerDeletedMerkleValue(child, pendingDeltas) if err != nil { return nil, false, fmt.Errorf("registering deleted merkle value: %w", err) } diff --git a/lib/trie/trie_endtoend_test.go b/lib/trie/trie_endtoend_test.go index 6d532331c9c..c2923b9bcdf 100644 --- a/lib/trie/trie_endtoend_test.go +++ b/lib/trie/trie_endtoend_test.go @@ -315,20 +315,23 @@ func TestTrieDiff(t *testing.T) { newTrie.Put(test.key, test.value) } - deletedMerkleValues := newTrie.deletedMerkleValues - expectedDeletedMerkleValues := map[string]struct{}{ + deletedMerkleValues := newTrie.deltas.Deleted() + expectedDeletdMerkleValues := map[common.Hash]struct{}{ // root branch Merkle value which was modified (by its descendants). // Other nodes result in an encoding of less than 32B so they are not // tracked since they are inlined in the branch. - "\xa9v\xfaUme$<\x03\x80\x89\xd4\x15\r\xb1\x9a䶊`\xe5M\xeah\x9c\xab\xbf\xbb\xc0\xfcrH": {}, + {0xa9, 0x76, 0xfa, 0x55, 0x6d, 0x65, 0x24, 0x3c, + 0x3, 0x80, 0x89, 0xd4, 0x15, 0xd, 0xb1, 0x9a, + 0xe4, 0xb6, 0x8a, 0x60, 0xe5, 0x4d, 0xea, 0x68, + 0x9c, 0xab, 0xbf, 0xbb, 0xc0, 0xfc, 0x72, 0x48}: {}, } - assert.Equal(t, expectedDeletedMerkleValues, deletedMerkleValues) + assert.Equal(t, expectedDeletdMerkleValues, deletedMerkleValues) err = newTrie.WriteDirty(storageDB) require.NoError(t, err) for deletedMerkleValue := range deletedMerkleValues { - err = storageDB.Del([]byte(deletedMerkleValue)) + err = storageDB.Del(deletedMerkleValue[:]) require.NoError(t, err) } diff --git a/lib/trie/trie_test.go b/lib/trie/trie_test.go index 45ff36c116f..4027e10a7ca 100644 --- a/lib/trie/trie_test.go +++ b/lib/trie/trie_test.go @@ -6,10 +6,11 @@ package trie import ( "bytes" "encoding/hex" - "reflect" + reflect "reflect" "testing" "github.com/ChainSafe/gossamer/internal/trie/node" + "github.com/ChainSafe/gossamer/internal/trie/tracking" "github.com/ChainSafe/gossamer/lib/common" gomock "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" @@ -18,8 +19,8 @@ import ( func Test_NewEmptyTrie(t *testing.T) { expectedTrie := &Trie{ - childTries: make(map[common.Hash]*Trie), - deletedMerkleValues: map[string]struct{}{}, + childTries: make(map[common.Hash]*Trie), + deltas: tracking.New(), } trie := NewEmptyTrie() assert.Equal(t, expectedTrie, trie) @@ -35,8 +36,8 @@ func Test_NewTrie(t *testing.T) { Key: []byte{0}, SubValue: []byte{17}, }, - childTries: make(map[common.Hash]*Trie), - deletedMerkleValues: map[string]struct{}{}, + childTries: make(map[common.Hash]*Trie), + deltas: tracking.New(), } trie := NewTrie(root) assert.Equal(t, expectedTrie, trie) @@ -45,6 +46,9 @@ func Test_NewTrie(t *testing.T) { func Test_Trie_Snapshot(t *testing.T) { t.Parallel() + emptyDeltas := newDeltas(nil) + setDeltas := newDeltas([]common.Hash{{1}}) + trie := &Trie{ generation: 8, root: &Node{Key: []byte{8}, SubValue: []byte{1}}, @@ -52,22 +56,15 @@ func Test_Trie_Snapshot(t *testing.T) { {1}: { generation: 1, root: &Node{Key: []byte{1}, SubValue: []byte{1}}, - deletedMerkleValues: map[string]struct{}{ - "a": {}, - }, + deltas: setDeltas, }, {2}: { generation: 2, root: &Node{Key: []byte{2}, SubValue: []byte{1}}, - deletedMerkleValues: map[string]struct{}{ - "b": {}, - }, + deltas: setDeltas, }, }, - deletedMerkleValues: map[string]struct{}{ - "a": {}, - "b": {}, - }, + deltas: setDeltas, } expectedTrie := &Trie{ @@ -75,17 +72,17 @@ func Test_Trie_Snapshot(t *testing.T) { root: &Node{Key: []byte{8}, SubValue: []byte{1}}, childTries: map[common.Hash]*Trie{ {1}: { - generation: 2, - root: &Node{Key: []byte{1}, SubValue: []byte{1}}, - deletedMerkleValues: map[string]struct{}{}, + generation: 2, + root: &Node{Key: []byte{1}, SubValue: []byte{1}}, + deltas: emptyDeltas, }, {2}: { - generation: 3, - root: &Node{Key: []byte{2}, SubValue: []byte{1}}, - deletedMerkleValues: map[string]struct{}{}, + generation: 3, + root: &Node{Key: []byte{2}, SubValue: []byte{1}}, + deltas: emptyDeltas, }, }, - deletedMerkleValues: map[string]struct{}{}, + deltas: emptyDeltas, } newTrie := trie.Snapshot() @@ -97,62 +94,42 @@ func Test_Trie_handleTrackedDeltas(t *testing.T) { t.Parallel() testCases := map[string]struct { - trie Trie - success bool - pendingDeletedMerkleValues map[string]struct{} - expectedTrie Trie + trie Trie + success bool + pendingDeltas DeltaDeletedGetter + expectedTrie Trie }{ "no success and generation 1": { trie: Trie{ generation: 1, - deletedMerkleValues: map[string]struct{}{ - "a": {}, - }, - }, - pendingDeletedMerkleValues: map[string]struct{}{ - "b": {}, + deltas: newDeltas([]common.Hash{{1}}), }, + pendingDeltas: newDeltas([]common.Hash{{2}}), expectedTrie: Trie{ generation: 1, - deletedMerkleValues: map[string]struct{}{ - "a": {}, - }, + deltas: newDeltas([]common.Hash{{1}}), }, }, "success and generation 0": { trie: Trie{ - deletedMerkleValues: map[string]struct{}{ - "a": {}, - }, - }, - success: true, - pendingDeletedMerkleValues: map[string]struct{}{ - "b": {}, + deltas: newDeltas([]common.Hash{{1}}), }, + success: true, + pendingDeltas: newDeltas([]common.Hash{{2}}), expectedTrie: Trie{ - deletedMerkleValues: map[string]struct{}{ - "a": {}, - }, + deltas: newDeltas([]common.Hash{{1}}), }, }, "success and generation 1": { trie: Trie{ generation: 1, - deletedMerkleValues: map[string]struct{}{ - "a": {}, - }, - }, - success: true, - pendingDeletedMerkleValues: map[string]struct{}{ - "a": {}, - "b": {}, + deltas: newDeltas([]common.Hash{{1}}), }, + success: true, + pendingDeltas: newDeltas([]common.Hash{{1}, {2}}), expectedTrie: Trie{ generation: 1, - deletedMerkleValues: map[string]struct{}{ - "a": {}, - "b": {}, - }, + deltas: newDeltas([]common.Hash{{1}, {2}}), }, }, } @@ -163,7 +140,7 @@ func Test_Trie_handleTrackedDeltas(t *testing.T) { t.Parallel() trie := testCase.trie - trie.handleTrackedDeltas(testCase.success, testCase.pendingDeletedMerkleValues) + trie.handleTrackedDeltas(testCase.success, testCase.pendingDeltas) assert.Equal(t, testCase.expectedTrie, trie) }) @@ -174,15 +151,15 @@ func Test_Trie_prepForMutation(t *testing.T) { t.Parallel() testCases := map[string]struct { - trie Trie - currentNode *Node - copySettings node.CopySettings - pendingDeletedMerkleValues map[string]struct{} - newNode *Node - copied bool - errSentinel error - errMessage string - expectedPendingDeletedMerkleValues map[string]struct{} + trie Trie + currentNode *Node + copySettings node.CopySettings + pendingDeltas DeltaRecorder + newNode *Node + copied bool + errSentinel error + errMessage string + expectedPendingDeltas DeltaRecorder }{ "no update": { trie: Trie{ @@ -219,7 +196,7 @@ func Test_Trie_prepForMutation(t *testing.T) { trie: Trie{ generation: 2, }, - pendingDeletedMerkleValues: map[string]struct{}{}, + pendingDeltas: newDeltas(nil), currentNode: &Node{ Generation: 1, Key: []byte{1}, @@ -241,9 +218,10 @@ func Test_Trie_prepForMutation(t *testing.T) { Dirty: true, }, copied: true, - expectedPendingDeletedMerkleValues: map[string]struct{}{ - "\x98\xfc\xd6k\xa3\x12\u009e\xf1\x93\x05/\xd0\xc1Ln8\xb1X\xbd\\\x025\x06E\x94\xca\xcc\x1a\xb5\x96]": {}, - }, + expectedPendingDeltas: newDeltas([]common.Hash{{ + 0x98, 0xfc, 0xd6, 0x6b, 0xa3, 0x12, 0xc2, 0x9e, 0xf1, 0x93, 0x5, 0x2f, 0xd0, 0xc1, 0x4c, 0x6e, + 0x38, 0xb1, 0x58, 0xbd, 0x5c, 0x2, 0x35, 0x6, 0x45, 0x94, 0xca, 0xcc, 0x1a, 0xb5, 0x96, 0x5d, + }}), }, } @@ -256,14 +234,14 @@ func Test_Trie_prepForMutation(t *testing.T) { expectedTrie := *testCase.trie.DeepCopy() newNode, err := trie.prepForMutation(testCase.currentNode, testCase.copySettings, - testCase.pendingDeletedMerkleValues) + testCase.pendingDeltas) require.ErrorIs(t, err, testCase.errSentinel) if testCase.errSentinel != nil { assert.EqualError(t, err, testCase.errMessage) } assert.Equal(t, testCase.newNode, newNode) - assert.Equal(t, testCase.expectedPendingDeletedMerkleValues, testCase.pendingDeletedMerkleValues) + assert.Equal(t, testCase.expectedPendingDeltas, testCase.pendingDeltas) assert.Equal(t, expectedTrie, trie) // Check for deep copy @@ -288,22 +266,25 @@ func Test_Trie_registerDeletedMerkleValue(t *testing.T) { } testCases := map[string]struct { - trie Trie - node *Node - pendingDeletedMerkleValues map[string]struct{} - expectedPendingDeletedMerkleValues map[string]struct{} - expectedTrie Trie + trie Trie + node *Node + pendingDeltas DeltaRecorder + expectedPendingDeltas DeltaRecorder + expectedTrie Trie }{ "dirty node not registered": { node: &Node{Dirty: true}, }, "clean root node registered": { - node: someSmallNode, - trie: Trie{root: someSmallNode}, - pendingDeletedMerkleValues: map[string]struct{}{}, - expectedPendingDeletedMerkleValues: map[string]struct{}{ - "`Qm\v\xb6\xe1\xbb\xfb\x12\x93\xf1\xb2v\xea\x95\x05\xe9\xf4\xa4\xe7ُb\r\x05\x11^\v\x85'J\xe1": {}, - }, + node: someSmallNode, + trie: Trie{root: someSmallNode}, + pendingDeltas: newDeltas(nil), + expectedPendingDeltas: newDeltas([]common.Hash{{ + 0x60, 0x51, 0x6d, 0x0b, 0xb6, 0xe1, 0xbb, 0xfb, + 0x12, 0x93, 0xf1, 0xb2, 0x76, 0xea, 0x95, 0x05, + 0xe9, 0xf4, 0xa4, 0xe7, 0xd9, 0x8f, 0x62, 0x0d, + 0x05, 0x11, 0x5e, 0x0b, 0x85, 0x27, 0x4a, 0xe1, + }}), expectedTrie: Trie{ root: &Node{ Key: []byte{1}, @@ -332,10 +313,11 @@ func Test_Trie_registerDeletedMerkleValue(t *testing.T) { 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32}, }, - pendingDeletedMerkleValues: map[string]struct{}{}, - expectedPendingDeletedMerkleValues: map[string]struct{}{ - "\x98\xfc\xd6k\xa3\x12\u009e\xf1\x93\x05/\xd0\xc1Ln8\xb1X\xbd\\\x025\x06E\x94\xca\xcc\x1a\xb5\x96]": {}, - }, + pendingDeltas: newDeltas(nil), + expectedPendingDeltas: newDeltas([]common.Hash{{ + 0x98, 0xfc, 0xd6, 0x6b, 0xa3, 0x12, 0xc2, 0x9e, 0xf1, 0x93, 0x5, 0x2f, 0xd0, 0xc1, 0x4c, 0x6e, + 0x38, 0xb1, 0x58, 0xbd, 0x5c, 0x2, 0x35, 0x6, 0x45, 0x94, 0xca, 0xcc, 0x1a, 0xb5, 0x96, 0x5d, + }}), }, } @@ -347,10 +329,10 @@ func Test_Trie_registerDeletedMerkleValue(t *testing.T) { trie := testCase.trie err := trie.registerDeletedMerkleValue(testCase.node, - testCase.pendingDeletedMerkleValues) + testCase.pendingDeltas) require.NoError(t, err) - assert.Equal(t, testCase.expectedPendingDeletedMerkleValues, testCase.pendingDeletedMerkleValues) + assert.Equal(t, testCase.expectedPendingDeltas, testCase.pendingDeltas) assert.Equal(t, testCase.expectedTrie, trie) }) } @@ -389,7 +371,7 @@ func testTrieForDeepCopy(t *testing.T, original, copy *Trie) { return } assertPointersNotEqual(t, original.generation, copy.generation) - assertPointersNotEqual(t, original.deletedMerkleValues, copy.deletedMerkleValues) + assertPointersNotEqual(t, original.deltas, copy.deltas) assertPointersNotEqual(t, original.childTries, copy.childTries) for hashKey, childTrie := range copy.childTries { originalChildTrie := original.childTries[hashKey] @@ -418,16 +400,10 @@ func Test_Trie_DeepCopy(t *testing.T) { {1, 2, 3}: { generation: 2, root: &Node{Key: []byte{1}, SubValue: []byte{1}}, - deletedMerkleValues: map[string]struct{}{ - "a": {}, - "b": {}, - }, + deltas: newDeltas([]common.Hash{{1}, {2}}), }, }, - deletedMerkleValues: map[string]struct{}{ - "a": {}, - "b": {}, - }, + deltas: newDeltas([]common.Hash{{1}, {2}}), }, trieCopy: &Trie{ generation: 1, @@ -436,16 +412,10 @@ func Test_Trie_DeepCopy(t *testing.T) { {1, 2, 3}: { generation: 2, root: &Node{Key: []byte{1}, SubValue: []byte{1}}, - deletedMerkleValues: map[string]struct{}{ - "a": {}, - "b": {}, - }, + deltas: newDeltas([]common.Hash{{1}, {2}}), }, }, - deletedMerkleValues: map[string]struct{}{ - "a": {}, - "b": {}, - }, + deltas: newDeltas([]common.Hash{{1}, {2}}), }, }, } @@ -1200,8 +1170,8 @@ func Test_Trie_Put(t *testing.T) { }{ "trie with key and value": { trie: Trie{ - generation: 1, - deletedMerkleValues: map[string]struct{}{}, + generation: 1, + deltas: newDeltas(nil), root: &Node{ Key: []byte{1, 2, 0, 5}, SubValue: []byte{1}, @@ -1211,9 +1181,12 @@ func Test_Trie_Put(t *testing.T) { value: []byte{2}, expectedTrie: Trie{ generation: 1, - deletedMerkleValues: map[string]struct{}{ - "\xa1\x95\b\x9c>\x8f\x8b[6\x97\x87\x00\xad\x95J\xed\x99\xe0\x84\x13\xcf\xc1\xe2\xb4\xc0\n]\x06J\xbef\xa9": {}, - }, + deltas: newDeltas([]common.Hash{ + { + 0xa1, 0x95, 0x08, 0x9c, 0x3e, 0x8f, 0x8b, 0x5b, 0x36, 0x97, 0x87, 0x00, 0xad, 0x95, 0x4a, 0xed, + 0x99, 0xe0, 0x84, 0x13, 0xcf, 0xc1, 0xe2, 0xb4, 0xc0, 0x0a, 0x5d, 0x06, 0x4a, 0xbe, 0x66, 0xa9, + }, + }), root: &Node{ Key: []byte{1, 2}, Generation: 1, @@ -1255,15 +1228,15 @@ func Test_Trie_insert(t *testing.T) { t.Parallel() testCases := map[string]struct { - trie Trie - parent *Node - key []byte - value []byte - deletedMerkleValues map[string]struct{} - newNode *Node - mutated bool - nodesCreated uint32 - expectedDeletedMerkleValues map[string]struct{} + trie Trie + parent *Node + key []byte + value []byte + pendingDeltas DeltaRecorder + newNode *Node + mutated bool + nodesCreated uint32 + expectedPendingDeltas DeltaRecorder }{ "nil parent": { trie: Trie{ @@ -1473,14 +1446,14 @@ func Test_Trie_insert(t *testing.T) { newNode, mutated, nodesCreated, err := trie.insert( testCase.parent, testCase.key, testCase.value, - testCase.deletedMerkleValues) + testCase.pendingDeltas) require.NoError(t, err) assert.Equal(t, testCase.newNode, newNode) assert.Equal(t, testCase.mutated, mutated) assert.Equal(t, testCase.nodesCreated, nodesCreated) assert.Equal(t, expectedTrie, trie) - assert.Equal(t, testCase.expectedDeletedMerkleValues, testCase.deletedMerkleValues) + assert.Equal(t, testCase.expectedPendingDeltas, testCase.pendingDeltas) }) } } @@ -1489,16 +1462,16 @@ func Test_Trie_insertInBranch(t *testing.T) { t.Parallel() testCases := map[string]struct { - parent *Node - key []byte - value []byte - deletedMerkleValues map[string]struct{} - newNode *Node - mutated bool - nodesCreated uint32 - errSentinel error - errMessage string - expectedDeletedMerkleValues map[string]struct{} + parent *Node + key []byte + value []byte + pendingDeltas DeltaRecorder + newNode *Node + mutated bool + nodesCreated uint32 + errSentinel error + errMessage string + expectedPendingDeltas DeltaRecorder }{ "insert existing value to branch": { parent: &Node{ @@ -1772,7 +1745,7 @@ func Test_Trie_insertInBranch(t *testing.T) { newNode, mutated, nodesCreated, err := trie.insertInBranch( testCase.parent, testCase.key, testCase.value, - testCase.deletedMerkleValues) + testCase.pendingDeltas) assert.ErrorIs(t, err, testCase.errSentinel) if testCase.errSentinel != nil { @@ -1782,7 +1755,7 @@ func Test_Trie_insertInBranch(t *testing.T) { assert.Equal(t, testCase.mutated, mutated) assert.Equal(t, testCase.nodesCreated, nodesCreated) assert.Equal(t, new(Trie), trie) // check no mutation - assert.Equal(t, testCase.expectedDeletedMerkleValues, testCase.deletedMerkleValues) + assert.Equal(t, testCase.expectedPendingDeltas, testCase.pendingDeltas) }) } } @@ -1798,15 +1771,15 @@ func Test_LoadFromMap(t *testing.T) { }{ "nil data": { expectedTrie: Trie{ - childTries: map[common.Hash]*Trie{}, - deletedMerkleValues: map[string]struct{}{}, + childTries: map[common.Hash]*Trie{}, + deltas: newDeltas(nil), }, }, "empty data": { data: map[string]string{}, expectedTrie: Trie{ - childTries: map[common.Hash]*Trie{}, - deletedMerkleValues: map[string]struct{}{}, + childTries: map[common.Hash]*Trie{}, + deltas: newDeltas(nil), }, }, "bad key": { @@ -1838,8 +1811,8 @@ func Test_LoadFromMap(t *testing.T) { }, Dirty: true, }, - childTries: map[common.Hash]*Trie{}, - deletedMerkleValues: map[string]struct{}{}, + childTries: map[common.Hash]*Trie{}, + deltas: newDeltas(nil), }, }, "load key values": { @@ -1868,8 +1841,8 @@ func Test_LoadFromMap(t *testing.T) { }, }), }, - childTries: map[common.Hash]*Trie{}, - deletedMerkleValues: map[string]struct{}{}, + childTries: map[common.Hash]*Trie{}, + deltas: newDeltas(nil), }, }, } @@ -2385,18 +2358,18 @@ func Test_Trie_clearPrefixLimitAtNode(t *testing.T) { t.Parallel() testCases := map[string]struct { - trie Trie - parent *Node - prefix []byte - limit uint32 - deletedMerkleValues map[string]struct{} - newParent *Node - valuesDeleted uint32 - nodesRemoved uint32 - allDeleted bool - errSentinel error - errMessage string - expectedDeletedMerkleValues map[string]struct{} + trie Trie + parent *Node + prefix []byte + limit uint32 + pendingDeltas DeltaRecorder + newParent *Node + valuesDeleted uint32 + nodesRemoved uint32 + allDeleted bool + errSentinel error + errMessage string + expectedPendingDeltas DeltaRecorder }{ "limit is zero": { allDeleted: true, @@ -2923,7 +2896,7 @@ func Test_Trie_clearPrefixLimitAtNode(t *testing.T) { newParent, valuesDeleted, nodesRemoved, allDeleted, err := trie.clearPrefixLimitAtNode(testCase.parent, testCase.prefix, - testCase.limit, testCase.deletedMerkleValues) + testCase.limit, testCase.pendingDeltas) assert.ErrorIs(t, err, testCase.errSentinel) if testCase.errSentinel != nil { @@ -2934,7 +2907,7 @@ func Test_Trie_clearPrefixLimitAtNode(t *testing.T) { assert.Equal(t, testCase.nodesRemoved, nodesRemoved) assert.Equal(t, testCase.allDeleted, allDeleted) assert.Equal(t, expectedTrie, trie) - assert.Equal(t, testCase.expectedDeletedMerkleValues, testCase.deletedMerkleValues) + assert.Equal(t, testCase.expectedPendingDeltas, testCase.pendingDeltas) }) } } @@ -2943,16 +2916,16 @@ func Test_Trie_deleteNodesLimit(t *testing.T) { t.Parallel() testCases := map[string]struct { - trie Trie - parent *Node - limit uint32 - deletedMerkleValues map[string]struct{} - newNode *Node - valuesDeleted uint32 - nodesRemoved uint32 - errSentinel error - errMessage string - expectedDeletedMerkleValues map[string]struct{} + trie Trie + parent *Node + limit uint32 + pendingDeltas DeltaRecorder + newNode *Node + valuesDeleted uint32 + nodesRemoved uint32 + errSentinel error + errMessage string + expectedPendingDeltas DeltaRecorder }{ "zero limit": { trie: Trie{ @@ -3110,7 +3083,7 @@ func Test_Trie_deleteNodesLimit(t *testing.T) { newNode, valuesDeleted, nodesRemoved, err := trie.deleteNodesLimit(testCase.parent, - testCase.limit, testCase.deletedMerkleValues) + testCase.limit, testCase.pendingDeltas) assert.ErrorIs(t, err, testCase.errSentinel) if testCase.errSentinel != nil { @@ -3120,7 +3093,7 @@ func Test_Trie_deleteNodesLimit(t *testing.T) { assert.Equal(t, testCase.valuesDeleted, valuesDeleted) assert.Equal(t, testCase.nodesRemoved, nodesRemoved) assert.Equal(t, expectedTrie, trie) - assert.Equal(t, testCase.expectedDeletedMerkleValues, testCase.deletedMerkleValues) + assert.Equal(t, testCase.expectedPendingDeltas, testCase.pendingDeltas) }) } } @@ -3135,29 +3108,31 @@ func Test_Trie_ClearPrefix(t *testing.T) { }{ "nil prefix": { trie: Trie{ - root: &Node{SubValue: []byte{1}}, - generation: 1, - deletedMerkleValues: map[string]struct{}{}, + root: &Node{SubValue: []byte{1}}, + generation: 1, + deltas: newDeltas(nil), }, expectedTrie: Trie{ generation: 1, - deletedMerkleValues: map[string]struct{}{ - "\xf9jt\x15\"\xbc\xc1O\n\xea/p`DR$\x1dY\xb5\xf2ݫ\x9aiH\xfd\xb3\xfe\xf5\xf9\x86C": {}, - }, + deltas: newDeltas([]common.Hash{{ + 0xf9, 0x6a, 0x74, 0x15, 0x22, 0xbc, 0xc1, 0x4f, 0x0a, 0xea, 0x2f, 0x70, 0x60, 0x44, 0x52, 0x24, + 0x1d, 0x59, 0xb5, 0xf2, 0xdd, 0xab, 0x9a, 0x69, 0x48, 0xfd, 0xb3, 0xfe, 0xf5, 0xf9, 0x86, 0x43, + }}), }, }, "empty prefix": { trie: Trie{ - root: &Node{SubValue: []byte{1}}, - generation: 1, - deletedMerkleValues: map[string]struct{}{}, + root: &Node{SubValue: []byte{1}}, + generation: 1, + deltas: newDeltas(nil), }, prefix: []byte{}, expectedTrie: Trie{ generation: 1, - deletedMerkleValues: map[string]struct{}{ - "\xf9jt\x15\"\xbc\xc1O\n\xea/p`DR$\x1dY\xb5\xf2ݫ\x9aiH\xfd\xb3\xfe\xf5\xf9\x86C": {}, - }, + deltas: newDeltas([]common.Hash{{ + 0xf9, 0x6a, 0x74, 0x15, 0x22, 0xbc, 0xc1, 0x4f, 0x0a, 0xea, 0x2f, 0x70, 0x60, 0x44, 0x52, 0x24, + 0x1d, 0x59, 0xb5, 0xf2, 0xdd, 0xab, 0x9a, 0x69, 0x48, 0xfd, 0xb3, 0xfe, 0xf5, 0xf9, 0x86, 0x43, + }}), }, }, "empty trie": { @@ -3186,7 +3161,7 @@ func Test_Trie_ClearPrefix(t *testing.T) { }, }), }, - deletedMerkleValues: map[string]struct{}{}, + deltas: newDeltas(nil), }, prefix: []byte{0x12, 0x16}, expectedTrie: Trie{ @@ -3197,9 +3172,10 @@ func Test_Trie_ClearPrefix(t *testing.T) { Generation: 1, Dirty: true, }, - deletedMerkleValues: map[string]struct{}{ - "_\xe1\b\xc8=\b2\x93S֑\x8e\x01\x04\xda̝!\x87\xfd\x9d\xaf\xa5\x82\xd1\xc52\xe5\xfe{.P": {}, - }, + deltas: newDeltas([]common.Hash{{ + 0x5f, 0xe1, 0x08, 0xc8, 0x3d, 0x08, 0x32, 0x93, 0x53, 0xd6, 0x91, 0x8e, 0x01, 0x04, 0xda, 0xcc, + 0x9d, 0x21, 0x87, 0xfd, 0x9d, 0xaf, 0xa5, 0x82, 0xd1, 0xc5, 0x32, 0xe5, 0xfe, 0x7b, 0x2e, 0x50, + }}), }, }, } @@ -3228,14 +3204,14 @@ func Test_Trie_clearPrefixAtNode(t *testing.T) { t.Parallel() testCases := map[string]struct { - trie Trie - parent *Node - prefix []byte - deletedMerkleValues map[string]struct{} - newParent *Node - nodesRemoved uint32 - expectedTrie Trie - expectedDeletedMerkleValues map[string]struct{} + trie Trie + parent *Node + prefix []byte + pendingDeltas DeltaRecorder + newParent *Node + nodesRemoved uint32 + expectedTrie Trie + expectedPendingDeltas DeltaRecorder }{ "delete one of two children of branch": { trie: Trie{ @@ -3547,13 +3523,13 @@ func Test_Trie_clearPrefixAtNode(t *testing.T) { trie := testCase.trie newParent, nodesRemoved, err := trie.clearPrefixAtNode( - testCase.parent, testCase.prefix, testCase.deletedMerkleValues) + testCase.parent, testCase.prefix, testCase.pendingDeltas) require.NoError(t, err) assert.Equal(t, testCase.newParent, newParent) assert.Equal(t, testCase.nodesRemoved, nodesRemoved) assert.Equal(t, testCase.expectedTrie, trie) - assert.Equal(t, testCase.expectedDeletedMerkleValues, testCase.deletedMerkleValues) + assert.Equal(t, testCase.expectedPendingDeltas, testCase.pendingDeltas) }) } } @@ -3568,28 +3544,30 @@ func Test_Trie_Delete(t *testing.T) { }{ "nil key": { trie: Trie{ - root: &Node{SubValue: []byte{1}}, - generation: 1, - deletedMerkleValues: map[string]struct{}{}, + root: &Node{SubValue: []byte{1}}, + generation: 1, + deltas: newDeltas(nil), }, expectedTrie: Trie{ generation: 1, - deletedMerkleValues: map[string]struct{}{ - "\xf9jt\x15\"\xbc\xc1O\n\xea/p`DR$\x1dY\xb5\xf2ݫ\x9aiH\xfd\xb3\xfe\xf5\xf9\x86C": {}, - }, + deltas: newDeltas([]common.Hash{{ + 0xf9, 0x6a, 0x74, 0x15, 0x22, 0xbc, 0xc1, 0x4f, 0x0a, 0xea, 0x2f, 0x70, 0x60, 0x44, 0x52, 0x24, + 0x1d, 0x59, 0xb5, 0xf2, 0xdd, 0xab, 0x9a, 0x69, 0x48, 0xfd, 0xb3, 0xfe, 0xf5, 0xf9, 0x86, 0x43, + }}), }, }, "empty key": { trie: Trie{ - root: &Node{SubValue: []byte{1}}, - generation: 1, - deletedMerkleValues: map[string]struct{}{}, + root: &Node{SubValue: []byte{1}}, + generation: 1, + deltas: newDeltas(nil), }, expectedTrie: Trie{ generation: 1, - deletedMerkleValues: map[string]struct{}{ - "\xf9jt\x15\"\xbc\xc1O\n\xea/p`DR$\x1dY\xb5\xf2ݫ\x9aiH\xfd\xb3\xfe\xf5\xf9\x86C": {}, - }, + deltas: newDeltas([]common.Hash{{ + 0xf9, 0x6a, 0x74, 0x15, 0x22, 0xbc, 0xc1, 0x4f, 0x0a, 0xea, 0x2f, 0x70, 0x60, 0x44, 0x52, 0x24, + 0x1d, 0x59, 0xb5, 0xf2, 0xdd, 0xab, 0x9a, 0x69, 0x48, 0xfd, 0xb3, 0xfe, 0xf5, 0xf9, 0x86, 0x43, + }}), }, }, "empty trie": { @@ -3619,7 +3597,7 @@ func Test_Trie_Delete(t *testing.T) { }, }), }, - deletedMerkleValues: map[string]struct{}{}, + deltas: newDeltas(nil), }, key: []byte{0x12, 0x16}, expectedTrie: Trie{ @@ -3644,9 +3622,10 @@ func Test_Trie_Delete(t *testing.T) { }, }), }, - deletedMerkleValues: map[string]struct{}{ - "=\x1b=r~\xe4\x04T\x9a]%1\xaa\xb9\xff\xf0\xee\xddŋ\xc3\v\xfe/\xe8+\x1a\f\xfe~v\xd5": {}, - }, + deltas: newDeltas([]common.Hash{{ + 0x3d, 0x1b, 0x3d, 0x72, 0x7e, 0xe4, 0x04, 0x54, 0x9a, 0x5d, 0x25, 0x31, 0xaa, 0xb9, 0xff, 0xf0, + 0xee, 0xdd, 0xc5, 0x8b, 0xc3, 0x0b, 0xfe, 0x2f, 0xe8, 0x2b, 0x1a, 0x0c, 0xfe, 0x7e, 0x76, 0xd5, + }}), }, }, } @@ -3675,17 +3654,17 @@ func Test_Trie_deleteAtNode(t *testing.T) { t.Parallel() testCases := map[string]struct { - trie Trie - parent *Node - key []byte - deletedMerkleValues map[string]struct{} - newParent *Node - updated bool - nodesRemoved uint32 - errSentinel error - errMessage string - expectedTrie Trie - expectedDeletedMerkleValues map[string]struct{} + trie Trie + parent *Node + key []byte + pendingDeltas DeltaRecorder + newParent *Node + updated bool + nodesRemoved uint32 + errSentinel error + errMessage string + expectedTrie Trie + expectedPendingDeltas DeltaRecorder }{ "nil parent": { key: []byte{1}, @@ -4016,7 +3995,7 @@ func Test_Trie_deleteAtNode(t *testing.T) { } newParent, updated, nodesRemoved, err := testCase.trie.deleteAtNode( - testCase.parent, testCase.key, testCase.deletedMerkleValues) + testCase.parent, testCase.key, testCase.pendingDeltas) assert.ErrorIs(t, err, testCase.errSentinel) if testCase.errSentinel != nil { @@ -4027,7 +4006,7 @@ func Test_Trie_deleteAtNode(t *testing.T) { assert.Equal(t, testCase.nodesRemoved, nodesRemoved) assert.Equal(t, testCase.expectedTrie, testCase.trie) assert.Equal(t, expectedKey, testCase.key) - assert.Equal(t, testCase.expectedDeletedMerkleValues, testCase.deletedMerkleValues) + assert.Equal(t, testCase.expectedPendingDeltas, testCase.pendingDeltas) }) } } @@ -4036,15 +4015,15 @@ func Test_Trie_handleDeletion(t *testing.T) { t.Parallel() testCases := map[string]struct { - trie Trie - branch *Node - deletedKey []byte - deletedMerkleValues map[string]struct{} - newNode *Node - branchChildMerged bool - errSentinel error - errMessage string - expectedDeletedMerkleValues map[string]struct{} + trie Trie + branch *Node + deletedKey []byte + pendingDeltas DeltaRecorder + newNode *Node + branchChildMerged bool + errSentinel error + errMessage string + expectedPendingDeltas DeltaRecorder }{ "branch with value and without children": { branch: &Node{ @@ -4162,7 +4141,7 @@ func Test_Trie_handleDeletion(t *testing.T) { expectedTrie := *trie.DeepCopy() newNode, branchChildMerged, err := trie.handleDeletion( - testCase.branch, testCase.deletedKey, testCase.deletedMerkleValues) + testCase.branch, testCase.deletedKey, testCase.pendingDeltas) assert.ErrorIs(t, err, testCase.errSentinel) if testCase.errSentinel != nil { @@ -4172,7 +4151,7 @@ func Test_Trie_handleDeletion(t *testing.T) { assert.Equal(t, testCase.newNode, newNode) assert.Equal(t, testCase.branchChildMerged, branchChildMerged) assert.Equal(t, expectedKey, testCase.deletedKey) - assert.Equal(t, testCase.expectedDeletedMerkleValues, testCase.deletedMerkleValues) + assert.Equal(t, testCase.expectedPendingDeltas, testCase.pendingDeltas) assert.Equal(t, expectedTrie, trie) }) }