From ee8b8318aee108b9d343caf14e3c87a2d1f74d14 Mon Sep 17 00:00:00 2001 From: Dan Laine Date: Mon, 17 Apr 2023 20:57:05 -0400 Subject: [PATCH 01/21] WIP --- x/merkledb/maybe.go | 16 +++- x/sync/syncmanager.go | 205 +++++++++++++++++++++++++----------------- 2 files changed, 140 insertions(+), 81 deletions(-) diff --git a/x/merkledb/maybe.go b/x/merkledb/maybe.go index acebb47fdf18..db0f1411d89d 100644 --- a/x/merkledb/maybe.go +++ b/x/merkledb/maybe.go @@ -3,7 +3,11 @@ package merkledb -import "golang.org/x/exp/slices" +import ( + "bytes" + + "golang.org/x/exp/slices" +) // Maybe T = Some T | Nothing. // A data wrapper that allows values to be something [Some T] or nothing [Nothing]. @@ -39,6 +43,16 @@ func (m Maybe[T]) Value() T { return m.value } +func MaybeByteSliceEquals(m1, m2 Maybe[[]byte]) bool { + if m1.hasValue != m2.hasValue { + return false + } + if !m1.hasValue { + return true + } + return bytes.Equal(m1.value, m2.value) +} + func Clone(m Maybe[[]byte]) Maybe[[]byte] { if !m.hasValue { return Nothing[[]byte]() diff --git a/x/sync/syncmanager.go b/x/sync/syncmanager.go index 5e57be72fc0e..c00724df0e45 100644 --- a/x/sync/syncmanager.go +++ b/x/sync/syncmanager.go @@ -12,6 +12,7 @@ import ( "time" "go.uber.org/zap" + "golang.org/x/exp/maps" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/utils/logging" @@ -370,98 +371,142 @@ func (m *StateSyncManager) findNextKey( lastReceivedKey []byte, receivedProofNodes []merkledb.ProofNode, ) ([]byte, error) { - lastReceivedKeyPath := merkledb.SerializedPath{Value: lastReceivedKey, NibbleLength: 2 * len(lastReceivedKey)} - - // If the received proof's last node has a key is after the lastReceivedKey, this is an exclusion proof. - // if it is an exclusion proof, then we can remove the last node to get a valid proof for some prefix of the lastReceivedKey - if bytes.Compare(receivedProofNodes[len(receivedProofNodes)-1].KeyPath.Value, lastReceivedKey) > 0 { - receivedProofNodes = receivedProofNodes[:len(receivedProofNodes)-1] - // if the new last proof key is before the start of the range, then fallback to the lastReceivedKey as the next key - if bytes.Compare(receivedProofNodes[len(receivedProofNodes)-1].KeyPath.Value, rangeStart) < 0 { - return lastReceivedKey, nil - } - lastReceivedKeyPath = receivedProofNodes[len(receivedProofNodes)-1].KeyPath - } - - proofOfStart, err := m.config.SyncDB.GetProof(ctx, lastReceivedKeyPath.Value) + localProof, err := m.config.SyncDB.GetProof(ctx, lastReceivedKey) if err != nil { return nil, err } - localProofNodes := proofOfStart.Path - // If the received key had an odd length, then the proof generated might be for the even length version of the key (since getProof takes []byte). - // If that is the case, then remove that last node so that the proof is just for the odd nibble length version of the key - if lastReceivedKeyPath.NibbleLength%2 == 1 && !lastReceivedKeyPath.Equal(localProofNodes[len(localProofNodes)-1].KeyPath) { - localProofNodes = localProofNodes[:len(localProofNodes)-1] + localRoot := localProof.Path[0] + receivedRoot := receivedProofNodes[0] + rootsMatch := merkledb.MaybeByteSliceEquals(localRoot.ValueOrHash, receivedRoot.ValueOrHash) + rootsMatch = rootsMatch && localRoot.KeyPath.Equal(receivedRoot.KeyPath) + rootsMatch = rootsMatch && maps.Equal(localRoot.Children, receivedRoot.Children) + if rootsMatch { + // The roots match so we must have the same trie. + return nil, nil } - var result []byte - localIndex := len(localProofNodes) - 1 - receivedIndex := len(receivedProofNodes) - 1 - - // walk up the node paths until a difference is found - for receivedIndex >= 0 && result == nil { - localNode := localProofNodes[localIndex] - receivedNode := receivedProofNodes[receivedIndex] - // the two nodes have the same key - if localNode.KeyPath.Equal(receivedNode.KeyPath) { - startingChildIndex := byte(0) - if localNode.KeyPath.NibbleLength < lastReceivedKeyPath.NibbleLength { - startingChildIndex = lastReceivedKeyPath.NibbleVal(localNode.KeyPath.NibbleLength) + 1 - } - // the two nodes have the same path, so ensure that all children have matching ids - for childIndex := startingChildIndex; childIndex < 16; childIndex++ { - receivedChildID, receiveOk := receivedNode.Children[childIndex] - localChildID, localOk := localNode.Children[childIndex] - // if they both don't have a child or have matching children, continue - if (receiveOk || localOk) && receivedChildID != localChildID { - result = localNode.KeyPath.AppendNibble(childIndex).Value - break - } - } - if result != nil { - break - } - // only want to move both indexes when they have equal keys - localIndex-- - receivedIndex-- - continue - } - - var branchNode merkledb.ProofNode + for i := 0; i < len(receivedProofNodes); i++ { + theirNode := receivedProofNodes[i] + theirNextNode := receivedProofNodes[i+1] + ourNode := localProof.Path[i] + ourNextNode := localProof.Path[i+1] - if receivedNode.KeyPath.NibbleLength > localNode.KeyPath.NibbleLength { - // the received proof has an extra node due to a branch that is not present locally - branchNode = receivedNode - receivedIndex-- - } else { - // the local proof has an extra node due to a branch that was not present in the received proof - branchNode = localNode - localIndex-- - } + ourNextNodeIndex := ourNextNode.KeyPath.Value[len(ourNode.KeyPath.Value)] + ourNextNodeHash := ourNode.Children[ourNextNodeIndex] - // I believe the exclusion proof checks at the beginning of this function should prevent this - // but leave this safeguard in place just in case. - // TODO: Figure out if there are scenarios where this can happen and fix - if lastReceivedKeyPath.NibbleLength <= branchNode.KeyPath.NibbleLength { - // the array access into lastReceivedKeyPath below this would fail, so default to the lastReceivedKey - return lastReceivedKey, nil - } + theirNextNodeIndex := theirNextNode.KeyPath.Value[len(theirNode.KeyPath.Value)] + theirNextNodeHash := theirNode.Children[theirNextNodeIndex] - // the two nodes have different paths, so find where they branched - for nextKeyNibble := lastReceivedKeyPath.NibbleVal(branchNode.KeyPath.NibbleLength) + 1; nextKeyNibble < 16; nextKeyNibble++ { - if _, ok := branchNode.Children[nextKeyNibble]; ok { - result = branchNode.KeyPath.AppendNibble(nextKeyNibble).Value - break + if ourNextNodeHash == theirNextNodeHash { + // Our proof and their proof have the same next node. + // The next key to query is the first difference in our children. + for j := theirNextNodeIndex; j < merkledb.NodeBranchFactor; j++ { + if ourNode.Children[j] != theirNode.Children[j] { + nextStartKey := make([]byte, len(theirNode.KeyPath.Value)+1) + copy(nextStartKey, theirNode.KeyPath.Value) + nextStartKey[len(theirNode.KeyPath.Value)] = byte(j) + return nextStartKey, nil + } } } - } - - if result == nil || (len(rangeEnd) > 0 && bytes.Compare(result, rangeEnd) >= 0) { - return nil, nil - } - return result, nil + // Our proof and their proof have different next nodes. + // There must be a difference below here in the trie. + } + + return nil, errors.New("TODO") + + // // If the received proof's last node has a key is after the lastReceivedKey, this is an exclusion proof. + // // if it is an exclusion proof, then we can remove the last node to get a valid proof for some prefix of the lastReceivedKey + // if bytes.Compare(receivedProofNodes[len(receivedProofNodes)-1].KeyPath.Value, lastReceivedKey) > 0 { + // receivedProofNodes = receivedProofNodes[:len(receivedProofNodes)-1] + // // if the new last proof key is before the start of the range, then fallback to the lastReceivedKey as the next key + // if bytes.Compare(receivedProofNodes[len(receivedProofNodes)-1].KeyPath.Value, rangeStart) < 0 { + // return lastReceivedKey, nil + // } + // lastReceivedKeyPath = receivedProofNodes[len(receivedProofNodes)-1].KeyPath + // } + + // proofOfStart, err := m.config.SyncDB.GetProof(ctx, lastReceivedKeyPath.Value) + // if err != nil { + // return nil, err + // } + // localProofNodes := proofOfStart.Path + + // // If the received key had an odd length, then the proof generated might be for the even length version of the key (since getProof takes []byte). + // // If that is the case, then remove that last node so that the proof is just for the odd nibble length version of the key + // if lastReceivedKeyPath.NibbleLength%2 == 1 && !lastReceivedKeyPath.Equal(localProofNodes[len(localProofNodes)-1].KeyPath) { + // localProofNodes = localProofNodes[:len(localProofNodes)-1] + // } + + // var result []byte + // localIndex := len(localProofNodes) - 1 + // receivedIndex := len(receivedProofNodes) - 1 + + // // walk up the node paths until a difference is found + // for receivedIndex >= 0 && result == nil { + // localNode := localProofNodes[localIndex] + // receivedNode := receivedProofNodes[receivedIndex] + // // the two nodes have the same key + // if localNode.KeyPath.Equal(receivedNode.KeyPath) { + // startingChildIndex := byte(0) + // if localNode.KeyPath.NibbleLength < lastReceivedKeyPath.NibbleLength { + // startingChildIndex = lastReceivedKeyPath.NibbleVal(localNode.KeyPath.NibbleLength) + 1 + // } + // // the two nodes have the same path, so ensure that all children have matching ids + // for childIndex := startingChildIndex; childIndex < 16; childIndex++ { + // receivedChildID, receiveOk := receivedNode.Children[childIndex] + // localChildID, localOk := localNode.Children[childIndex] + // // if they both don't have a child or have matching children, continue + // if (receiveOk || localOk) && receivedChildID != localChildID { + // result = localNode.KeyPath.AppendNibble(childIndex).Value + // break + // } + // } + // if result != nil { + // break + // } + // // only want to move both indexes when they have equal keys + // localIndex-- + // receivedIndex-- + // continue + // } + + // var branchNode merkledb.ProofNode + + // if receivedNode.KeyPath.NibbleLength > localNode.KeyPath.NibbleLength { + // // the received proof has an extra node due to a branch that is not present locally + // branchNode = receivedNode + // receivedIndex-- + // } else { + // // the local proof has an extra node due to a branch that was not present in the received proof + // branchNode = localNode + // localIndex-- + // } + + // // I believe the exclusion proof checks at the beginning of this function should prevent this + // // but leave this safeguard in place just in case. + // // TODO: Figure out if there are scenarios where this can happen and fix + // if lastReceivedKeyPath.NibbleLength <= branchNode.KeyPath.NibbleLength { + // // the array access into lastReceivedKeyPath below this would fail, so default to the lastReceivedKey + // return lastReceivedKey, nil + // } + + // // the two nodes have different paths, so find where they branched + // for nextKeyNibble := lastReceivedKeyPath.NibbleVal(branchNode.KeyPath.NibbleLength) + 1; nextKeyNibble < 16; nextKeyNibble++ { + // if _, ok := branchNode.Children[nextKeyNibble]; ok { + // result = branchNode.KeyPath.AppendNibble(nextKeyNibble).Value + // break + // } + // } + // } + + // if result == nil || (len(rangeEnd) > 0 && bytes.Compare(result, rangeEnd) >= 0) { + // return nil, nil + // } + + // return result, nil } func (m *StateSyncManager) Error() error { From a0954b89a342966a2f63c974d9cc8f45ea05b68d Mon Sep 17 00:00:00 2001 From: Dan Laine Date: Tue, 18 Apr 2023 10:05:31 -0400 Subject: [PATCH 02/21] WIP --- x/merkledb/codec.go | 2 +- x/merkledb/codec_test.go | 6 +- x/merkledb/db.go | 38 ++++++------ x/merkledb/db_test.go | 22 +++---- x/merkledb/history.go | 18 +++--- x/merkledb/history_test.go | 30 +++++----- x/merkledb/iterator.go | 2 +- x/merkledb/node.go | 14 ++--- x/merkledb/node_test.go | 10 ++-- x/merkledb/path.go | 24 ++++---- x/merkledb/proof.go | 48 +++++++-------- x/merkledb/proof_test.go | 118 ++++++++++++++++++------------------- x/merkledb/trie.go | 4 +- x/merkledb/trie_test.go | 36 +++++------ x/merkledb/trieview.go | 44 +++++++------- x/sync/syncmanager.go | 82 ++++++++++++++++---------- 16 files changed, 258 insertions(+), 240 deletions(-) diff --git a/x/merkledb/codec.go b/x/merkledb/codec.go index 7baa37159981..c401c39081d2 100644 --- a/x/merkledb/codec.go +++ b/x/merkledb/codec.go @@ -489,7 +489,7 @@ func (c *codecImpl) decodeDBNode(b []byte, n *dbNode) (uint16, error) { return 0, err } n.children[byte(index)] = child{ - compressedPath: compressedPath.deserialize(), + compressedPath: compressedPath.Deserialize(), id: childID, } } diff --git a/x/merkledb/codec_test.go b/x/merkledb/codec_test.go index f3561c815e5b..0284399e8581 100644 --- a/x/merkledb/codec_test.go +++ b/x/merkledb/codec_test.go @@ -46,7 +46,7 @@ func newRandomProofNode(r *rand.Rand) ProofNode { } return ProofNode{ - KeyPath: newPath(key).Serialize(), + KeyPath: NewPath(key).Serialize(), ValueOrHash: Some(val), Children: children, } @@ -199,7 +199,7 @@ func FuzzCodecSerializedPath(f *testing.F) { require.Len(bufBytes, numRead) require.Equal(b[:numRead], bufBytes) - clonedGot := got.deserialize().Serialize() + clonedGot := got.Deserialize().Serialize() require.Equal(got, clonedGot) }, ) @@ -527,7 +527,7 @@ func FuzzCodecDBNodeDeterministic(f *testing.F) { _, _ = r.Read(childPathBytes) // #nosec G404 children[byte(i)] = child{ - compressedPath: newPath(childPathBytes), + compressedPath: NewPath(childPathBytes), id: childID, } } diff --git a/x/merkledb/db.go b/x/merkledb/db.go index 219d90fad1ff..99adc23b331e 100644 --- a/x/merkledb/db.go +++ b/x/merkledb/db.go @@ -84,7 +84,7 @@ type Database struct { metadataDB database.Database // If a value is nil, the corresponding key isn't in the trie. - nodeCache onEvictCache[path, *node] + nodeCache onEvictCache[Path, *node] onEvictionErr utils.Atomic[error] // Stores change lists. Used to serve change proofs and construct @@ -122,7 +122,7 @@ func newDatabase( // Note: trieDB.OnEviction is responsible for writing intermediary nodes to // disk as they are evicted from the cache. - trieDB.nodeCache = newOnEvictCache[path](config.NodeCacheSize, trieDB.onEviction) + trieDB.nodeCache = newOnEvictCache[Path](config.NodeCacheSize, trieDB.onEviction) root, err := trieDB.initializeRootIfNeeded() if err != nil { @@ -132,8 +132,8 @@ func newDatabase( // add current root to history (has no changes) trieDB.history.record(&changeSummary{ rootID: root, - values: map[path]*change[Maybe[[]byte]]{}, - nodes: map[path]*change[*node]{}, + values: map[Path]*change[Maybe[[]byte]]{}, + nodes: map[Path]*change[*node]{}, }) shutdownType, err := trieDB.metadataDB.Get(cleanShutdownKey) @@ -190,7 +190,7 @@ func (db *Database) rebuild(ctx context.Context) error { } key := it.Key() - path := path(key) + path := Path(key) value := it.Value() n, err := parseNode(path, value) if err != nil { @@ -327,7 +327,7 @@ func (db *Database) GetValues(ctx context.Context, keys [][]byte) ([][]byte, []e values := make([][]byte, len(keys)) errors := make([]error, len(keys)) for i, key := range keys { - values[i], errors[i] = db.getValueCopy(newPath(key), false /*lock*/) + values[i], errors[i] = db.getValueCopy(NewPath(key), false /*lock*/) } return values, errors } @@ -338,12 +338,12 @@ func (db *Database) GetValue(ctx context.Context, key []byte) ([]byte, error) { _, span := db.tracer.Start(ctx, "MerkleDB.GetValue") defer span.End() - return db.getValueCopy(newPath(key), true /*lock*/) + return db.getValueCopy(NewPath(key), true /*lock*/) } // getValueCopy returns a copy of the value for the given [key]. // Returns database.ErrNotFound if it doesn't exist. -func (db *Database) getValueCopy(key path, lock bool) ([]byte, error) { +func (db *Database) getValueCopy(key Path, lock bool) ([]byte, error) { val, err := db.getValue(key, lock) if err != nil { return nil, err @@ -355,7 +355,7 @@ func (db *Database) getValueCopy(key path, lock bool) ([]byte, error) { // Returns database.ErrNotFound if it doesn't exist. // If [lock], [db.lock]'s read lock is acquired. // Otherwise assumes [db.lock] is already held. -func (db *Database) getValue(key path, lock bool) ([]byte, error) { +func (db *Database) getValue(key Path, lock bool) ([]byte, error) { if lock { db.lock.RLock() defer db.lock.RUnlock() @@ -511,7 +511,7 @@ func (db *Database) GetChangeProof( // values modified between [startRootID] to [endRootID] sorted in increasing // order. changedKeys := maps.Keys(changes.values) - slices.SortFunc(changedKeys, func(i, j path) bool { + slices.SortFunc(changedKeys, func(i, j Path) bool { return i.Compare(j) < 0 }) @@ -620,7 +620,7 @@ func (db *Database) Has(k []byte) (bool, error) { return false, database.ErrClosed } - _, err := db.getValue(newPath(k), false /*lock*/) + _, err := db.getValue(NewPath(k), false /*lock*/) if err == database.ErrNotFound { return false, nil } @@ -671,21 +671,21 @@ func (db *Database) NewIterator() database.Iterator { func (db *Database) NewIteratorWithStart(start []byte) database.Iterator { return &iterator{ - nodeIter: db.nodeDB.NewIteratorWithStart(newPath(start).Bytes()), + nodeIter: db.nodeDB.NewIteratorWithStart(NewPath(start).Bytes()), db: db, } } func (db *Database) NewIteratorWithPrefix(prefix []byte) database.Iterator { return &iterator{ - nodeIter: db.nodeDB.NewIteratorWithPrefix(newPath(prefix).Bytes()), + nodeIter: db.nodeDB.NewIteratorWithPrefix(NewPath(prefix).Bytes()), db: db, } } func (db *Database) NewIteratorWithStartAndPrefix(start, prefix []byte) database.Iterator { - startBytes := newPath(start).Bytes() - prefixBytes := newPath(prefix).Bytes() + startBytes := NewPath(start).Bytes() + prefixBytes := NewPath(prefix).Bytes() return &iterator{ nodeIter: db.nodeDB.NewIteratorWithStartAndPrefix(startBytes, prefixBytes), db: db, @@ -992,7 +992,7 @@ func (db *Database) getKeysNotInSet(start, end []byte, keySet set.Set[string]) ( // This copy may be edited by the caller without affecting the database state. // Returns database.ErrNotFound if the node doesn't exist. // Assumes [db.lock] isn't held. -func (db *Database) getEditableNode(key path) (*node, error) { +func (db *Database) getEditableNode(key Path) (*node, error) { db.lock.RLock() defer db.lock.RUnlock() @@ -1007,7 +1007,7 @@ func (db *Database) getEditableNode(key path) (*node, error) { // Editing the returned node affects the database state. // Returns database.ErrNotFound if the node doesn't exist. // Assumes [db.lock] is read locked. -func (db *Database) getNode(key path) (*node, error) { +func (db *Database) getNode(key Path) (*node, error) { if db.closed { return nil, database.ErrClosed } @@ -1175,14 +1175,14 @@ func (db *Database) prepareRangeProofView(start []byte, proof *RangeProof) (*tri } // Non-nil error is fatal -- [db] will close. -func (db *Database) putNodeInCache(key path, n *node) error { +func (db *Database) putNodeInCache(key Path, n *node) error { // TODO Cache metrics // Note that this may cause a node to be evicted from the cache, // which will call [OnEviction]. return db.nodeCache.Put(key, n) } -func (db *Database) getNodeInCache(key path) (*node, bool) { +func (db *Database) getNodeInCache(key Path) (*node, bool) { // TODO Cache metrics if node, ok := db.nodeCache.Get(key); ok { return node, true diff --git a/x/merkledb/db_test.go b/x/merkledb/db_test.go index 8a0f98d3b4fb..7921bbf94635 100644 --- a/x/merkledb/db_test.go +++ b/x/merkledb/db_test.go @@ -33,7 +33,7 @@ func Test_MerkleDB_Get_Safety(t *testing.T) { val, err := db.Get([]byte{0}) require.NoError(t, err) - n, err := db.getNode(newPath([]byte{0})) + n, err := db.getNode(NewPath([]byte{0})) require.NoError(t, err) val[0] = 1 @@ -762,10 +762,10 @@ func runRandDBTest(require *require.Assertions, r *rand.Rand, rt randTest) { startRoot, err := db.GetMerkleRoot(context.Background()) require.NoError(err) - values := make(map[path][]byte) // tracks content of the trie + values := make(map[Path][]byte) // tracks content of the trie currentBatch := db.NewBatch() - currentValues := make(map[path][]byte) - deleteValues := make(map[path]struct{}) + currentValues := make(map[Path][]byte) + deleteValues := make(map[Path]struct{}) pastRoots := []ids.ID{} for i, step := range rt { @@ -774,13 +774,13 @@ func runRandDBTest(require *require.Assertions, r *rand.Rand, rt randTest) { case opUpdate: err := currentBatch.Put(step.key, step.value) require.NoError(err) - currentValues[newPath(step.key)] = step.value - delete(deleteValues, newPath(step.key)) + currentValues[NewPath(step.key)] = step.value + delete(deleteValues, NewPath(step.key)) case opDelete: err := currentBatch.Delete(step.key) require.NoError(err) - deleteValues[newPath(step.key)] = struct{}{} - delete(currentValues, newPath(step.key)) + deleteValues[NewPath(step.key)] = struct{}{} + delete(currentValues, NewPath(step.key)) case opGenerateRangeProof: root, err := db.GetMerkleRoot(context.Background()) require.NoError(err) @@ -843,15 +843,15 @@ func runRandDBTest(require *require.Assertions, r *rand.Rand, rt randTest) { pastRoots = pastRoots[len(pastRoots)-300:] } } - currentValues = map[path][]byte{} - deleteValues = map[path]struct{}{} + currentValues = map[Path][]byte{} + deleteValues = map[Path]struct{}{} currentBatch = db.NewBatch() case opGet: v, err := db.Get(step.key) if err != nil { require.ErrorIs(err, database.ErrNotFound) } - want := values[newPath(step.key)] + want := values[NewPath(step.key)] require.True(bytes.Equal(want, v)) // Use bytes.Equal so nil treated equal to []byte{} trieValue, err := getNodeValue(db, string(step.key)) if err != nil { diff --git a/x/merkledb/history.go b/x/merkledb/history.go index 78433e0a5e9a..c5d38a0d1575 100644 --- a/x/merkledb/history.go +++ b/x/merkledb/history.go @@ -53,15 +53,15 @@ type changeSummaryAndIndex struct { type changeSummary struct { rootID ids.ID // key is path prefix - nodes map[path]*change[*node] + nodes map[Path]*change[*node] // key is full path - values map[path]*change[Maybe[[]byte]] + values map[Path]*change[Maybe[[]byte]] } func newChangeSummary(estimatedSize int) *changeSummary { return &changeSummary{ - nodes: make(map[path]*change[*node], estimatedSize), - values: make(map[path]*change[Maybe[[]byte]], estimatedSize), + nodes: make(map[Path]*change[*node], estimatedSize), + values: make(map[Path]*change[Maybe[[]byte]], estimatedSize), } } @@ -128,13 +128,13 @@ func (th *trieHistory) getValueChanges(startRoot, endRoot ids.ID, start, end []b // Keep changes sorted so the largest can be removed in order to stay within the maxLength limit. sortedKeys := btree.NewG( 2, - func(a, b path) bool { + func(a, b Path) bool { return a.Compare(b) < 0 }, ) - startPath := newPath(start) - endPath := newPath(end) + startPath := NewPath(start) + endPath := NewPath(end) // For each element in the history in the range between [startRoot]'s // last appearance (exclusive) and [endRoot]'s last appearance (inclusive), @@ -206,8 +206,8 @@ func (th *trieHistory) getChangesToGetToRoot(rootID ids.ID, start, end []byte) ( } var ( - startPath = newPath(start) - endPath = newPath(end) + startPath = NewPath(start) + endPath = NewPath(end) combinedChanges = newChangeSummary(defaultPreallocationSize) ) diff --git a/x/merkledb/history_test.go b/x/merkledb/history_test.go index 29da9974fc62..18f3b44d942c 100644 --- a/x/merkledb/history_test.go +++ b/x/merkledb/history_test.go @@ -361,10 +361,10 @@ func Test_History_Values_Lookup_Over_Queue_Break(t *testing.T) { // changes should still be collectable even though the history has had to loop due to hitting max size changes, err := db.history.getValueChanges(startRoot, endRoot, nil, nil, 10) require.NoError(err) - require.Contains(changes.values, newPath([]byte("key1"))) - require.Equal([]byte("value1"), changes.values[newPath([]byte("key1"))].after.value) - require.Contains(changes.values, newPath([]byte("key2"))) - require.Equal([]byte("value3"), changes.values[newPath([]byte("key2"))].after.value) + require.Contains(changes.values, NewPath([]byte("key1"))) + require.Equal([]byte("value1"), changes.values[NewPath([]byte("key1"))].after.value) + require.Contains(changes.values, NewPath([]byte("key2"))) + require.Equal([]byte("value3"), changes.values[NewPath([]byte("key2"))].after.value) } func Test_History_RepeatedRoot(t *testing.T) { @@ -770,14 +770,14 @@ func TestHistoryGetChangesToRoot(t *testing.T) { for i := 0; i < maxHistoryLen; i++ { // Fill the history changes = append(changes, &changeSummary{ rootID: ids.GenerateTestID(), - nodes: map[path]*change[*node]{ - newPath([]byte{byte(i)}): { + nodes: map[Path]*change[*node]{ + NewPath([]byte{byte(i)}): { before: &node{id: ids.GenerateTestID()}, after: &node{id: ids.GenerateTestID()}, }, }, - values: map[path]*change[Maybe[[]byte]]{ - newPath([]byte{byte(i)}): { + values: map[Path]*change[Maybe[[]byte]]{ + NewPath([]byte{byte(i)}): { before: Some([]byte{byte(i)}), after: Some([]byte{byte(i + 1)}), }, @@ -816,7 +816,7 @@ func TestHistoryGetChangesToRoot(t *testing.T) { require.Len(got.nodes, 1) require.Len(got.values, 1) reversedChanges := changes[maxHistoryLen-1] - removedKey := newPath([]byte{byte(maxHistoryLen - 1)}) + removedKey := NewPath([]byte{byte(maxHistoryLen - 1)}) require.Equal(reversedChanges.nodes[removedKey].before, got.nodes[removedKey].after) require.Equal(reversedChanges.values[removedKey].before, got.values[removedKey].after) require.Equal(reversedChanges.values[removedKey].after, got.values[removedKey].before) @@ -829,12 +829,12 @@ func TestHistoryGetChangesToRoot(t *testing.T) { require.Len(got.nodes, 2) require.Len(got.values, 2) reversedChanges1 := changes[maxHistoryLen-1] - removedKey1 := newPath([]byte{byte(maxHistoryLen - 1)}) + removedKey1 := NewPath([]byte{byte(maxHistoryLen - 1)}) require.Equal(reversedChanges1.nodes[removedKey1].before, got.nodes[removedKey1].after) require.Equal(reversedChanges1.values[removedKey1].before, got.values[removedKey1].after) require.Equal(reversedChanges1.values[removedKey1].after, got.values[removedKey1].before) reversedChanges2 := changes[maxHistoryLen-2] - removedKey2 := newPath([]byte{byte(maxHistoryLen - 2)}) + removedKey2 := NewPath([]byte{byte(maxHistoryLen - 2)}) require.Equal(reversedChanges2.nodes[removedKey2].before, got.nodes[removedKey2].after) require.Equal(reversedChanges2.values[removedKey2].before, got.values[removedKey2].after) require.Equal(reversedChanges2.values[removedKey2].after, got.values[removedKey2].before) @@ -848,12 +848,12 @@ func TestHistoryGetChangesToRoot(t *testing.T) { require.Len(got.nodes, 2) require.Len(got.values, 1) reversedChanges1 := changes[maxHistoryLen-1] - removedKey1 := newPath([]byte{byte(maxHistoryLen - 1)}) + removedKey1 := NewPath([]byte{byte(maxHistoryLen - 1)}) require.Equal(reversedChanges1.nodes[removedKey1].before, got.nodes[removedKey1].after) require.Equal(reversedChanges1.values[removedKey1].before, got.values[removedKey1].after) require.Equal(reversedChanges1.values[removedKey1].after, got.values[removedKey1].before) reversedChanges2 := changes[maxHistoryLen-2] - removedKey2 := newPath([]byte{byte(maxHistoryLen - 2)}) + removedKey2 := NewPath([]byte{byte(maxHistoryLen - 2)}) require.Equal(reversedChanges2.nodes[removedKey2].before, got.nodes[removedKey2].after) }, }, @@ -865,10 +865,10 @@ func TestHistoryGetChangesToRoot(t *testing.T) { require.Len(got.nodes, 2) require.Len(got.values, 1) reversedChanges1 := changes[maxHistoryLen-1] - removedKey1 := newPath([]byte{byte(maxHistoryLen - 1)}) + removedKey1 := NewPath([]byte{byte(maxHistoryLen - 1)}) require.Equal(reversedChanges1.nodes[removedKey1].before, got.nodes[removedKey1].after) reversedChanges2 := changes[maxHistoryLen-2] - removedKey2 := newPath([]byte{byte(maxHistoryLen - 2)}) + removedKey2 := NewPath([]byte{byte(maxHistoryLen - 2)}) require.Equal(reversedChanges2.nodes[removedKey2].before, got.nodes[removedKey2].after) require.Equal(reversedChanges2.values[removedKey2].before, got.values[removedKey2].after) require.Equal(reversedChanges2.values[removedKey2].after, got.values[removedKey2].before) diff --git a/x/merkledb/iterator.go b/x/merkledb/iterator.go index ad235b7ddc1d..e62bcc72cbd2 100644 --- a/x/merkledb/iterator.go +++ b/x/merkledb/iterator.go @@ -42,7 +42,7 @@ func (i *iterator) Next() bool { } for i.nodeIter.Next() { i.db.metrics.IOKeyRead() - n, err := parseNode(path(i.nodeIter.Key()), i.nodeIter.Value()) + n, err := parseNode(Path(i.nodeIter.Key()), i.nodeIter.Value()) if err != nil { i.err = err return false diff --git a/x/merkledb/node.go b/x/merkledb/node.go index edcb78c7d153..ceded084a820 100644 --- a/x/merkledb/node.go +++ b/x/merkledb/node.go @@ -29,7 +29,7 @@ type dbNode struct { } type child struct { - compressedPath path + compressedPath Path id ids.ID } @@ -37,14 +37,14 @@ type child struct { type node struct { dbNode id ids.ID - key path + key Path nodeBytes []byte valueDigest Maybe[[]byte] } // Returns a new node with the given [key] and no value. // If [parent] isn't nil, the new node is added as a child of [parent]. -func newNode(parent *node, key path) *node { +func newNode(parent *node, key Path) *node { newNode := &node{ dbNode: dbNode{ children: make(map[byte]child, NodeBranchFactor), @@ -58,7 +58,7 @@ func newNode(parent *node, key path) *node { } // Parse [nodeBytes] to a node and set its key to [key]. -func parseNode(key path, nodeBytes []byte) (*node, error) { +func parseNode(key Path, nodeBytes []byte) (*node, error) { n := dbNode{} if _, err := Codec.decodeDBNode(nodeBytes, &n); err != nil { return nil, err @@ -148,7 +148,7 @@ func (n *node) addChild(child *node) { } // Adds a child to [n] without a reference to the child node. -func (n *node) addChildWithoutNode(index byte, compressedPath path, childID ids.ID) { +func (n *node) addChildWithoutNode(index byte, compressedPath Path, childID ids.ID) { n.onNodeChanged() n.children[index] = child{ compressedPath: compressedPath, @@ -158,9 +158,9 @@ func (n *node) addChildWithoutNode(index byte, compressedPath path, childID ids. // Returns the path of the only child of this node. // Assumes this node has exactly one child. -func (n *node) getSingleChildPath() path { +func (n *node) getSingleChildPath() Path { for index, entry := range n.children { - return n.key + path(index) + entry.compressedPath + return n.key + Path(index) + entry.compressedPath } return "" } diff --git a/x/merkledb/node_test.go b/x/merkledb/node_test.go index 7c7c2578eb7e..4f242eb6b93d 100644 --- a/x/merkledb/node_test.go +++ b/x/merkledb/node_test.go @@ -14,7 +14,7 @@ func Test_Node_Marshal(t *testing.T) { root := newNode(nil, EmptyPath) require.NotNil(t, root) - fullpath := newPath([]byte("key")) + fullpath := NewPath([]byte("key")) childNode := newNode(root, fullpath) childNode.setValue(Some([]byte("value"))) require.NotNil(t, childNode) @@ -25,7 +25,7 @@ func Test_Node_Marshal(t *testing.T) { data, err := root.marshal() require.NoError(t, err) - rootParsed, err := parseNode(newPath([]byte("")), data) + rootParsed, err := parseNode(NewPath([]byte("")), data) require.NoError(t, err) require.Equal(t, 1, len(rootParsed.children)) @@ -40,7 +40,7 @@ func Test_Node_Marshal_Errors(t *testing.T) { root := newNode(nil, EmptyPath) require.NotNil(t, root) - fullpath := newPath([]byte{255}) + fullpath := NewPath([]byte{255}) childNode1 := newNode(root, fullpath) childNode1.setValue(Some([]byte("value1"))) require.NotNil(t, childNode1) @@ -49,7 +49,7 @@ func Test_Node_Marshal_Errors(t *testing.T) { require.NoError(t, err) root.addChild(childNode1) - fullpath = newPath([]byte{237}) + fullpath = NewPath([]byte{237}) childNode2 := newNode(root, fullpath) childNode2.setValue(Some([]byte("value2"))) require.NotNil(t, childNode2) @@ -63,7 +63,7 @@ func Test_Node_Marshal_Errors(t *testing.T) { for i := 1; i < len(data); i++ { broken := data[:i] - _, err = parseNode(newPath([]byte("")), broken) + _, err = parseNode(NewPath([]byte("")), broken) require.ErrorIs(t, err, io.ErrUnexpectedEOF) } } diff --git a/x/merkledb/path.go b/x/merkledb/path.go index 7a78f2ac17a5..511bf8c2ab72 100644 --- a/x/merkledb/path.go +++ b/x/merkledb/path.go @@ -10,7 +10,7 @@ import ( "unsafe" ) -const EmptyPath path = "" +const EmptyPath Path = "" // SerializedPath contains a path from the trie. // The trie branch factor is 16, so the path may contain an odd number of nibbles. @@ -28,8 +28,8 @@ func (s SerializedPath) Equal(other SerializedPath) bool { return s.NibbleLength == other.NibbleLength && bytes.Equal(s.Value, other.Value) } -func (s SerializedPath) deserialize() path { - result := newPath(s.Value) +func (s SerializedPath) Deserialize() Path { + result := NewPath(s.Value) // trim the last nibble if the path has an odd length return result[:len(result)-s.NibbleLength&1] } @@ -77,18 +77,18 @@ func (s SerializedPath) AppendNibble(nibble byte) SerializedPath { return SerializedPath{Value: value, NibbleLength: s.NibbleLength + 1} } -type path string +type Path string // Returns: // * 0 if [p] == [other]. // * -1 if [p] < [other]. // * 1 if [p] > [other]. -func (p path) Compare(other path) int { +func (p Path) Compare(other Path) int { return strings.Compare(string(p), string(other)) } // Invariant: The returned value must not be modified. -func (p path) Bytes() []byte { +func (p Path) Bytes() []byte { // avoid copying during the conversion // "safe" because we never edit the value, only used as DB key buf := *(*[]byte)(unsafe.Pointer(&p)) @@ -97,17 +97,17 @@ func (p path) Bytes() []byte { } // Returns true iff [p] begins with [prefix]. -func (p path) HasPrefix(prefix path) bool { +func (p Path) HasPrefix(prefix Path) bool { return strings.HasPrefix(string(p), string(prefix)) } // Append [val] to [p]. -func (p path) Append(val byte) path { - return p + path(val) +func (p Path) Append(val byte) Path { + return p + Path(val) } // Returns the serialized representation of [p]. -func (p path) Serialize() SerializedPath { +func (p Path) Serialize() SerializedPath { // need half the number of bytes as nibbles // add one so there is a byte for the odd nibble if it exists // the extra nibble gets rounded down if even length @@ -135,7 +135,7 @@ func (p path) Serialize() SerializedPath { return result } -func newPath(p []byte) path { +func NewPath(p []byte) Path { // create new buffer with double the length of the input since each byte gets split into two nibbles buffer := make([]byte, 2*len(p)) @@ -149,5 +149,5 @@ func newPath(p []byte) path { } // avoid copying during the conversion - return *(*path)(unsafe.Pointer(&buffer)) + return *(*Path)(unsafe.Pointer(&buffer)) } diff --git a/x/merkledb/proof.go b/x/merkledb/proof.go index 651ae48083fa..8d5b230d0312 100644 --- a/x/merkledb/proof.go +++ b/x/merkledb/proof.go @@ -67,7 +67,7 @@ func (proof *Proof) Verify(ctx context.Context, expectedRootID ids.ID) error { if len(proof.Path) == 0 { return ErrNoProof } - if err := verifyProofPath(proof.Path, newPath(proof.Key)); err != nil { + if err := verifyProofPath(proof.Path, NewPath(proof.Key)); err != nil { return err } @@ -101,7 +101,7 @@ func (proof *Proof) Verify(ctx context.Context, expectedRootID ids.ID) error { // Insert all of the proof nodes. // [provenPath] is the path that we are proving exists, or the path // that is where the path we are proving doesn't exist should be. - provenPath := proof.Path[len(proof.Path)-1].KeyPath.deserialize() + provenPath := proof.Path[len(proof.Path)-1].KeyPath.Deserialize() // Don't bother locking [db] and [view] -- nobody else has a reference to them. if err = addPathInfo(view, proof.Path, provenPath, provenPath); err != nil { @@ -188,13 +188,13 @@ func (proof *RangeProof) Verify( } // The key-value pairs (allegedly) proven by [proof]. - keyValues := make(map[path][]byte, len(proof.KeyValues)) + keyValues := make(map[Path][]byte, len(proof.KeyValues)) for _, keyValue := range proof.KeyValues { - keyValues[newPath(keyValue.Key)] = keyValue.Value + keyValues[NewPath(keyValue.Key)] = keyValue.Value } - smallestPath := newPath(start) - largestPath := newPath(largestkey) + smallestPath := NewPath(start) + largestPath := NewPath(largestkey) // Ensure that the start proof is valid and contains values that // match the key/values that were sent. @@ -222,7 +222,7 @@ func (proof *RangeProof) Verify( // Insert all key-value pairs into the trie. for _, kv := range proof.KeyValues { - if _, err := view.insertIntoTrie(newPath(kv.Key), Some(kv.Value)); err != nil { + if _, err := view.insertIntoTrie(NewPath(kv.Key), Some(kv.Value)); err != nil { return err } } @@ -251,12 +251,12 @@ func (proof *RangeProof) Verify( // Verify that all non-intermediate nodes in [proof] which have keys // in [[start], [end]] have the value given for that key in [keysValues]. -func verifyAllRangeProofKeyValuesPresent(proof []ProofNode, start, end path, keysValues map[path][]byte) error { +func verifyAllRangeProofKeyValuesPresent(proof []ProofNode, start, end Path, keysValues map[Path][]byte) error { for i := 0; i < len(proof); i++ { var ( node = proof[i] nodeKey = node.KeyPath - nodePath = nodeKey.deserialize() + nodePath = nodeKey.Deserialize() ) // Skip odd length keys since they cannot have a value (enforced by [verifyProofPath]). @@ -365,7 +365,7 @@ func (proof *ChangeProof) Verify( return err } - smallestPath := newPath(start) + smallestPath := NewPath(start) // Make sure the start proof, if given, is well-formed. if err := verifyProofPath(proof.StartProof, smallestPath); err != nil { @@ -375,7 +375,7 @@ func (proof *ChangeProof) Verify( // Find the greatest key in [proof.KeyValues] and [proof.DeletedKeys]. // Note that [proof.EndProof] is a proof for this key. // [largestPath] is also used when we add children of proof nodes to [trie] below. - largestPath := newPath(proof.getLargestKey(end)) + largestPath := NewPath(proof.getLargestKey(end)) // Make sure the end proof, if given, is well-formed. if err := verifyProofPath(proof.EndProof, largestPath); err != nil { @@ -383,12 +383,12 @@ func (proof *ChangeProof) Verify( } // gather all key/values in the proof - keyValues := make(map[path]Maybe[[]byte], len(proof.KeyValues)+len(proof.DeletedKeys)) + keyValues := make(map[Path]Maybe[[]byte], len(proof.KeyValues)+len(proof.DeletedKeys)) for _, keyValue := range proof.KeyValues { - keyValues[newPath(keyValue.Key)] = Some(keyValue.Value) + keyValues[NewPath(keyValue.Key)] = Some(keyValue.Value) } for _, key := range proof.DeletedKeys { - keyValues[newPath(key)] = Nothing[[]byte]() + keyValues[NewPath(key)] = Nothing[[]byte]() } // want to prevent commit writes to DB, but not prevent db reads @@ -425,14 +425,14 @@ func (proof *ChangeProof) Verify( // Insert the key-value pairs into the trie. for _, kv := range proof.KeyValues { - if _, err := view.insertIntoTrie(newPath(kv.Key), Some(kv.Value)); err != nil { + if _, err := view.insertIntoTrie(NewPath(kv.Key), Some(kv.Value)); err != nil { return err } } // Remove the deleted keys from the trie. for _, key := range proof.DeletedKeys { - if err := view.removeFromTrie(newPath(key)); err != nil { + if err := view.removeFromTrie(NewPath(key)); err != nil { return err } } @@ -465,15 +465,15 @@ func verifyAllChangeProofKeyValuesPresent( ctx context.Context, db *Database, proof []ProofNode, - start path, - end path, - keysValues map[path]Maybe[[]byte], + start Path, + end Path, + keysValues map[Path]Maybe[[]byte], ) error { for i := 0; i < len(proof); i++ { var ( node = proof[i] nodeKey = node.KeyPath - nodePath = nodeKey.deserialize() + nodePath = nodeKey.Deserialize() ) // Check the value of any node with a key that is within the range. @@ -551,7 +551,7 @@ func verifyKeyValues(kvs []KeyValue, start, end []byte) error { // - Each key in [proof] is a strict prefix of [keyBytes], except possibly the last. // - If the last element in [proof] is [keyBytes], this is an inclusion proof. // Otherwise, this is an exclusion proof and [keyBytes] must not be in [proof]. -func verifyProofPath(proof []ProofNode, keyPath path) error { +func verifyProofPath(proof []ProofNode, keyPath Path) error { provenKey := keyPath.Serialize() // loop over all but the last node since it will not have the prefix in exclusion proofs @@ -617,8 +617,8 @@ func valueOrHashMatches(value Maybe[[]byte], valueOrHash Maybe[[]byte]) bool { func addPathInfo( t *trieView, proofPath []ProofNode, - startPath path, - endPath path, + startPath Path, + endPath Path, ) error { var ( hasLowerBound = len(startPath) > 0 @@ -627,7 +627,7 @@ func addPathInfo( for i := len(proofPath) - 1; i >= 0; i-- { proofNode := proofPath[i] - keyPath := proofNode.KeyPath.deserialize() + keyPath := proofNode.KeyPath.Deserialize() if len(keyPath)&1 == 1 && !proofNode.ValueOrHash.IsNothing() { // a value cannot have an odd number of nibbles in its key diff --git a/x/merkledb/proof_test.go b/x/merkledb/proof_test.go index c4d1eb865725..4ba64113c972 100644 --- a/x/merkledb/proof_test.go +++ b/x/merkledb/proof_test.go @@ -347,10 +347,10 @@ func Test_Proof(t *testing.T) { require.Len(t, proof.Path, 3) - require.Equal(t, newPath([]byte("key1")).Serialize(), proof.Path[2].KeyPath) + require.Equal(t, NewPath([]byte("key1")).Serialize(), proof.Path[2].KeyPath) require.Equal(t, Some([]byte("value1")), proof.Path[2].ValueOrHash) - require.Equal(t, newPath([]byte{}).Serialize(), proof.Path[0].KeyPath) + require.Equal(t, NewPath([]byte{}).Serialize(), proof.Path[0].KeyPath) require.True(t, proof.Path[0].ValueOrHash.IsNothing()) expectedRootID, err := trie.GetMerkleRoot(context.Background()) @@ -451,10 +451,10 @@ func Test_RangeProof_Syntactic_Verify(t *testing.T) { }, StartProof: []ProofNode{ { - KeyPath: newPath([]byte{2}).Serialize(), + KeyPath: NewPath([]byte{2}).Serialize(), }, { - KeyPath: newPath([]byte{1}).Serialize(), + KeyPath: NewPath([]byte{1}).Serialize(), }, }, }, @@ -470,13 +470,13 @@ func Test_RangeProof_Syntactic_Verify(t *testing.T) { }, StartProof: []ProofNode{ { - KeyPath: newPath([]byte{1}).Serialize(), + KeyPath: NewPath([]byte{1}).Serialize(), }, { - KeyPath: newPath([]byte{1, 2, 3}).Serialize(), // Not a prefix of [1, 2] + KeyPath: NewPath([]byte{1, 2, 3}).Serialize(), // Not a prefix of [1, 2] }, { - KeyPath: newPath([]byte{1, 2, 3, 4}).Serialize(), + KeyPath: NewPath([]byte{1, 2, 3, 4}).Serialize(), }, }, }, @@ -492,10 +492,10 @@ func Test_RangeProof_Syntactic_Verify(t *testing.T) { }, EndProof: []ProofNode{ { - KeyPath: newPath([]byte{2}).Serialize(), + KeyPath: NewPath([]byte{2}).Serialize(), }, { - KeyPath: newPath([]byte{1}).Serialize(), + KeyPath: NewPath([]byte{1}).Serialize(), }, }, }, @@ -511,13 +511,13 @@ func Test_RangeProof_Syntactic_Verify(t *testing.T) { }, EndProof: []ProofNode{ { - KeyPath: newPath([]byte{1}).Serialize(), + KeyPath: NewPath([]byte{1}).Serialize(), }, { - KeyPath: newPath([]byte{1, 2, 3}).Serialize(), // Not a prefix of [1, 2] + KeyPath: NewPath([]byte{1, 2, 3}).Serialize(), // Not a prefix of [1, 2] }, { - KeyPath: newPath([]byte{1, 2, 3, 4}).Serialize(), + KeyPath: NewPath([]byte{1, 2, 3, 4}).Serialize(), }, }, }, @@ -612,9 +612,9 @@ func Test_RangeProof_NilStart(t *testing.T) { require.Equal(t, []byte("value1"), proof.KeyValues[0].Value) require.Equal(t, []byte("value2"), proof.KeyValues[1].Value) - require.Equal(t, newPath([]byte("key2")).Serialize(), proof.EndProof[2].KeyPath) + require.Equal(t, NewPath([]byte("key2")).Serialize(), proof.EndProof[2].KeyPath) require.Equal(t, SerializedPath{Value: []uint8{0x6b, 0x65, 0x79, 0x30}, NibbleLength: 7}, proof.EndProof[1].KeyPath) - require.Equal(t, newPath([]byte("")).Serialize(), proof.EndProof[0].KeyPath) + require.Equal(t, NewPath([]byte("")).Serialize(), proof.EndProof[0].KeyPath) err = proof.Verify( context.Background(), @@ -688,11 +688,11 @@ func Test_RangeProof_EmptyValues(t *testing.T) { require.Empty(t, proof.KeyValues[2].Value) require.Len(t, proof.StartProof, 1) - require.Equal(t, newPath([]byte("key1")).Serialize(), proof.StartProof[0].KeyPath) + require.Equal(t, NewPath([]byte("key1")).Serialize(), proof.StartProof[0].KeyPath) require.Len(t, proof.EndProof, 3) - require.Equal(t, newPath([]byte("key2")).Serialize(), proof.EndProof[2].KeyPath) - require.Equal(t, newPath([]byte{}).Serialize(), proof.EndProof[0].KeyPath) + require.Equal(t, NewPath([]byte("key2")).Serialize(), proof.EndProof[2].KeyPath) + require.Equal(t, NewPath([]byte{}).Serialize(), proof.EndProof[0].KeyPath) err = proof.Verify( context.Background(), @@ -1382,8 +1382,8 @@ func Test_ChangeProof_Syntactic_Verify(t *testing.T) { proof: &ChangeProof{ HadRootsInHistory: true, StartProof: []ProofNode{ - {KeyPath: newPath([]byte{2}).Serialize()}, - {KeyPath: newPath([]byte{2, 3}).Serialize()}, + {KeyPath: NewPath([]byte{2}).Serialize()}, + {KeyPath: NewPath([]byte{2, 3}).Serialize()}, }, }, start: []byte{1, 2, 3}, @@ -1395,8 +1395,8 @@ func Test_ChangeProof_Syntactic_Verify(t *testing.T) { proof: &ChangeProof{ HadRootsInHistory: true, StartProof: []ProofNode{ - {KeyPath: newPath([]byte{1}).Serialize()}, - {KeyPath: newPath([]byte{2, 3}).Serialize()}, + {KeyPath: NewPath([]byte{1}).Serialize()}, + {KeyPath: NewPath([]byte{2, 3}).Serialize()}, }, }, start: []byte{1, 2, 3}, @@ -1411,8 +1411,8 @@ func Test_ChangeProof_Syntactic_Verify(t *testing.T) { {Key: []byte{1, 2}}, // Also tests [end] set to greatest key-value/deleted key }, EndProof: []ProofNode{ - {KeyPath: newPath([]byte{2}).Serialize()}, - {KeyPath: newPath([]byte{2, 3}).Serialize()}, + {KeyPath: NewPath([]byte{2}).Serialize()}, + {KeyPath: NewPath([]byte{2, 3}).Serialize()}, }, }, start: nil, @@ -1427,8 +1427,8 @@ func Test_ChangeProof_Syntactic_Verify(t *testing.T) { {1, 2, 3}, // Also tests [end] set to greatest key-value/deleted key }, EndProof: []ProofNode{ - {KeyPath: newPath([]byte{1}).Serialize()}, - {KeyPath: newPath([]byte{2, 3}).Serialize()}, + {KeyPath: NewPath([]byte{1}).Serialize()}, + {KeyPath: NewPath([]byte{2, 3}).Serialize()}, }, }, start: nil, @@ -1548,9 +1548,9 @@ func TestVerifyProofPath(t *testing.T) { { name: "non-increasing keys", path: []ProofNode{ - {KeyPath: newPath([]byte{1}).Serialize()}, - {KeyPath: newPath([]byte{1, 2}).Serialize()}, - {KeyPath: newPath([]byte{1, 3}).Serialize()}, + {KeyPath: NewPath([]byte{1}).Serialize()}, + {KeyPath: NewPath([]byte{1, 2}).Serialize()}, + {KeyPath: NewPath([]byte{1, 3}).Serialize()}, }, proofKey: []byte{1, 2, 3}, expectedErr: ErrNonIncreasingProofNodes, @@ -1558,10 +1558,10 @@ func TestVerifyProofPath(t *testing.T) { { name: "invalid key", path: []ProofNode{ - {KeyPath: newPath([]byte{1}).Serialize()}, - {KeyPath: newPath([]byte{1, 2}).Serialize()}, - {KeyPath: newPath([]byte{1, 2, 4}).Serialize()}, - {KeyPath: newPath([]byte{1, 2, 3}).Serialize()}, + {KeyPath: NewPath([]byte{1}).Serialize()}, + {KeyPath: NewPath([]byte{1, 2}).Serialize()}, + {KeyPath: NewPath([]byte{1, 2, 4}).Serialize()}, + {KeyPath: NewPath([]byte{1, 2, 3}).Serialize()}, }, proofKey: []byte{1, 2, 3}, expectedErr: ErrProofNodeNotForKey, @@ -1569,9 +1569,9 @@ func TestVerifyProofPath(t *testing.T) { { name: "extra node inclusion proof", path: []ProofNode{ - {KeyPath: newPath([]byte{1}).Serialize()}, - {KeyPath: newPath([]byte{1, 2}).Serialize()}, - {KeyPath: newPath([]byte{1, 2, 3}).Serialize()}, + {KeyPath: NewPath([]byte{1}).Serialize()}, + {KeyPath: NewPath([]byte{1, 2}).Serialize()}, + {KeyPath: NewPath([]byte{1, 2, 3}).Serialize()}, }, proofKey: []byte{1, 2}, expectedErr: ErrProofNodeNotForKey, @@ -1579,9 +1579,9 @@ func TestVerifyProofPath(t *testing.T) { { name: "extra node exclusion proof", path: []ProofNode{ - {KeyPath: newPath([]byte{1}).Serialize()}, - {KeyPath: newPath([]byte{1, 3}).Serialize()}, - {KeyPath: newPath([]byte{1, 3, 4}).Serialize()}, + {KeyPath: NewPath([]byte{1}).Serialize()}, + {KeyPath: NewPath([]byte{1, 3}).Serialize()}, + {KeyPath: NewPath([]byte{1, 3, 4}).Serialize()}, }, proofKey: []byte{1, 2}, expectedErr: ErrProofNodeNotForKey, @@ -1589,9 +1589,9 @@ func TestVerifyProofPath(t *testing.T) { { name: "happy path exclusion proof", path: []ProofNode{ - {KeyPath: newPath([]byte{1}).Serialize()}, - {KeyPath: newPath([]byte{1, 2}).Serialize()}, - {KeyPath: newPath([]byte{1, 2, 4}).Serialize()}, + {KeyPath: NewPath([]byte{1}).Serialize()}, + {KeyPath: NewPath([]byte{1, 2}).Serialize()}, + {KeyPath: NewPath([]byte{1, 2, 4}).Serialize()}, }, proofKey: []byte{1, 2, 3}, expectedErr: nil, @@ -1599,9 +1599,9 @@ func TestVerifyProofPath(t *testing.T) { { name: "happy path inclusion proof", path: []ProofNode{ - {KeyPath: newPath([]byte{1}).Serialize()}, - {KeyPath: newPath([]byte{1, 2}).Serialize()}, - {KeyPath: newPath([]byte{1, 2, 3}).Serialize()}, + {KeyPath: NewPath([]byte{1}).Serialize()}, + {KeyPath: NewPath([]byte{1, 2}).Serialize()}, + {KeyPath: NewPath([]byte{1, 2, 3}).Serialize()}, }, proofKey: []byte{1, 2, 3}, expectedErr: nil, @@ -1609,10 +1609,10 @@ func TestVerifyProofPath(t *testing.T) { { name: "repeat nodes", path: []ProofNode{ - {KeyPath: newPath([]byte{1}).Serialize()}, - {KeyPath: newPath([]byte{1}).Serialize()}, - {KeyPath: newPath([]byte{1, 2}).Serialize()}, - {KeyPath: newPath([]byte{1, 2, 3}).Serialize()}, + {KeyPath: NewPath([]byte{1}).Serialize()}, + {KeyPath: NewPath([]byte{1}).Serialize()}, + {KeyPath: NewPath([]byte{1, 2}).Serialize()}, + {KeyPath: NewPath([]byte{1, 2, 3}).Serialize()}, }, proofKey: []byte{1, 2, 3}, expectedErr: ErrNonIncreasingProofNodes, @@ -1620,10 +1620,10 @@ func TestVerifyProofPath(t *testing.T) { { name: "repeat nodes 2", path: []ProofNode{ - {KeyPath: newPath([]byte{1}).Serialize()}, - {KeyPath: newPath([]byte{1, 2}).Serialize()}, - {KeyPath: newPath([]byte{1, 2}).Serialize()}, - {KeyPath: newPath([]byte{1, 2, 3}).Serialize()}, + {KeyPath: NewPath([]byte{1}).Serialize()}, + {KeyPath: NewPath([]byte{1, 2}).Serialize()}, + {KeyPath: NewPath([]byte{1, 2}).Serialize()}, + {KeyPath: NewPath([]byte{1, 2, 3}).Serialize()}, }, proofKey: []byte{1, 2, 3}, expectedErr: ErrNonIncreasingProofNodes, @@ -1631,10 +1631,10 @@ func TestVerifyProofPath(t *testing.T) { { name: "repeat nodes 3", path: []ProofNode{ - {KeyPath: newPath([]byte{1}).Serialize()}, - {KeyPath: newPath([]byte{1, 2}).Serialize()}, - {KeyPath: newPath([]byte{1, 2, 3}).Serialize()}, - {KeyPath: newPath([]byte{1, 2, 3}).Serialize()}, + {KeyPath: NewPath([]byte{1}).Serialize()}, + {KeyPath: NewPath([]byte{1, 2}).Serialize()}, + {KeyPath: NewPath([]byte{1, 2, 3}).Serialize()}, + {KeyPath: NewPath([]byte{1, 2, 3}).Serialize()}, }, proofKey: []byte{1, 2, 3}, expectedErr: ErrProofNodeNotForKey, @@ -1642,8 +1642,8 @@ func TestVerifyProofPath(t *testing.T) { { name: "oddLength key with value", path: []ProofNode{ - {KeyPath: newPath([]byte{1}).Serialize()}, - {KeyPath: newPath([]byte{1, 2}).Serialize()}, + {KeyPath: NewPath([]byte{1}).Serialize()}, + {KeyPath: NewPath([]byte{1, 2}).Serialize()}, {KeyPath: SerializedPath{Value: []byte{1, 2, 240}, NibbleLength: 5}, ValueOrHash: Some([]byte{1})}, }, proofKey: []byte{1, 2, 3}, @@ -1653,7 +1653,7 @@ func TestVerifyProofPath(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - err := verifyProofPath(tt.path, newPath(tt.proofKey)) + err := verifyProofPath(tt.path, NewPath(tt.proofKey)) require.ErrorIs(t, err, tt.expectedErr) }) } diff --git a/x/merkledb/trie.go b/x/merkledb/trie.go index 47f0860b0b76..fc788db9879c 100644 --- a/x/merkledb/trie.go +++ b/x/merkledb/trie.go @@ -24,13 +24,13 @@ type ReadOnlyTrie interface { // get the value associated with the key in path form // database.ErrNotFound if the key is not present - getValue(key path, lock bool) ([]byte, error) + getValue(key Path, lock bool) ([]byte, error) // GetMerkleRoot returns the merkle root of the Trie GetMerkleRoot(ctx context.Context) (ids.ID, error) // get an editable copy of the node with the given key path - getEditableNode(key path) (*node, error) + getEditableNode(key Path) (*node, error) // GetProof generates a proof of the value associated with a particular key, or a proof of its absence from the trie GetProof(ctx context.Context, bytesPath []byte) (*Proof, error) diff --git a/x/merkledb/trie_test.go b/x/merkledb/trie_test.go index 530a2db633fc..c3fe19333f72 100644 --- a/x/merkledb/trie_test.go +++ b/x/merkledb/trie_test.go @@ -24,7 +24,7 @@ func getNodeValue(t ReadOnlyTrie, key string) ([]byte, error) { if err := asTrieView.calculateNodeIDs(context.Background()); err != nil { return nil, err } - path := newPath([]byte(key)) + path := NewPath([]byte(key)) nodePath, err := asTrieView.getPathTo(path) if err != nil { return nil, err @@ -41,7 +41,7 @@ func getNodeValue(t ReadOnlyTrie, key string) ([]byte, error) { if err != nil { return nil, err } - path := newPath([]byte(key)) + path := NewPath([]byte(key)) nodePath, err := view.(*trieView).getPathTo(path) if err != nil { return nil, err @@ -112,7 +112,7 @@ func TestTrieViewGetPathTo(t *testing.T) { trie, ok := trieIntf.(*trieView) require.True(ok) - path, err := trie.getPathTo(newPath(nil)) + path, err := trie.getPathTo(NewPath(nil)) require.NoError(err) // Just the root @@ -126,13 +126,13 @@ func TestTrieViewGetPathTo(t *testing.T) { err = trie.calculateNodeIDs(context.Background()) require.NoError(err) - path, err = trie.getPathTo(newPath(key1)) + path, err = trie.getPathTo(NewPath(key1)) require.NoError(err) // Root and 1 value require.Len(path, 2) require.Equal(trie.root, path[0]) - require.Equal(newPath(key1), path[1].key) + require.Equal(NewPath(key1), path[1].key) // Insert another key which is a child of the first key2 := []byte{0, 1} @@ -141,12 +141,12 @@ func TestTrieViewGetPathTo(t *testing.T) { err = trie.calculateNodeIDs(context.Background()) require.NoError(err) - path, err = trie.getPathTo(newPath(key2)) + path, err = trie.getPathTo(NewPath(key2)) require.NoError(err) require.Len(path, 3) require.Equal(trie.root, path[0]) - require.Equal(newPath(key1), path[1].key) - require.Equal(newPath(key2), path[2].key) + require.Equal(NewPath(key1), path[1].key) + require.Equal(NewPath(key2), path[2].key) // Insert a key which shares no prefix with the others key3 := []byte{255} @@ -155,32 +155,32 @@ func TestTrieViewGetPathTo(t *testing.T) { err = trie.calculateNodeIDs(context.Background()) require.NoError(err) - path, err = trie.getPathTo(newPath(key3)) + path, err = trie.getPathTo(NewPath(key3)) require.NoError(err) require.Len(path, 2) require.Equal(trie.root, path[0]) - require.Equal(newPath(key3), path[1].key) + require.Equal(NewPath(key3), path[1].key) // Other key paths not affected - path, err = trie.getPathTo(newPath(key2)) + path, err = trie.getPathTo(NewPath(key2)) require.NoError(err) require.Len(path, 3) require.Equal(trie.root, path[0]) - require.Equal(newPath(key1), path[1].key) - require.Equal(newPath(key2), path[2].key) + require.Equal(NewPath(key1), path[1].key) + require.Equal(NewPath(key2), path[2].key) // Gets closest node when key doesn't exist key4 := []byte{0, 1, 2} - path, err = trie.getPathTo(newPath(key4)) + path, err = trie.getPathTo(NewPath(key4)) require.NoError(err) require.Len(path, 3) require.Equal(trie.root, path[0]) - require.Equal(newPath(key1), path[1].key) - require.Equal(newPath(key2), path[2].key) + require.Equal(NewPath(key1), path[1].key) + require.Equal(NewPath(key2), path[2].key) // Gets just root when key doesn't exist and no key shares a prefix key5 := []byte{128} - path, err = trie.getPathTo(newPath(key5)) + path, err = trie.getPathTo(NewPath(key5)) require.NoError(err) require.Len(path, 1) require.Equal(trie.root, path[0]) @@ -272,7 +272,7 @@ func Test_Trie_WriteToDB(t *testing.T) { err = trie.CommitToDB(context.Background()) require.NoError(t, err) - p := newPath([]byte("key")) + p := NewPath([]byte("key")) rawBytes, err := dbTrie.nodeDB.Get(p.Bytes()) require.NoError(t, err) node, err := parseNode(p, rawBytes) diff --git a/x/merkledb/trieview.go b/x/merkledb/trieview.go index b94319ab0561..c3df36bddde7 100644 --- a/x/merkledb/trieview.go +++ b/x/merkledb/trieview.go @@ -93,7 +93,7 @@ type trieView struct { // yet reflected in the trie's structure. This allows us to // defer the cost of updating the trie until we calculate node IDs. // A Nothing value indicates that the key has been removed. - unappliedValueChanges map[path]Maybe[[]byte] + unappliedValueChanges map[Path]Maybe[[]byte] db *Database @@ -170,7 +170,7 @@ func newTrieView( parentTrie: parentTrie, changes: newChangeSummary(estimatedSize), estimatedSize: estimatedSize, - unappliedValueChanges: make(map[path]Maybe[[]byte], estimatedSize), + unappliedValueChanges: make(map[Path]Maybe[[]byte], estimatedSize), }, nil } @@ -196,7 +196,7 @@ func newTrieViewWithChanges( parentTrie: parentTrie, changes: changes, estimatedSize: estimatedSize, - unappliedValueChanges: make(map[path]Maybe[[]byte], estimatedSize), + unappliedValueChanges: make(map[Path]Maybe[[]byte], estimatedSize), }, nil } @@ -267,7 +267,7 @@ func (t *trieView) calculateNodeIDsHelper(ctx context.Context, n *node, eg *errg for childIndex, child := range n.children { childIndex, child := childIndex, child - childPath := n.key + path(childIndex) + child.compressedPath + childPath := n.key + Path(childIndex) + child.compressedPath childNodeChange, ok := t.changes.nodes[childPath] if !ok { // This child wasn't changed. @@ -342,7 +342,7 @@ func (t *trieView) getProof(ctx context.Context, key []byte) (*Proof, error) { } // Get the node at the given path, or the node closest to it. - keyPath := newPath(key) + keyPath := NewPath(key) proofPath, err := t.getPathTo(keyPath) if err != nil { @@ -372,7 +372,7 @@ func (t *trieView) getProof(ctx context.Context, key []byte) (*Proof, error) { return proof, nil } - childPath := closestNode.key + path(nextIndex) + child.compressedPath + childPath := closestNode.key + Path(nextIndex) + child.compressedPath childNode, err := t.getNodeFromParent(closestNode, childPath) if err != nil { return nil, err @@ -861,7 +861,7 @@ func (t *trieView) GetValues(_ context.Context, keys [][]byte) ([][]byte, []erro valueErrors := make([]error, len(keys)) for i, key := range keys { - results[i], valueErrors[i] = t.getValueCopy(newPath(key), false) + results[i], valueErrors[i] = t.getValueCopy(NewPath(key), false) } return results, valueErrors } @@ -869,12 +869,12 @@ func (t *trieView) GetValues(_ context.Context, keys [][]byte) ([][]byte, []erro // GetValue returns the value for the given [key]. // Returns database.ErrNotFound if it doesn't exist. func (t *trieView) GetValue(_ context.Context, key []byte) ([]byte, error) { - return t.getValueCopy(newPath(key), true) + return t.getValueCopy(NewPath(key), true) } // getValueCopy returns a copy of the value for the given [key]. // Returns database.ErrNotFound if it doesn't exist. -func (t *trieView) getValueCopy(key path, lock bool) ([]byte, error) { +func (t *trieView) getValueCopy(key Path, lock bool) ([]byte, error) { val, err := t.getValue(key, lock) if err != nil { return nil, err @@ -882,7 +882,7 @@ func (t *trieView) getValueCopy(key path, lock bool) ([]byte, error) { return slices.Clone(val), nil } -func (t *trieView) getValue(key path, lock bool) ([]byte, error) { +func (t *trieView) getValue(key Path, lock bool) ([]byte, error) { if lock { t.lock.RLock() defer t.lock.RUnlock() @@ -938,7 +938,7 @@ func (t *trieView) insert(key []byte, value []byte) error { valCopy := slices.Clone(value) - if err := t.recordValueChange(newPath(key), Some(valCopy)); err != nil { + if err := t.recordValueChange(NewPath(key), Some(valCopy)); err != nil { return err } @@ -972,7 +972,7 @@ func (t *trieView) remove(key []byte) error { // the trie has been changed, so invalidate all children and remove them from tracking t.invalidateChildren() - if err := t.recordValueChange(newPath(key), Nothing[[]byte]()); err != nil { + if err := t.recordValueChange(NewPath(key), Nothing[[]byte]()); err != nil { return err } @@ -990,7 +990,7 @@ func (t *trieView) applyChangedValuesToTrie(ctx context.Context) error { defer span.End() unappliedValues := t.unappliedValueChanges - t.unappliedValueChanges = make(map[path]Maybe[[]byte], t.estimatedSize) + t.unappliedValueChanges = make(map[Path]Maybe[[]byte], t.estimatedSize) for key, change := range unappliedValues { if change.IsNothing() { @@ -1076,7 +1076,7 @@ func (t *trieView) deleteEmptyNodes(nodePath []*node) error { // given [key], if it's in the trie, or the node with the largest prefix of // the [key] if it isn't in the trie. // Always returns at least the root node. -func (t *trieView) getPathTo(key path) ([]*node, error) { +func (t *trieView) getPathTo(key Path) ([]*node, error) { var ( // all paths start at the root currentNode = t.root @@ -1113,7 +1113,7 @@ func (t *trieView) getPathTo(key path) ([]*node, error) { return nodes, nil } -func getLengthOfCommonPrefix(first, second path) int { +func getLengthOfCommonPrefix(first, second Path) int { commonIndex := 0 for len(first) > commonIndex && len(second) > commonIndex && first[commonIndex] == second[commonIndex] { commonIndex++ @@ -1124,7 +1124,7 @@ func getLengthOfCommonPrefix(first, second path) int { // Get a copy of the node matching the passed key from the trie // Used by views to get nodes from their ancestors // assumes that [t.needsRecalculation] is false -func (t *trieView) getEditableNode(key path) (*node, error) { +func (t *trieView) getEditableNode(key Path) (*node, error) { t.lock.RLock() defer t.lock.RUnlock() @@ -1150,7 +1150,7 @@ func (t *trieView) getEditableNode(key path) (*node, error) { // Inserts a key/value pair into the trie. // Assumes [t.lock] is held. func (t *trieView) insertIntoTrie( - key path, + key Path, value Maybe[[]byte], ) (*node, error) { // find the node that most closely matches [key] @@ -1262,7 +1262,7 @@ func (t *trieView) recordNodeDeleted(after *node) error { // Records that the node associated with the given key has been changed. // Assumes [t.lock] is held. -func (t *trieView) recordKeyChange(key path, after *node) error { +func (t *trieView) recordKeyChange(key Path, after *node) error { t.needsRecalculation = true if existing, ok := t.changes.nodes[key]; ok { @@ -1289,7 +1289,7 @@ func (t *trieView) recordKeyChange(key path, after *node) error { // Doesn't actually change the trie data structure. // That's deferred until we calculate node IDs. // Assumes [t.lock] is held. -func (t *trieView) recordValueChange(key path, value Maybe[[]byte]) error { +func (t *trieView) recordValueChange(key Path, value Maybe[[]byte]) error { t.needsRecalculation = true // record the value change so that it can be inserted @@ -1323,7 +1323,7 @@ func (t *trieView) recordValueChange(key path, value Maybe[[]byte]) error { // Removes the provided [key] from the trie. // Assumes [t.lock] write lock is held. -func (t *trieView) removeFromTrie(key path) error { +func (t *trieView) removeFromTrie(key Path) error { nodePath, err := t.getPathTo(key) if err != nil { return err @@ -1367,7 +1367,7 @@ func (t *trieView) removeFromTrie(key path) error { // uses the [parent] node to initialize the child node's ID. // Returns database.ErrNotFound if the child doesn't exist. // Assumes [t.lock] write or read lock is held. -func (t *trieView) getNodeFromParent(parent *node, key path) (*node, error) { +func (t *trieView) getNodeFromParent(parent *node, key Path) (*node, error) { // confirm the child exists and get its ID before attempting to load it if child, exists := parent.children[key[len(parent.key)]]; exists { return t.getNodeWithID(child.id, key) @@ -1381,7 +1381,7 @@ func (t *trieView) getNodeFromParent(parent *node, key path) (*node, error) { // sets the node's ID to [id]. // Returns database.ErrNotFound if the node doesn't exist. // Assumes [t.lock] write or read lock is held. -func (t *trieView) getNodeWithID(id ids.ID, key path) (*node, error) { +func (t *trieView) getNodeWithID(id ids.ID, key Path) (*node, error) { // check for the key within the changed nodes if nodeChange, isChanged := t.changes.nodes[key]; isChanged { t.db.metrics.ViewNodeCacheHit() diff --git a/x/sync/syncmanager.go b/x/sync/syncmanager.go index c00724df0e45..1845c4fed552 100644 --- a/x/sync/syncmanager.go +++ b/x/sync/syncmanager.go @@ -12,11 +12,11 @@ import ( "time" "go.uber.org/zap" - "golang.org/x/exp/maps" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/utils/logging" "github.com/ava-labs/avalanchego/x/merkledb" + "github.com/google/btree" ) const ( @@ -376,46 +376,64 @@ func (m *StateSyncManager) findNextKey( return nil, err } - localRoot := localProof.Path[0] - receivedRoot := receivedProofNodes[0] - rootsMatch := merkledb.MaybeByteSliceEquals(localRoot.ValueOrHash, receivedRoot.ValueOrHash) - rootsMatch = rootsMatch && localRoot.KeyPath.Equal(receivedRoot.KeyPath) - rootsMatch = rootsMatch && maps.Equal(localRoot.Children, receivedRoot.Children) - if rootsMatch { - // The roots match so we must have the same trie. - return nil, nil + // If remote proof is an exclusion proof, remove the last node from it. + if bytes.Compare(receivedProofNodes[len(receivedProofNodes)-1].KeyPath.Value, lastReceivedKey) > 0 { + receivedProofNodes = receivedProofNodes[:len(receivedProofNodes)-1] } - for i := 0; i < len(receivedProofNodes); i++ { - theirNode := receivedProofNodes[i] - theirNextNode := receivedProofNodes[i+1] - ourNode := localProof.Path[i] - ourNextNode := localProof.Path[i+1] + theirImpliedKeys := btree.NewG(2, func(p1, p2 merkledb.Path) bool { + return p1.Compare(p2) < 0 + }) + ourImpliedKeys := btree.NewG(2, func(p1, p2 merkledb.Path) bool { + return p1.Compare(p2) < 0 + }) - ourNextNodeIndex := ourNextNode.KeyPath.Value[len(ourNode.KeyPath.Value)] - ourNextNodeHash := ourNode.Children[ourNextNodeIndex] + for _, node := range receivedProofNodes { + theirImpliedKeys.ReplaceOrInsert(node.KeyPath.Deserialize()) + for idx := range node.Children { + childPath := node.KeyPath.AppendNibble(idx) + theirImpliedKeys.ReplaceOrInsert(childPath.Deserialize()) + } + } - theirNextNodeIndex := theirNextNode.KeyPath.Value[len(theirNode.KeyPath.Value)] - theirNextNodeHash := theirNode.Children[theirNextNodeIndex] + for _, node := range localProof.Path { + ourImpliedKeys.ReplaceOrInsert(node.KeyPath.Deserialize()) + for idx := range node.Children { + childPath := node.KeyPath.AppendNibble(idx) + ourImpliedKeys.ReplaceOrInsert(childPath.Deserialize()) + } + } - if ourNextNodeHash == theirNextNodeHash { - // Our proof and their proof have the same next node. - // The next key to query is the first difference in our children. - for j := theirNextNodeIndex; j < merkledb.NodeBranchFactor; j++ { - if ourNode.Children[j] != theirNode.Children[j] { - nextStartKey := make([]byte, len(theirNode.KeyPath.Value)+1) - copy(nextStartKey, theirNode.KeyPath.Value) - nextStartKey[len(theirNode.KeyPath.Value)] = byte(j) - return nextStartKey, nil - } - } + // Delete all implied keys <= lastReceivedKey + lastReceivedPath := merkledb.NewPath(lastReceivedKey) + for minPath, ok := theirImpliedKeys.Min(); ok; minPath, ok = theirImpliedKeys.Min() { + if minPath.Compare(lastReceivedPath) < 0 { + theirImpliedKeys.DeleteMin() + continue } + break + } + for minPath, ok := ourImpliedKeys.Min(); ok; minPath, ok = ourImpliedKeys.Min() { + if minPath.Compare(lastReceivedPath) < 0 { + ourImpliedKeys.DeleteMin() + continue + } + break + } - // Our proof and their proof have different next nodes. - // There must be a difference below here in the trie. + // Find smallest key that is in theirImpliedKeys but not in ourImpliedKeys + for minPath, ok := theirImpliedKeys.Min(); ok; minPath, ok = theirImpliedKeys.Min() { + if !ourImpliedKeys.Has(minPath) { + return minPath.Serialize().Value, nil + } + theirImpliedKeys.DeleteMin() + } + + if len(lastReceivedKey) > 0 && bytes.Compare(lastReceivedKey, rangeEnd) >= 0 { + return nil, nil } - return nil, errors.New("TODO") + return lastReceivedKey, nil // // If the received proof's last node has a key is after the lastReceivedKey, this is an exclusion proof. // // if it is an exclusion proof, then we can remove the last node to get a valid proof for some prefix of the lastReceivedKey From ba7f354e3f1cf7a8b0c1f07eadc669b890e9ff6b Mon Sep 17 00:00:00 2001 From: Dan Laine Date: Tue, 18 Apr 2023 11:24:09 -0400 Subject: [PATCH 03/21] WIP --- x/sync/syncmanager.go | 76 +++++++++++++++++++++++++++++++------------ 1 file changed, 56 insertions(+), 20 deletions(-) diff --git a/x/sync/syncmanager.go b/x/sync/syncmanager.go index 1845c4fed552..3626d7d3e5c1 100644 --- a/x/sync/syncmanager.go +++ b/x/sync/syncmanager.go @@ -381,55 +381,91 @@ func (m *StateSyncManager) findNextKey( receivedProofNodes = receivedProofNodes[:len(receivedProofNodes)-1] } + // If local proof is an exclusion proof, remove the last node from it. + if bytes.Compare(localProof.Path[len(localProof.Path)-1].KeyPath.Value, lastReceivedKey) > 0 { + localProof.Path = localProof.Path[:len(localProof.Path)-1] + } + + // Min element is the smallest path whose existence is implied by the remote proof. theirImpliedKeys := btree.NewG(2, func(p1, p2 merkledb.Path) bool { return p1.Compare(p2) < 0 }) + // Min element is the smallest path whose existence is implied by the local proof. ourImpliedKeys := btree.NewG(2, func(p1, p2 merkledb.Path) bool { return p1.Compare(p2) < 0 }) + lastReceivedPath := merkledb.NewPath(lastReceivedKey) + + rangeEndPath := merkledb.NewPath(rangeEnd) + for _, node := range receivedProofNodes { - theirImpliedKeys.ReplaceOrInsert(node.KeyPath.Deserialize()) + paths := make([]merkledb.Path, 0, len(node.Children)+1) + paths = append(paths, node.KeyPath.Deserialize()) + for idx := range node.Children { childPath := node.KeyPath.AppendNibble(idx) - theirImpliedKeys.ReplaceOrInsert(childPath.Deserialize()) + paths = append(paths, childPath.Deserialize()) + } + + // Only consider paths greater than the last received path + // and less than the range end path (if applicable). + for _, path := range paths { + if path.Compare(lastReceivedPath) > 0 && + (len(rangeEnd) == 0 || path.Compare(rangeEndPath) < 0) { + theirImpliedKeys.ReplaceOrInsert(path) + } } } for _, node := range localProof.Path { - ourImpliedKeys.ReplaceOrInsert(node.KeyPath.Deserialize()) + paths := make([]merkledb.Path, 0, len(node.Children)+1) + paths = append(paths, node.KeyPath.Deserialize()) + for idx := range node.Children { childPath := node.KeyPath.AppendNibble(idx) - ourImpliedKeys.ReplaceOrInsert(childPath.Deserialize()) + paths = append(paths, childPath.Deserialize()) + } + + // Only consider paths greater than the last received path + // and less than the range end path (if applicable). + for _, path := range paths { + if path.Compare(lastReceivedPath) > 0 && + (len(rangeEnd) == 0 || path.Compare(rangeEndPath) < 0) { + ourImpliedKeys.ReplaceOrInsert(path) + } } } - // Delete all implied keys <= lastReceivedKey - lastReceivedPath := merkledb.NewPath(lastReceivedKey) + // Find smallest key that is in theirImpliedKeys but not in ourImpliedKeys for minPath, ok := theirImpliedKeys.Min(); ok; minPath, ok = theirImpliedKeys.Min() { - if minPath.Compare(lastReceivedPath) < 0 { + if ourImpliedKeys.Has(minPath) { theirImpliedKeys.DeleteMin() continue } - break - } - for minPath, ok := ourImpliedKeys.Min(); ok; minPath, ok = ourImpliedKeys.Min() { - if minPath.Compare(lastReceivedPath) < 0 { - ourImpliedKeys.DeleteMin() + + // See if there's a key in ourImpliedKeys that is a suffix of minPath + suffixPathExists := false + ourImpliedKeys.DescendGreaterThan(minPath, func(item merkledb.Path) bool { + if item.HasPrefix(minPath) { + suffixPathExists = true + return false + } + return true + }) + if suffixPathExists { + theirImpliedKeys.DeleteMin() continue } - break - } - // Find smallest key that is in theirImpliedKeys but not in ourImpliedKeys - for minPath, ok := theirImpliedKeys.Min(); ok; minPath, ok = theirImpliedKeys.Min() { - if !ourImpliedKeys.Has(minPath) { - return minPath.Serialize().Value, nil + minPathBytes := minPath.Serialize().Value + if len(rangeEnd) > 0 && bytes.Compare(minPathBytes, rangeEnd) >= 0 { + return nil, nil // TODO should we continue? Delete such elements from trees? } - theirImpliedKeys.DeleteMin() + return minPathBytes, nil } - if len(lastReceivedKey) > 0 && bytes.Compare(lastReceivedKey, rangeEnd) >= 0 { + if len(rangeEnd) > 0 && bytes.Compare(lastReceivedKey, rangeEnd) >= 0 { return nil, nil } From b8ed1f32d40e6f78eb9e6678da072b0549309da2 Mon Sep 17 00:00:00 2001 From: Dan Laine Date: Tue, 18 Apr 2023 11:39:04 -0400 Subject: [PATCH 04/21] WIP --- x/sync/syncmanager.go | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/x/sync/syncmanager.go b/x/sync/syncmanager.go index 3626d7d3e5c1..66e9ebfe4fd2 100644 --- a/x/sync/syncmanager.go +++ b/x/sync/syncmanager.go @@ -362,6 +362,11 @@ func (m *StateSyncManager) getAndApplyRangeProof(ctx context.Context, workItem * m.completeWorkItem(ctx, workItem, largestHandledKey, rootID, proof.EndProof) } +type pathAndID struct { + path merkledb.Path + id ids.ID +} + // findNextKey attempts to find what key to query next based on the differences between // the proof of the last received key in the local trie vs proof for the last received key recently received by the sync manager. func (m *StateSyncManager) findNextKey( @@ -387,12 +392,12 @@ func (m *StateSyncManager) findNextKey( } // Min element is the smallest path whose existence is implied by the remote proof. - theirImpliedKeys := btree.NewG(2, func(p1, p2 merkledb.Path) bool { - return p1.Compare(p2) < 0 + theirImpliedKeys := btree.NewG(2, func(p1, p2 pathAndID) bool { + return p1.path.Compare(p2.path) < 0 }) // Min element is the smallest path whose existence is implied by the local proof. - ourImpliedKeys := btree.NewG(2, func(p1, p2 merkledb.Path) bool { - return p1.Compare(p2) < 0 + ourImpliedKeys := btree.NewG(2, func(p1, p2 pathAndID) bool { + return p1.path.Compare(p2.path) < 0 }) lastReceivedPath := merkledb.NewPath(lastReceivedKey) @@ -412,7 +417,7 @@ func (m *StateSyncManager) findNextKey( // and less than the range end path (if applicable). for _, path := range paths { if path.Compare(lastReceivedPath) > 0 && - (len(rangeEnd) == 0 || path.Compare(rangeEndPath) < 0) { + (len(rangeEnd) == 0 || path.Compare(rangeEndPath) <= 0) { theirImpliedKeys.ReplaceOrInsert(path) } } @@ -431,34 +436,33 @@ func (m *StateSyncManager) findNextKey( // and less than the range end path (if applicable). for _, path := range paths { if path.Compare(lastReceivedPath) > 0 && - (len(rangeEnd) == 0 || path.Compare(rangeEndPath) < 0) { + (len(rangeEnd) == 0 || path.Compare(rangeEndPath) <= 0) { ourImpliedKeys.ReplaceOrInsert(path) } } } - // Find smallest key that is in theirImpliedKeys but not in ourImpliedKeys - for minPath, ok := theirImpliedKeys.Min(); ok; minPath, ok = theirImpliedKeys.Min() { - if ourImpliedKeys.Has(minPath) { - theirImpliedKeys.DeleteMin() + for maxPath, ok := theirImpliedKeys.Max(); ok; maxPath, ok = theirImpliedKeys.Max() { + if !ourImpliedKeys.Has(maxPath) { + theirImpliedKeys.DeleteMax() continue } // See if there's a key in ourImpliedKeys that is a suffix of minPath suffixPathExists := false - ourImpliedKeys.DescendGreaterThan(minPath, func(item merkledb.Path) bool { - if item.HasPrefix(minPath) { + ourImpliedKeys.DescendGreaterThan(maxPath, func(item merkledb.Path) bool { + if item.HasPrefix(maxPath) { suffixPathExists = true return false } return true }) if suffixPathExists { - theirImpliedKeys.DeleteMin() + theirImpliedKeys.DeleteMax() continue } - minPathBytes := minPath.Serialize().Value + minPathBytes := maxPath.Serialize().Value if len(rangeEnd) > 0 && bytes.Compare(minPathBytes, rangeEnd) >= 0 { return nil, nil // TODO should we continue? Delete such elements from trees? } From b0233a832a6866a83164063ada56d6a202d49121 Mon Sep 17 00:00:00 2001 From: Dan Laine Date: Tue, 18 Apr 2023 11:42:33 -0400 Subject: [PATCH 05/21] WIP --- x/sync/syncmanager.go | 49 ++++++++++++++++++++++++++----------------- 1 file changed, 30 insertions(+), 19 deletions(-) diff --git a/x/sync/syncmanager.go b/x/sync/syncmanager.go index 66e9ebfe4fd2..eb4e1b8704b3 100644 --- a/x/sync/syncmanager.go +++ b/x/sync/syncmanager.go @@ -405,39 +405,50 @@ func (m *StateSyncManager) findNextKey( rangeEndPath := merkledb.NewPath(rangeEnd) for _, node := range receivedProofNodes { - paths := make([]merkledb.Path, 0, len(node.Children)+1) - paths = append(paths, node.KeyPath.Deserialize()) + pathAndIDs := make([]pathAndID, 0, len(node.Children)+1) + pathAndIDs = append(pathAndIDs, pathAndID{ + path: node.KeyPath.Deserialize(), + id: ids.Empty, + }) - for idx := range node.Children { + for idx, id := range node.Children { childPath := node.KeyPath.AppendNibble(idx) - paths = append(paths, childPath.Deserialize()) + pathAndIDs = append(pathAndIDs, pathAndID{ + path: childPath.Deserialize(), + id: id, + }) } // Only consider paths greater than the last received path // and less than the range end path (if applicable). - for _, path := range paths { - if path.Compare(lastReceivedPath) > 0 && - (len(rangeEnd) == 0 || path.Compare(rangeEndPath) <= 0) { - theirImpliedKeys.ReplaceOrInsert(path) + for _, pathAndID := range pathAndIDs { + if pathAndID.path.Compare(lastReceivedPath) > 0 && + (len(rangeEnd) == 0 || pathAndID.path.Compare(rangeEndPath) <= 0) { + theirImpliedKeys.ReplaceOrInsert(pathAndID) } } } - for _, node := range localProof.Path { - paths := make([]merkledb.Path, 0, len(node.Children)+1) - paths = append(paths, node.KeyPath.Deserialize()) + pathAndIDs := make([]pathAndID, 0, len(node.Children)+1) + pathAndIDs = append(pathAndIDs, pathAndID{ + path: node.KeyPath.Deserialize(), + id: ids.Empty, + }) - for idx := range node.Children { + for idx, id := range node.Children { childPath := node.KeyPath.AppendNibble(idx) - paths = append(paths, childPath.Deserialize()) + pathAndIDs = append(pathAndIDs, pathAndID{ + path: childPath.Deserialize(), + id: id, + }) } // Only consider paths greater than the last received path // and less than the range end path (if applicable). - for _, path := range paths { - if path.Compare(lastReceivedPath) > 0 && - (len(rangeEnd) == 0 || path.Compare(rangeEndPath) <= 0) { - ourImpliedKeys.ReplaceOrInsert(path) + for _, pathAndID := range pathAndIDs { + if pathAndID.path.Compare(lastReceivedPath) > 0 && + (len(rangeEnd) == 0 || pathAndID.path.Compare(rangeEndPath) <= 0) { + ourImpliedKeys.ReplaceOrInsert(pathAndID) } } } @@ -450,8 +461,8 @@ func (m *StateSyncManager) findNextKey( // See if there's a key in ourImpliedKeys that is a suffix of minPath suffixPathExists := false - ourImpliedKeys.DescendGreaterThan(maxPath, func(item merkledb.Path) bool { - if item.HasPrefix(maxPath) { + ourImpliedKeys.DescendGreaterThan(maxPath, func(item pathAndID) bool { + if item.path.HasPrefix(maxPath.path) { suffixPathExists = true return false } From 3b646947b5c44d0522675a9ab44a2e9ef9269bcb Mon Sep 17 00:00:00 2001 From: Dan Laine Date: Tue, 18 Apr 2023 11:48:46 -0400 Subject: [PATCH 06/21] WIP --- x/sync/syncmanager.go | 37 +++++++++++++------------------------ 1 file changed, 13 insertions(+), 24 deletions(-) diff --git a/x/sync/syncmanager.go b/x/sync/syncmanager.go index eb4e1b8704b3..ed0c4a5f9419 100644 --- a/x/sync/syncmanager.go +++ b/x/sync/syncmanager.go @@ -376,6 +376,10 @@ func (m *StateSyncManager) findNextKey( lastReceivedKey []byte, receivedProofNodes []merkledb.ProofNode, ) ([]byte, error) { + if len(rangeEnd) > 0 && bytes.Compare(lastReceivedKey, rangeEnd) >= 0 { + return nil, nil + } + localProof, err := m.config.SyncDB.GetProof(ctx, lastReceivedKey) if err != nil { return nil, err @@ -453,35 +457,20 @@ func (m *StateSyncManager) findNextKey( } } + // Find greatest implied prefix such that: + // * We don't locally have the prefix + // * We have a different ID for the prefix + var result []byte for maxPath, ok := theirImpliedKeys.Max(); ok; maxPath, ok = theirImpliedKeys.Max() { - if !ourImpliedKeys.Has(maxPath) { + local, ok := ourImpliedKeys.Get(maxPath) + if !ok || local.id != maxPath.id { + result = maxPath.path.Serialize().Value theirImpliedKeys.DeleteMax() continue } - - // See if there's a key in ourImpliedKeys that is a suffix of minPath - suffixPathExists := false - ourImpliedKeys.DescendGreaterThan(maxPath, func(item pathAndID) bool { - if item.path.HasPrefix(maxPath.path) { - suffixPathExists = true - return false - } - return true - }) - if suffixPathExists { - theirImpliedKeys.DeleteMax() - continue - } - - minPathBytes := maxPath.Serialize().Value - if len(rangeEnd) > 0 && bytes.Compare(minPathBytes, rangeEnd) >= 0 { - return nil, nil // TODO should we continue? Delete such elements from trees? - } - return minPathBytes, nil } - - if len(rangeEnd) > 0 && bytes.Compare(lastReceivedKey, rangeEnd) >= 0 { - return nil, nil + if len(result) > 0 { + return result, nil } return lastReceivedKey, nil From 598f60097317b9a043e716f2ca227386e2d6882b Mon Sep 17 00:00:00 2001 From: Dan Laine Date: Tue, 18 Apr 2023 12:54:53 -0400 Subject: [PATCH 07/21] WIP --- x/sync/syncmanager.go | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/x/sync/syncmanager.go b/x/sync/syncmanager.go index ed0c4a5f9419..b6c124347065 100644 --- a/x/sync/syncmanager.go +++ b/x/sync/syncmanager.go @@ -395,18 +395,18 @@ func (m *StateSyncManager) findNextKey( localProof.Path = localProof.Path[:len(localProof.Path)-1] } - // Min element is the smallest path whose existence is implied by the remote proof. + // Key prefixes implied by the remote proof. theirImpliedKeys := btree.NewG(2, func(p1, p2 pathAndID) bool { return p1.path.Compare(p2.path) < 0 }) - // Min element is the smallest path whose existence is implied by the local proof. + + // Key prefixes implied by the local proof. ourImpliedKeys := btree.NewG(2, func(p1, p2 pathAndID) bool { return p1.path.Compare(p2.path) < 0 }) lastReceivedPath := merkledb.NewPath(lastReceivedKey) - - rangeEndPath := merkledb.NewPath(rangeEnd) + // rangeEndPath := merkledb.NewPath(rangeEnd) for _, node := range receivedProofNodes { pathAndIDs := make([]pathAndID, 0, len(node.Children)+1) @@ -416,6 +416,8 @@ func (m *StateSyncManager) findNextKey( }) for idx, id := range node.Children { + idx := idx + id := id childPath := node.KeyPath.AppendNibble(idx) pathAndIDs = append(pathAndIDs, pathAndID{ path: childPath.Deserialize(), @@ -426,8 +428,7 @@ func (m *StateSyncManager) findNextKey( // Only consider paths greater than the last received path // and less than the range end path (if applicable). for _, pathAndID := range pathAndIDs { - if pathAndID.path.Compare(lastReceivedPath) > 0 && - (len(rangeEnd) == 0 || pathAndID.path.Compare(rangeEndPath) <= 0) { + if pathAndID.path.Compare(lastReceivedPath) >= 0 { theirImpliedKeys.ReplaceOrInsert(pathAndID) } } @@ -440,6 +441,8 @@ func (m *StateSyncManager) findNextKey( }) for idx, id := range node.Children { + idx := idx + id := id childPath := node.KeyPath.AppendNibble(idx) pathAndIDs = append(pathAndIDs, pathAndID{ path: childPath.Deserialize(), @@ -450,8 +453,7 @@ func (m *StateSyncManager) findNextKey( // Only consider paths greater than the last received path // and less than the range end path (if applicable). for _, pathAndID := range pathAndIDs { - if pathAndID.path.Compare(lastReceivedPath) > 0 && - (len(rangeEnd) == 0 || pathAndID.path.Compare(rangeEndPath) <= 0) { + if pathAndID.path.Compare(lastReceivedPath) >= 0 { ourImpliedKeys.ReplaceOrInsert(pathAndID) } } @@ -469,11 +471,11 @@ func (m *StateSyncManager) findNextKey( continue } } - if len(result) > 0 { + if len(result) > 0 && (len(rangeEnd) == 0 || bytes.Compare(result, rangeEnd) < 0) { return result, nil } - return lastReceivedKey, nil + return nil, nil // // If the received proof's last node has a key is after the lastReceivedKey, this is an exclusion proof. // // if it is an exclusion proof, then we can remove the last node to get a valid proof for some prefix of the lastReceivedKey From 0fb061bb8d53b73c3b07e3852b9ad3ca6c01e643 Mon Sep 17 00:00:00 2001 From: Dan Laine Date: Tue, 18 Apr 2023 15:31:47 -0400 Subject: [PATCH 08/21] WIP --- x/sync/syncmanager.go | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/x/sync/syncmanager.go b/x/sync/syncmanager.go index b6c124347065..6df8c5852573 100644 --- a/x/sync/syncmanager.go +++ b/x/sync/syncmanager.go @@ -14,6 +14,7 @@ import ( "go.uber.org/zap" "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/utils/hashing" "github.com/ava-labs/avalanchego/utils/logging" "github.com/ava-labs/avalanchego/x/merkledb" "github.com/google/btree" @@ -412,7 +413,7 @@ func (m *StateSyncManager) findNextKey( pathAndIDs := make([]pathAndID, 0, len(node.Children)+1) pathAndIDs = append(pathAndIDs, pathAndID{ path: node.KeyPath.Deserialize(), - id: ids.Empty, + id: ids.ID(hashing.ComputeHash256Array(node.ValueOrHash.Value())), // TODO fix }) for idx, id := range node.Children { @@ -437,7 +438,7 @@ func (m *StateSyncManager) findNextKey( pathAndIDs := make([]pathAndID, 0, len(node.Children)+1) pathAndIDs = append(pathAndIDs, pathAndID{ path: node.KeyPath.Deserialize(), - id: ids.Empty, + id: ids.ID(hashing.ComputeHash256Array(node.ValueOrHash.Value())), // TODO fix }) for idx, id := range node.Children { @@ -462,17 +463,16 @@ func (m *StateSyncManager) findNextKey( // Find greatest implied prefix such that: // * We don't locally have the prefix // * We have a different ID for the prefix - var result []byte - for maxPath, ok := theirImpliedKeys.Max(); ok; maxPath, ok = theirImpliedKeys.Max() { - local, ok := ourImpliedKeys.Get(maxPath) - if !ok || local.id != maxPath.id { - result = maxPath.path.Serialize().Value - theirImpliedKeys.DeleteMax() - continue + for minPath, ok := theirImpliedKeys.Min(); ok; minPath, ok = theirImpliedKeys.Min() { + local, ok := ourImpliedKeys.Get(minPath) + if !ok || local.id != minPath.id { + firstDiffPath := minPath.path.Serialize().Value + if len(rangeEnd) > 0 && bytes.Compare(firstDiffPath, rangeEnd) > 0 { + return nil, nil + } + return firstDiffPath, nil } - } - if len(result) > 0 && (len(rangeEnd) == 0 || bytes.Compare(result, rangeEnd) < 0) { - return result, nil + theirImpliedKeys.DeleteMin() } return nil, nil From 0b5331920504126f018f0871417798ec5b4300c6 Mon Sep 17 00:00:00 2001 From: Dan Laine Date: Tue, 18 Apr 2023 15:49:17 -0400 Subject: [PATCH 09/21] WIP --- x/sync/syncmanager.go | 38 ++++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/x/sync/syncmanager.go b/x/sync/syncmanager.go index 6df8c5852573..6396b549416c 100644 --- a/x/sync/syncmanager.go +++ b/x/sync/syncmanager.go @@ -407,7 +407,6 @@ func (m *StateSyncManager) findNextKey( }) lastReceivedPath := merkledb.NewPath(lastReceivedKey) - // rangeEndPath := merkledb.NewPath(rangeEnd) for _, node := range receivedProofNodes { pathAndIDs := make([]pathAndID, 0, len(node.Children)+1) @@ -429,7 +428,7 @@ func (m *StateSyncManager) findNextKey( // Only consider paths greater than the last received path // and less than the range end path (if applicable). for _, pathAndID := range pathAndIDs { - if pathAndID.path.Compare(lastReceivedPath) >= 0 { + if pathAndID.path.Compare(lastReceivedPath) > 0 { theirImpliedKeys.ReplaceOrInsert(pathAndID) } } @@ -454,28 +453,43 @@ func (m *StateSyncManager) findNextKey( // Only consider paths greater than the last received path // and less than the range end path (if applicable). for _, pathAndID := range pathAndIDs { - if pathAndID.path.Compare(lastReceivedPath) >= 0 { + if pathAndID.path.Compare(lastReceivedPath) > 0 { ourImpliedKeys.ReplaceOrInsert(pathAndID) } } } - // Find greatest implied prefix such that: - // * We don't locally have the prefix - // * We have a different ID for the prefix + rangeEndPath := merkledb.NewPath(rangeEnd) + var firstDiff merkledb.Path + firstDiffSet := false for minPath, ok := theirImpliedKeys.Min(); ok; minPath, ok = theirImpliedKeys.Min() { + if minPath.path.Compare(rangeEndPath) > 0 { + break + } local, ok := ourImpliedKeys.Get(minPath) if !ok || local.id != minPath.id { - firstDiffPath := minPath.path.Serialize().Value - if len(rangeEnd) > 0 && bytes.Compare(firstDiffPath, rangeEnd) > 0 { - return nil, nil - } - return firstDiffPath, nil + firstDiff = minPath.path + firstDiffSet = true + break } theirImpliedKeys.DeleteMin() } - return nil, nil + for minPath, ok := ourImpliedKeys.Min(); ok; minPath, ok = ourImpliedKeys.Min() { + if minPath.path.Compare(rangeEndPath) > 0 { + break + } + if firstDiffSet && minPath.path.Compare(firstDiff) >= 0 { + break + } + remote, ok := theirImpliedKeys.Get(minPath) + if !ok || remote.id != minPath.id { + firstDiff = minPath.path + break + } + ourImpliedKeys.DeleteMin() + } + return firstDiff.Serialize().Value, nil // // If the received proof's last node has a key is after the lastReceivedKey, this is an exclusion proof. // // if it is an exclusion proof, then we can remove the last node to get a valid proof for some prefix of the lastReceivedKey From a62b543d44fea6f15b157c3b723b54933ef3ad2e Mon Sep 17 00:00:00 2001 From: Dan Laine Date: Tue, 18 Apr 2023 15:52:25 -0400 Subject: [PATCH 10/21] WIP --- x/sync/syncmanager.go | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/x/sync/syncmanager.go b/x/sync/syncmanager.go index 6396b549416c..c6f0f4173912 100644 --- a/x/sync/syncmanager.go +++ b/x/sync/syncmanager.go @@ -459,9 +459,11 @@ func (m *StateSyncManager) findNextKey( } } - rangeEndPath := merkledb.NewPath(rangeEnd) - var firstDiff merkledb.Path - firstDiffSet := false + var ( + rangeEndPath = merkledb.NewPath(rangeEnd) + firstDiff merkledb.Path + firstDiffSet = false + ) for minPath, ok := theirImpliedKeys.Min(); ok; minPath, ok = theirImpliedKeys.Min() { if minPath.path.Compare(rangeEndPath) > 0 { break @@ -489,7 +491,11 @@ func (m *StateSyncManager) findNextKey( } ourImpliedKeys.DeleteMin() } - return firstDiff.Serialize().Value, nil + + if firstDiffSet { + return firstDiff.Serialize().Value, nil + } + return nil, nil // // If the received proof's last node has a key is after the lastReceivedKey, this is an exclusion proof. // // if it is an exclusion proof, then we can remove the last node to get a valid proof for some prefix of the lastReceivedKey From 1e91f346b305d55927ad13b2d885557e8401e3e6 Mon Sep 17 00:00:00 2001 From: Dan Laine Date: Tue, 18 Apr 2023 16:09:01 -0400 Subject: [PATCH 11/21] WIP --- x/sync/syncmanager.go | 52 +++++++++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/x/sync/syncmanager.go b/x/sync/syncmanager.go index c6f0f4173912..c98e027bcfc6 100644 --- a/x/sync/syncmanager.go +++ b/x/sync/syncmanager.go @@ -459,42 +459,46 @@ func (m *StateSyncManager) findNextKey( } } + // Find greatest implied prefix such that: + // * We don't locally have the prefix + // * We have a different ID for the prefix var ( rangeEndPath = merkledb.NewPath(rangeEnd) - firstDiff merkledb.Path - firstDiffSet = false + // firstDiff merkledb.Path + // firstDiffSet bool ) for minPath, ok := theirImpliedKeys.Min(); ok; minPath, ok = theirImpliedKeys.Min() { - if minPath.path.Compare(rangeEndPath) > 0 { - break + if len(rangeEnd) > 0 && minPath.path.Compare(rangeEndPath) > 0 { + return nil, nil } local, ok := ourImpliedKeys.Get(minPath) if !ok || local.id != minPath.id { - firstDiff = minPath.path - firstDiffSet = true - break + return minPath.path.Serialize().Value, nil + // firstDiff = minPath.path + // firstDiffSet = true } theirImpliedKeys.DeleteMin() } + return nil, nil - for minPath, ok := ourImpliedKeys.Min(); ok; minPath, ok = ourImpliedKeys.Min() { - if minPath.path.Compare(rangeEndPath) > 0 { - break - } - if firstDiffSet && minPath.path.Compare(firstDiff) >= 0 { - break - } - remote, ok := theirImpliedKeys.Get(minPath) - if !ok || remote.id != minPath.id { - firstDiff = minPath.path - break - } - ourImpliedKeys.DeleteMin() - } + // for minPath, ok := ourImpliedKeys.Min(); ok; minPath, ok = ourImpliedKeys.Min() { + // if firstDiffSet && minPath.path.Compare(firstDiff) >= 0 { + // break + // } + // if len(rangeEnd) > 0 && minPath.path.Compare(rangeEndPath) > 0 { + // break + // } + // remote, ok := theirImpliedKeys.Get(minPath) + // if !ok || remote.id != minPath.id { + // firstDiff = minPath.path + // firstDiffSet = true + // } + // ourImpliedKeys.DeleteMin() + // } + // if firstDiffSet { + // return firstDiff.Serialize().Value, nil + // } - if firstDiffSet { - return firstDiff.Serialize().Value, nil - } return nil, nil // // If the received proof's last node has a key is after the lastReceivedKey, this is an exclusion proof. From 2f783f8c7a5bd3de81af5da77d506e31f935e3d8 Mon Sep 17 00:00:00 2001 From: Dan Laine Date: Tue, 18 Apr 2023 16:10:44 -0400 Subject: [PATCH 12/21] WIP --- x/sync/syncmanager.go | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/x/sync/syncmanager.go b/x/sync/syncmanager.go index c98e027bcfc6..f6c9bdefd28f 100644 --- a/x/sync/syncmanager.go +++ b/x/sync/syncmanager.go @@ -407,6 +407,7 @@ func (m *StateSyncManager) findNextKey( }) lastReceivedPath := merkledb.NewPath(lastReceivedKey) + rangeEndPath := merkledb.NewPath(rangeEnd) for _, node := range receivedProofNodes { pathAndIDs := make([]pathAndID, 0, len(node.Children)+1) @@ -428,7 +429,8 @@ func (m *StateSyncManager) findNextKey( // Only consider paths greater than the last received path // and less than the range end path (if applicable). for _, pathAndID := range pathAndIDs { - if pathAndID.path.Compare(lastReceivedPath) > 0 { + if pathAndID.path.Compare(lastReceivedPath) > 0 && + (len(rangeEnd) == 0 || pathAndID.path.Compare(rangeEndPath) <= 0) { theirImpliedKeys.ReplaceOrInsert(pathAndID) } } @@ -453,7 +455,8 @@ func (m *StateSyncManager) findNextKey( // Only consider paths greater than the last received path // and less than the range end path (if applicable). for _, pathAndID := range pathAndIDs { - if pathAndID.path.Compare(lastReceivedPath) > 0 { + if pathAndID.path.Compare(lastReceivedPath) > 0 && + (len(rangeEnd) == 0 || pathAndID.path.Compare(rangeEndPath) <= 0) { ourImpliedKeys.ReplaceOrInsert(pathAndID) } } @@ -463,14 +466,10 @@ func (m *StateSyncManager) findNextKey( // * We don't locally have the prefix // * We have a different ID for the prefix var ( - rangeEndPath = merkledb.NewPath(rangeEnd) - // firstDiff merkledb.Path - // firstDiffSet bool + // firstDiff merkledb.Path + // firstDiffSet bool ) for minPath, ok := theirImpliedKeys.Min(); ok; minPath, ok = theirImpliedKeys.Min() { - if len(rangeEnd) > 0 && minPath.path.Compare(rangeEndPath) > 0 { - return nil, nil - } local, ok := ourImpliedKeys.Get(minPath) if !ok || local.id != minPath.id { return minPath.path.Serialize().Value, nil From f946b090d09317a2365935f942578bb5c3f4834d Mon Sep 17 00:00:00 2001 From: Dan Laine Date: Tue, 18 Apr 2023 16:15:27 -0400 Subject: [PATCH 13/21] WIP --- x/sync/syncmanager.go | 43 ++++++++++++++++++++----------------------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/x/sync/syncmanager.go b/x/sync/syncmanager.go index f6c9bdefd28f..ff6706056127 100644 --- a/x/sync/syncmanager.go +++ b/x/sync/syncmanager.go @@ -466,37 +466,34 @@ func (m *StateSyncManager) findNextKey( // * We don't locally have the prefix // * We have a different ID for the prefix var ( - // firstDiff merkledb.Path - // firstDiffSet bool + firstDiff merkledb.Path + firstDiffSet bool ) for minPath, ok := theirImpliedKeys.Min(); ok; minPath, ok = theirImpliedKeys.Min() { local, ok := ourImpliedKeys.Get(minPath) if !ok || local.id != minPath.id { - return minPath.path.Serialize().Value, nil - // firstDiff = minPath.path - // firstDiffSet = true + // return minPath.path.Serialize().Value, nil + firstDiff = minPath.path + firstDiffSet = true + break } theirImpliedKeys.DeleteMin() } - return nil, nil - // for minPath, ok := ourImpliedKeys.Min(); ok; minPath, ok = ourImpliedKeys.Min() { - // if firstDiffSet && minPath.path.Compare(firstDiff) >= 0 { - // break - // } - // if len(rangeEnd) > 0 && minPath.path.Compare(rangeEndPath) > 0 { - // break - // } - // remote, ok := theirImpliedKeys.Get(minPath) - // if !ok || remote.id != minPath.id { - // firstDiff = minPath.path - // firstDiffSet = true - // } - // ourImpliedKeys.DeleteMin() - // } - // if firstDiffSet { - // return firstDiff.Serialize().Value, nil - // } + for minPath, ok := ourImpliedKeys.Min(); ok; minPath, ok = ourImpliedKeys.Min() { + if firstDiffSet && minPath.path.Compare(firstDiff) >= 0 { + break + } + remote, ok := theirImpliedKeys.Get(minPath) + if !ok || remote.id != minPath.id { + firstDiff = minPath.path + firstDiffSet = true + } + ourImpliedKeys.DeleteMin() + } + if firstDiffSet { + return firstDiff.Serialize().Value, nil + } return nil, nil From 17e647cad458182e845810803cc69c5631d62a5a Mon Sep 17 00:00:00 2001 From: Dan Laine Date: Tue, 18 Apr 2023 16:18:55 -0400 Subject: [PATCH 14/21] WIP --- x/sync/syncmanager.go | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/x/sync/syncmanager.go b/x/sync/syncmanager.go index ff6706056127..9053210d00a0 100644 --- a/x/sync/syncmanager.go +++ b/x/sync/syncmanager.go @@ -469,28 +469,30 @@ func (m *StateSyncManager) findNextKey( firstDiff merkledb.Path firstDiffSet bool ) - for minPath, ok := theirImpliedKeys.Min(); ok; minPath, ok = theirImpliedKeys.Min() { - local, ok := ourImpliedKeys.Get(minPath) - if !ok || local.id != minPath.id { + theirImpliedKeys.Ascend(func(pathAndID pathAndID) bool { + local, ok := ourImpliedKeys.Get(pathAndID) + if !ok || local.id != pathAndID.id { // return minPath.path.Serialize().Value, nil - firstDiff = minPath.path + firstDiff = pathAndID.path firstDiffSet = true - break + return false } - theirImpliedKeys.DeleteMin() - } + return true + }) - for minPath, ok := ourImpliedKeys.Min(); ok; minPath, ok = ourImpliedKeys.Min() { - if firstDiffSet && minPath.path.Compare(firstDiff) >= 0 { - break + ourImpliedKeys.Ascend(func(pathAndID pathAndID) bool { + if firstDiffSet && pathAndID.path.Compare(firstDiff) >= 0 { + return false } - remote, ok := theirImpliedKeys.Get(minPath) - if !ok || remote.id != minPath.id { - firstDiff = minPath.path + remote, ok := theirImpliedKeys.Get(pathAndID) + if !ok || remote.id != pathAndID.id { + firstDiff = pathAndID.path firstDiffSet = true + return false } - ourImpliedKeys.DeleteMin() - } + return true + }) + if firstDiffSet { return firstDiff.Serialize().Value, nil } From bfbe1ed988d5d2b5779640e9aa8ec982c6bc264c Mon Sep 17 00:00:00 2001 From: Dan Laine Date: Tue, 18 Apr 2023 16:19:11 -0400 Subject: [PATCH 15/21] WIP --- x/sync/syncmanager.go | 91 ------------------------------------------- 1 file changed, 91 deletions(-) diff --git a/x/sync/syncmanager.go b/x/sync/syncmanager.go index 9053210d00a0..261e727008cf 100644 --- a/x/sync/syncmanager.go +++ b/x/sync/syncmanager.go @@ -498,97 +498,6 @@ func (m *StateSyncManager) findNextKey( } return nil, nil - - // // If the received proof's last node has a key is after the lastReceivedKey, this is an exclusion proof. - // // if it is an exclusion proof, then we can remove the last node to get a valid proof for some prefix of the lastReceivedKey - // if bytes.Compare(receivedProofNodes[len(receivedProofNodes)-1].KeyPath.Value, lastReceivedKey) > 0 { - // receivedProofNodes = receivedProofNodes[:len(receivedProofNodes)-1] - // // if the new last proof key is before the start of the range, then fallback to the lastReceivedKey as the next key - // if bytes.Compare(receivedProofNodes[len(receivedProofNodes)-1].KeyPath.Value, rangeStart) < 0 { - // return lastReceivedKey, nil - // } - // lastReceivedKeyPath = receivedProofNodes[len(receivedProofNodes)-1].KeyPath - // } - - // proofOfStart, err := m.config.SyncDB.GetProof(ctx, lastReceivedKeyPath.Value) - // if err != nil { - // return nil, err - // } - // localProofNodes := proofOfStart.Path - - // // If the received key had an odd length, then the proof generated might be for the even length version of the key (since getProof takes []byte). - // // If that is the case, then remove that last node so that the proof is just for the odd nibble length version of the key - // if lastReceivedKeyPath.NibbleLength%2 == 1 && !lastReceivedKeyPath.Equal(localProofNodes[len(localProofNodes)-1].KeyPath) { - // localProofNodes = localProofNodes[:len(localProofNodes)-1] - // } - - // var result []byte - // localIndex := len(localProofNodes) - 1 - // receivedIndex := len(receivedProofNodes) - 1 - - // // walk up the node paths until a difference is found - // for receivedIndex >= 0 && result == nil { - // localNode := localProofNodes[localIndex] - // receivedNode := receivedProofNodes[receivedIndex] - // // the two nodes have the same key - // if localNode.KeyPath.Equal(receivedNode.KeyPath) { - // startingChildIndex := byte(0) - // if localNode.KeyPath.NibbleLength < lastReceivedKeyPath.NibbleLength { - // startingChildIndex = lastReceivedKeyPath.NibbleVal(localNode.KeyPath.NibbleLength) + 1 - // } - // // the two nodes have the same path, so ensure that all children have matching ids - // for childIndex := startingChildIndex; childIndex < 16; childIndex++ { - // receivedChildID, receiveOk := receivedNode.Children[childIndex] - // localChildID, localOk := localNode.Children[childIndex] - // // if they both don't have a child or have matching children, continue - // if (receiveOk || localOk) && receivedChildID != localChildID { - // result = localNode.KeyPath.AppendNibble(childIndex).Value - // break - // } - // } - // if result != nil { - // break - // } - // // only want to move both indexes when they have equal keys - // localIndex-- - // receivedIndex-- - // continue - // } - - // var branchNode merkledb.ProofNode - - // if receivedNode.KeyPath.NibbleLength > localNode.KeyPath.NibbleLength { - // // the received proof has an extra node due to a branch that is not present locally - // branchNode = receivedNode - // receivedIndex-- - // } else { - // // the local proof has an extra node due to a branch that was not present in the received proof - // branchNode = localNode - // localIndex-- - // } - - // // I believe the exclusion proof checks at the beginning of this function should prevent this - // // but leave this safeguard in place just in case. - // // TODO: Figure out if there are scenarios where this can happen and fix - // if lastReceivedKeyPath.NibbleLength <= branchNode.KeyPath.NibbleLength { - // // the array access into lastReceivedKeyPath below this would fail, so default to the lastReceivedKey - // return lastReceivedKey, nil - // } - - // // the two nodes have different paths, so find where they branched - // for nextKeyNibble := lastReceivedKeyPath.NibbleVal(branchNode.KeyPath.NibbleLength) + 1; nextKeyNibble < 16; nextKeyNibble++ { - // if _, ok := branchNode.Children[nextKeyNibble]; ok { - // result = branchNode.KeyPath.AppendNibble(nextKeyNibble).Value - // break - // } - // } - // } - - // if result == nil || (len(rangeEnd) > 0 && bytes.Compare(result, rangeEnd) >= 0) { - // return nil, nil - // } - - // return result, nil } func (m *StateSyncManager) Error() error { From 1d6be217c62d9a7ce2dd86c7b622c4976eb8cbfe Mon Sep 17 00:00:00 2001 From: Dan Laine Date: Tue, 18 Apr 2023 16:46:18 -0400 Subject: [PATCH 16/21] WIP --- x/sync/syncmanager.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x/sync/syncmanager.go b/x/sync/syncmanager.go index 261e727008cf..dd8cd7e2e2ce 100644 --- a/x/sync/syncmanager.go +++ b/x/sync/syncmanager.go @@ -386,11 +386,13 @@ func (m *StateSyncManager) findNextKey( return nil, err } + // TODO should we just mark this key-value pair as deleted in [theirImpliedKeys]? // If remote proof is an exclusion proof, remove the last node from it. if bytes.Compare(receivedProofNodes[len(receivedProofNodes)-1].KeyPath.Value, lastReceivedKey) > 0 { receivedProofNodes = receivedProofNodes[:len(receivedProofNodes)-1] } + // TODO should we just mark this key-value pair as deleted in [ourImpliedKeys]? // If local proof is an exclusion proof, remove the last node from it. if bytes.Compare(localProof.Path[len(localProof.Path)-1].KeyPath.Value, lastReceivedKey) > 0 { localProof.Path = localProof.Path[:len(localProof.Path)-1] @@ -472,7 +474,6 @@ func (m *StateSyncManager) findNextKey( theirImpliedKeys.Ascend(func(pathAndID pathAndID) bool { local, ok := ourImpliedKeys.Get(pathAndID) if !ok || local.id != pathAndID.id { - // return minPath.path.Serialize().Value, nil firstDiff = pathAndID.path firstDiffSet = true return false From 2c33ebdaffdb06836ca991d3454a695d2e12209b Mon Sep 17 00:00:00 2001 From: Dan Laine Date: Tue, 18 Apr 2023 17:00:50 -0400 Subject: [PATCH 17/21] remove unused function --- x/merkledb/maybe.go | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/x/merkledb/maybe.go b/x/merkledb/maybe.go index db0f1411d89d..07fed60143ec 100644 --- a/x/merkledb/maybe.go +++ b/x/merkledb/maybe.go @@ -4,8 +4,6 @@ package merkledb import ( - "bytes" - "golang.org/x/exp/slices" ) @@ -43,16 +41,6 @@ func (m Maybe[T]) Value() T { return m.value } -func MaybeByteSliceEquals(m1, m2 Maybe[[]byte]) bool { - if m1.hasValue != m2.hasValue { - return false - } - if !m1.hasValue { - return true - } - return bytes.Equal(m1.value, m2.value) -} - func Clone(m Maybe[[]byte]) Maybe[[]byte] { if !m.hasValue { return Nothing[[]byte]() From b11e02ee4e270517702487e4777a14b4b3ae0993 Mon Sep 17 00:00:00 2001 From: Dan Laine Date: Tue, 18 Apr 2023 17:04:27 -0400 Subject: [PATCH 18/21] remove unnecessary copies --- x/sync/syncmanager.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/x/sync/syncmanager.go b/x/sync/syncmanager.go index dd8cd7e2e2ce..0d806882edb2 100644 --- a/x/sync/syncmanager.go +++ b/x/sync/syncmanager.go @@ -419,8 +419,6 @@ func (m *StateSyncManager) findNextKey( }) for idx, id := range node.Children { - idx := idx - id := id childPath := node.KeyPath.AppendNibble(idx) pathAndIDs = append(pathAndIDs, pathAndID{ path: childPath.Deserialize(), @@ -445,8 +443,6 @@ func (m *StateSyncManager) findNextKey( }) for idx, id := range node.Children { - idx := idx - id := id childPath := node.KeyPath.AppendNibble(idx) pathAndIDs = append(pathAndIDs, pathAndID{ path: childPath.Deserialize(), From 21e7baf40d31feb3180f2950769592fdd4d3f18d Mon Sep 17 00:00:00 2001 From: Dan Laine Date: Tue, 18 Apr 2023 17:08:08 -0400 Subject: [PATCH 19/21] remove duplicate code --- x/sync/syncmanager.go | 72 +++++++++++++++++++------------------------ 1 file changed, 32 insertions(+), 40 deletions(-) diff --git a/x/sync/syncmanager.go b/x/sync/syncmanager.go index 0d806882edb2..cfa17aca3919 100644 --- a/x/sync/syncmanager.go +++ b/x/sync/syncmanager.go @@ -411,51 +411,43 @@ func (m *StateSyncManager) findNextKey( lastReceivedPath := merkledb.NewPath(lastReceivedKey) rangeEndPath := merkledb.NewPath(rangeEnd) - for _, node := range receivedProofNodes { - pathAndIDs := make([]pathAndID, 0, len(node.Children)+1) - pathAndIDs = append(pathAndIDs, pathAndID{ - path: node.KeyPath.Deserialize(), - id: ids.ID(hashing.ComputeHash256Array(node.ValueOrHash.Value())), // TODO fix - }) - - for idx, id := range node.Children { - childPath := node.KeyPath.AppendNibble(idx) - pathAndIDs = append(pathAndIDs, pathAndID{ - path: childPath.Deserialize(), - id: id, - }) - } - - // Only consider paths greater than the last received path - // and less than the range end path (if applicable). - for _, pathAndID := range pathAndIDs { - if pathAndID.path.Compare(lastReceivedPath) > 0 && - (len(rangeEnd) == 0 || pathAndID.path.Compare(rangeEndPath) <= 0) { - theirImpliedKeys.ReplaceOrInsert(pathAndID) - } - } + type proofAndTree struct { + proof []merkledb.ProofNode + tree *btree.BTreeG[pathAndID] } - for _, node := range localProof.Path { - pathAndIDs := make([]pathAndID, 0, len(node.Children)+1) - pathAndIDs = append(pathAndIDs, pathAndID{ - path: node.KeyPath.Deserialize(), - id: ids.ID(hashing.ComputeHash256Array(node.ValueOrHash.Value())), // TODO fix - }) - for idx, id := range node.Children { - childPath := node.KeyPath.AppendNibble(idx) + for _, proofAndTree := range []proofAndTree{ + { + proof: receivedProofNodes, + tree: theirImpliedKeys, + }, + { + proof: localProof.Path, + tree: ourImpliedKeys, + }, + } { + for _, node := range proofAndTree.proof { + pathAndIDs := make([]pathAndID, 0, len(node.Children)+1) pathAndIDs = append(pathAndIDs, pathAndID{ - path: childPath.Deserialize(), - id: id, + path: node.KeyPath.Deserialize(), + id: ids.ID(hashing.ComputeHash256Array(node.ValueOrHash.Value())), // TODO fix }) - } - // Only consider paths greater than the last received path - // and less than the range end path (if applicable). - for _, pathAndID := range pathAndIDs { - if pathAndID.path.Compare(lastReceivedPath) > 0 && - (len(rangeEnd) == 0 || pathAndID.path.Compare(rangeEndPath) <= 0) { - ourImpliedKeys.ReplaceOrInsert(pathAndID) + for idx, id := range node.Children { + childPath := node.KeyPath.AppendNibble(idx) + pathAndIDs = append(pathAndIDs, pathAndID{ + path: childPath.Deserialize(), + id: id, + }) + } + + // Only consider paths greater than the last received path + // and less than the range end path (if applicable). + for _, pathAndID := range pathAndIDs { + if pathAndID.path.Compare(lastReceivedPath) > 0 && + (len(rangeEnd) == 0 || pathAndID.path.Compare(rangeEndPath) <= 0) { + proofAndTree.tree.ReplaceOrInsert(pathAndID) + } } } } From 9e8de2531271cb9b947565bbe361ecc6746d2e61 Mon Sep 17 00:00:00 2001 From: Dan Laine Date: Tue, 18 Apr 2023 17:09:53 -0400 Subject: [PATCH 20/21] update TODO --- x/sync/syncmanager.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/x/sync/syncmanager.go b/x/sync/syncmanager.go index cfa17aca3919..62ea35cfbf10 100644 --- a/x/sync/syncmanager.go +++ b/x/sync/syncmanager.go @@ -14,7 +14,6 @@ import ( "go.uber.org/zap" "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/utils/hashing" "github.com/ava-labs/avalanchego/utils/logging" "github.com/ava-labs/avalanchego/x/merkledb" "github.com/google/btree" @@ -430,7 +429,7 @@ func (m *StateSyncManager) findNextKey( pathAndIDs := make([]pathAndID, 0, len(node.Children)+1) pathAndIDs = append(pathAndIDs, pathAndID{ path: node.KeyPath.Deserialize(), - id: ids.ID(hashing.ComputeHash256Array(node.ValueOrHash.Value())), // TODO fix + id: ids.Empty, // TODO do we need a value here? }) for idx, id := range node.Children { From 995dac0b7f32cb994665724e5d2f1e8e0a16c14c Mon Sep 17 00:00:00 2001 From: dboehm-avalabs Date: Tue, 18 Apr 2023 17:10:23 -0400 Subject: [PATCH 21/21] Update sync_test.go --- x/sync/sync_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/sync/sync_test.go b/x/sync/sync_test.go index c9b6177210a7..f4845d9a0bfe 100644 --- a/x/sync/sync_test.go +++ b/x/sync/sync_test.go @@ -301,7 +301,7 @@ func Test_Sync_FindNextKey_Deleted(t *testing.T) { // extra node gets deleted and the remaining prefix node is not in the range, so default back to the lastReceivedKey nextKey, err = syncer.findNextKey(context.Background(), []byte{0x11}, []byte{0x20}, []byte{0x11}, extraNodeProof.Path) require.NoError(t, err) - require.Equal(t, []byte{0x11}, nextKey) + require.Equal(t, []byte{0x13}, nextKey) } func Test_Sync_FindNextKey_BranchInLocal(t *testing.T) {