Skip to content

Commit

Permalink
Add Maybe to the end bound of proofs (Part 2) (#1813)
Browse files Browse the repository at this point in the history
Co-authored-by: Dan Laine <daniel.laine@avalabs.org>
  • Loading branch information
dboehm-avalabs and Dan Laine authored Aug 8, 2023
1 parent 8cb298b commit cfb957c
Show file tree
Hide file tree
Showing 21 changed files with 375 additions and 345 deletions.
374 changes: 189 additions & 185 deletions proto/pb/sync/sync.pb.go

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions proto/sync/sync.proto
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ message GetChangeProofRequest {
bytes start_root_hash = 1;
bytes end_root_hash = 2;
bytes start_key = 3;
bytes end_key = 4;
MaybeBytes end_key = 4;
uint32 key_limit = 5;
}

Expand Down Expand Up @@ -104,7 +104,7 @@ message SyncGetRangeProofRequest {
message GetRangeProofRequest {
bytes root_hash = 1;
bytes start_key = 2;
bytes end_key = 3;
MaybeBytes end_key = 3;
uint32 key_limit = 4;
}

Expand Down
36 changes: 16 additions & 20 deletions x/merkledb/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ type ChangeProofer interface {
startRootID ids.ID,
endRootID ids.ID,
start []byte,
end []byte,
end Maybe[[]byte],
maxLength int,
) (*ChangeProof, error)

Expand Down Expand Up @@ -97,8 +97,8 @@ type RangeProofer interface {
GetRangeProofAtRoot(
ctx context.Context,
rootID ids.ID,
start,
end []byte,
start []byte,
end Maybe[[]byte],
maxLength int,
) (*RangeProof, error)

Expand Down Expand Up @@ -480,8 +480,8 @@ func (db *merkleDB) getProof(ctx context.Context, key []byte) (*Proof, error) {
// [start, end].
func (db *merkleDB) GetRangeProof(
ctx context.Context,
start,
end []byte,
start []byte,
end Maybe[[]byte],
maxLength int,
) (*RangeProof, error) {
db.commitLock.RLock()
Expand All @@ -495,8 +495,8 @@ func (db *merkleDB) GetRangeProof(
func (db *merkleDB) GetRangeProofAtRoot(
ctx context.Context,
rootID ids.ID,
start,
end []byte,
start []byte,
end Maybe[[]byte],
maxLength int,
) (*RangeProof, error) {
db.commitLock.RLock()
Expand All @@ -509,8 +509,8 @@ func (db *merkleDB) GetRangeProofAtRoot(
func (db *merkleDB) getRangeProofAtRoot(
ctx context.Context,
rootID ids.ID,
start,
end []byte,
start []byte,
end Maybe[[]byte],
maxLength int,
) (*RangeProof, error) {
if db.closed {
Expand All @@ -532,10 +532,10 @@ func (db *merkleDB) GetChangeProof(
startRootID ids.ID,
endRootID ids.ID,
start []byte,
end []byte,
end Maybe[[]byte],
maxLength int,
) (*ChangeProof, error) {
if len(end) > 0 && bytes.Compare(start, end) == 1 {
if end.HasValue() && bytes.Compare(start, end.Value()) == 1 {
return nil, ErrStartAfterEnd
}
if startRootID == endRootID {
Expand Down Expand Up @@ -578,20 +578,18 @@ func (db *merkleDB) GetChangeProof(
result.KeyChanges = append(result.KeyChanges, KeyChange{
Key: serializedKey,
// create a copy so edits of the []byte don't affect the db
Value: Clone(change.after),
Value: MaybeBind(change.after, slices.Clone[[]byte]),
})
}

largestKey := Nothing[[]byte]()
largestKey := end
if len(result.KeyChanges) > 0 {
largestKey = Some(result.KeyChanges[len(result.KeyChanges)-1].Key)
} else if len(end) > 0 {
largestKey = Some(end)
}

// Since we hold [db.commitlock] we must still have sufficient
// history to recreate the trie at [endRootID].
historicalView, err := db.getHistoricalViewForRange(endRootID, start, largestKey.Value())
historicalView, err := db.getHistoricalViewForRange(endRootID, start, largestKey)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -1006,14 +1004,12 @@ func (db *merkleDB) VerifyChangeProof(
// Find the greatest key in [proof.KeyChanges]
// 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 := Nothing[path]()
largestPath := MaybeBind(end, newPath)
if len(proof.KeyChanges) > 0 {
// If [proof] has key-value pairs, we should insert children
// greater than [end] to ancestors of the node containing [end]
// so that we get the expected root ID.
largestPath = Some(newPath(proof.KeyChanges[len(proof.KeyChanges)-1].Key))
} else if end.HasValue() {
largestPath = Some(newPath(end.Value()))
}

// Make sure the end proof, if given, is well-formed.
Expand Down Expand Up @@ -1169,7 +1165,7 @@ func (db *merkleDB) initializeRootIfNeeded() (ids.ID, error) {
func (db *merkleDB) getHistoricalViewForRange(
rootID ids.ID,
start []byte,
end []byte,
end Maybe[[]byte],
) (*trieView, error) {
currentRootID := db.getMerkleRoot()

Expand Down
21 changes: 12 additions & 9 deletions x/merkledb/db_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ func Test_MerkleDB_Commit_Proof_To_Empty_Trie(t *testing.T) {
require.NoError(batch.Put([]byte("key3"), []byte("3")))
require.NoError(batch.Write())

proof, err := db.GetRangeProof(context.Background(), []byte("key1"), []byte("key3"), 10)
proof, err := db.GetRangeProof(context.Background(), []byte("key1"), Some([]byte("key3")), 10)
require.NoError(err)

freshDB, err := getBasicDB()
Expand Down Expand Up @@ -289,7 +289,7 @@ func Test_MerkleDB_Commit_Proof_To_Filled_Trie(t *testing.T) {
require.NoError(batch.Put([]byte("key3"), []byte("3")))
require.NoError(batch.Write())

proof, err := db.GetRangeProof(context.Background(), []byte("key1"), []byte("key3"), 10)
proof, err := db.GetRangeProof(context.Background(), []byte("key1"), Some([]byte("key3")), 10)
require.NoError(err)

freshDB, err := getBasicDB()
Expand Down Expand Up @@ -768,12 +768,13 @@ func runRandDBTest(require *require.Assertions, r *rand.Rand, rt randTest) {
if len(pastRoots) > 0 {
root = pastRoots[r.Intn(len(pastRoots))]
}
rangeProof, err := db.GetRangeProofAtRoot(context.Background(), root, step.key, step.value, 100)
require.NoError(err)
end := Nothing[[]byte]()
if len(step.value) > 0 {
end = Some(step.value)
}

rangeProof, err := db.GetRangeProofAtRoot(context.Background(), root, step.key, end, 100)
require.NoError(err)
require.NoError(rangeProof.Verify(
context.Background(),
step.key,
Expand All @@ -787,18 +788,20 @@ func runRandDBTest(require *require.Assertions, r *rand.Rand, rt randTest) {
if len(pastRoots) > 1 {
root = pastRoots[r.Intn(len(pastRoots))]
}
changeProof, err := db.GetChangeProof(context.Background(), startRoot, root, step.key, step.value, 100)
end := Nothing[[]byte]()
if len(step.value) > 0 {
end = Some(step.value)
}

changeProof, err := db.GetChangeProof(context.Background(), startRoot, root, step.key, end, 100)
if startRoot == root {
require.ErrorIs(err, errSameRoot)
continue
}
require.NoError(err)
changeProofDB, err := getBasicDB()
require.NoError(err)
end := Nothing[[]byte]()
if len(step.value) > 0 {
end = Some(step.value)
}

require.NoError(changeProofDB.VerifyChangeProof(
context.Background(),
changeProof,
Expand Down
12 changes: 6 additions & 6 deletions x/merkledb/history.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ func newTrieHistory(maxHistoryLookback int) *trieHistory {

// Returns up to [maxLength] key-value pair changes with keys in [start, end] that
// occurred between [startRoot] and [endRoot].
func (th *trieHistory) getValueChanges(startRoot, endRoot ids.ID, start, end []byte, maxLength int) (*changeSummary, error) {
func (th *trieHistory) getValueChanges(startRoot, endRoot ids.ID, start []byte, end Maybe[[]byte], maxLength int) (*changeSummary, error) {
if maxLength <= 0 {
return nil, fmt.Errorf("%w but was %d", ErrInvalidMaxLength, maxLength)
}
Expand Down Expand Up @@ -132,7 +132,7 @@ func (th *trieHistory) getValueChanges(startRoot, endRoot ids.ID, start, end []b
)

startPath := newPath(start)
endPath := newPath(end)
endPath := MaybeBind(end, newPath)

// For each element in the history in the range between [startRoot]'s
// last appearance (exclusive) and [endRoot]'s last appearance (inclusive),
Expand All @@ -158,7 +158,7 @@ func (th *trieHistory) getValueChanges(startRoot, endRoot ids.ID, start, end []b
for key, valueChange := range item.values {
// The key is outside the range [start, end].
if (len(startPath) > 0 && key.Compare(startPath) < 0) ||
(len(endPath) > 0 && key.Compare(endPath) > 0) {
(end.HasValue() && key.Compare(endPath.Value()) > 0) {
continue
}

Expand Down Expand Up @@ -198,7 +198,7 @@ func (th *trieHistory) getValueChanges(startRoot, endRoot ids.ID, start, end []b
// for the keys in [start, end].
// If [start] is nil, all keys are considered > [start].
// If [end] is nil, all keys are considered < [end].
func (th *trieHistory) getChangesToGetToRoot(rootID ids.ID, start, end []byte) (*changeSummary, error) {
func (th *trieHistory) getChangesToGetToRoot(rootID ids.ID, start []byte, end Maybe[[]byte]) (*changeSummary, error) {
// [lastRootChange] is the last change in the history resulting in [rootID].
lastRootChange, ok := th.lastChanges[rootID]
if !ok {
Expand All @@ -207,7 +207,7 @@ func (th *trieHistory) getChangesToGetToRoot(rootID ids.ID, start, end []byte) (

var (
startPath = newPath(start)
endPath = newPath(end)
endPath = MaybeBind(end, newPath)
combinedChanges = newChangeSummary(defaultPreallocationSize)
)

Expand All @@ -227,7 +227,7 @@ func (th *trieHistory) getChangesToGetToRoot(rootID ids.ID, start, end []byte) (

for key, valueChange := range item.values {
if (len(startPath) == 0 || key.Compare(startPath) >= 0) &&
(len(endPath) == 0 || key.Compare(endPath) <= 0) {
(endPath.IsNothing() || key.Compare(endPath.Value()) <= 0) {
if existing, ok := combinedChanges.values[key]; ok {
existing.after = valueChange.before
} else {
Expand Down
Loading

0 comments on commit cfb957c

Please sign in to comment.