Skip to content

Commit 3366c15

Browse files
dboehm-avalabsDarioush JalaliDan Laine
authored
Merkle db fix range proof commit bug (#2019)
Signed-off-by: David Boehm <91908103+dboehm-avalabs@users.noreply.github.com> Co-authored-by: Darioush Jalali <darioush.jalali@avalabs.org> Co-authored-by: Dan Laine <daniel.laine@avalabs.org>
1 parent 6fe9dd0 commit 3366c15

File tree

9 files changed

+219
-153
lines changed

9 files changed

+219
-153
lines changed

proto/pb/sync/sync.pb.go

Lines changed: 137 additions & 126 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

proto/sync/sync.proto

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,8 @@ message GetRangeProofResponse {
122122

123123
message CommitRangeProofRequest {
124124
MaybeBytes start_key = 1;
125-
RangeProof range_proof = 2;
125+
MaybeBytes end_key = 2;
126+
RangeProof range_proof = 3;
126127
}
127128

128129
message ChangeProof {

x/merkledb/db.go

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,9 @@ type RangeProofer interface {
109109
) (*RangeProof, error)
110110

111111
// CommitRangeProof commits the key/value pairs within the [proof] to the db.
112-
// [start] is the smallest key in the range this [proof] covers.
113-
CommitRangeProof(ctx context.Context, start maybe.Maybe[[]byte], proof *RangeProof) error
112+
// [start] is the smallest possible key in the range this [proof] covers.
113+
// [end] is the largest possible key in the range this [proof] covers.
114+
CommitRangeProof(ctx context.Context, start, end maybe.Maybe[[]byte], proof *RangeProof) error
114115
}
115116

116117
type MerkleDB interface {
@@ -334,7 +335,7 @@ func (db *merkleDB) CommitChangeProof(ctx context.Context, proof *ChangeProof) e
334335
return view.commitToDB(ctx)
335336
}
336337

337-
func (db *merkleDB) CommitRangeProof(ctx context.Context, start maybe.Maybe[[]byte], proof *RangeProof) error {
338+
func (db *merkleDB) CommitRangeProof(ctx context.Context, start, end maybe.Maybe[[]byte], proof *RangeProof) error {
338339
db.commitLock.Lock()
339340
defer db.commitLock.Unlock()
340341

@@ -352,11 +353,11 @@ func (db *merkleDB) CommitRangeProof(ctx context.Context, start maybe.Maybe[[]by
352353
}
353354
}
354355

355-
var largestKey []byte
356+
largestKey := end
356357
if len(proof.KeyValues) > 0 {
357-
largestKey = proof.KeyValues[len(proof.KeyValues)-1].Key
358+
largestKey = maybe.Some(proof.KeyValues[len(proof.KeyValues)-1].Key)
358359
}
359-
keysToDelete, err := db.getKeysNotInSet(start.Value(), largestKey, keys)
360+
keysToDelete, err := db.getKeysNotInSet(start, largestKey, keys)
360361
if err != nil {
361362
return err
362363
}
@@ -1128,19 +1129,19 @@ func (db *merkleDB) getHistoricalViewForRange(
11281129
}
11291130

11301131
// Returns all keys in range [start, end] that aren't in [keySet].
1131-
// If [start] is nil, then the range has no lower bound.
1132-
// If [end] is nil, then the range has no upper bound.
1133-
func (db *merkleDB) getKeysNotInSet(start, end []byte, keySet set.Set[string]) ([][]byte, error) {
1132+
// If [start] is Nothing, then the range has no lower bound.
1133+
// If [end] is Nothing, then the range has no upper bound.
1134+
func (db *merkleDB) getKeysNotInSet(start, end maybe.Maybe[[]byte], keySet set.Set[string]) ([][]byte, error) {
11341135
db.lock.RLock()
11351136
defer db.lock.RUnlock()
11361137

1137-
it := db.NewIteratorWithStart(start)
1138+
it := db.NewIteratorWithStart(start.Value())
11381139
defer it.Release()
11391140

11401141
keysNotInSet := make([][]byte, 0, keySet.Len())
11411142
for it.Next() {
11421143
key := it.Key()
1143-
if len(end) != 0 && bytes.Compare(key, end) > 0 {
1144+
if end.HasValue() && bytes.Compare(key, end.Value()) > 0 {
11441145
break
11451146
}
11461147
if !keySet.Contains(string(key)) {

x/merkledb/db_test.go

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,47 @@ func Test_MerkleDB_Invalidate_Siblings_On_Commit(t *testing.T) {
301301
require.False(viewToCommit.(*trieView).isInvalid())
302302
}
303303

304-
func Test_MerkleDB_Commit_Proof_To_Empty_Trie(t *testing.T) {
304+
func Test_MerkleDB_CommitRangeProof_DeletesValuesInRange(t *testing.T) {
305+
require := require.New(t)
306+
307+
db, err := getBasicDB()
308+
require.NoError(err)
309+
310+
// value that shouldn't be deleted
311+
require.NoError(db.Put([]byte("key6"), []byte("3")))
312+
313+
startRoot, err := db.GetMerkleRoot(context.Background())
314+
require.NoError(err)
315+
316+
// Get an empty proof
317+
proof, err := db.GetRangeProof(
318+
context.Background(),
319+
maybe.Nothing[[]byte](),
320+
maybe.Some([]byte("key3")),
321+
10,
322+
)
323+
require.NoError(err)
324+
325+
// confirm there are no key.values in the proof
326+
require.Empty(proof.KeyValues)
327+
328+
// add values to be deleted by proof commit
329+
batch := db.NewBatch()
330+
require.NoError(batch.Put([]byte("key1"), []byte("1")))
331+
require.NoError(batch.Put([]byte("key2"), []byte("2")))
332+
require.NoError(batch.Put([]byte("key3"), []byte("3")))
333+
require.NoError(batch.Write())
334+
335+
// despite having no key/values in it, committing this proof should delete key1-key3.
336+
require.NoError(db.CommitRangeProof(context.Background(), maybe.Nothing[[]byte](), maybe.Some([]byte("key3")), proof))
337+
338+
afterCommitRoot, err := db.GetMerkleRoot(context.Background())
339+
require.NoError(err)
340+
341+
require.Equal(startRoot, afterCommitRoot)
342+
}
343+
344+
func Test_MerkleDB_CommitRangeProof_EmptyTrie(t *testing.T) {
305345
require := require.New(t)
306346

307347
// Populate [db1] with 3 key-value pairs.
@@ -326,7 +366,7 @@ func Test_MerkleDB_Commit_Proof_To_Empty_Trie(t *testing.T) {
326366
db2, err := getBasicDB()
327367
require.NoError(err)
328368

329-
require.NoError(db2.CommitRangeProof(context.Background(), maybe.Some([]byte("key1")), proof))
369+
require.NoError(db2.CommitRangeProof(context.Background(), maybe.Some([]byte("key1")), maybe.Some([]byte("key3")), proof))
330370

331371
// [db2] should have the same key-value pairs as [db1].
332372
db2Root, err := db2.GetMerkleRoot(context.Background())
@@ -338,7 +378,7 @@ func Test_MerkleDB_Commit_Proof_To_Empty_Trie(t *testing.T) {
338378
require.Equal(db1Root, db2Root)
339379
}
340380

341-
func Test_MerkleDB_Commit_Proof_To_Filled_Trie(t *testing.T) {
381+
func Test_MerkleDB_CommitRangeProof_TrieWithInitialValues(t *testing.T) {
342382
require := require.New(t)
343383

344384
// Populate [db1] with 3 key-value pairs.
@@ -374,6 +414,7 @@ func Test_MerkleDB_Commit_Proof_To_Filled_Trie(t *testing.T) {
374414
require.NoError(db2.CommitRangeProof(
375415
context.Background(),
376416
maybe.Some([]byte("key1")),
417+
maybe.Some([]byte("key3")),
377418
proof,
378419
))
379420

x/merkledb/mock_db.go

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

x/sync/g_db/db_client.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,13 +157,18 @@ func (c *DBClient) GetRangeProofAtRoot(
157157
func (c *DBClient) CommitRangeProof(
158158
ctx context.Context,
159159
startKey maybe.Maybe[[]byte],
160+
endKey maybe.Maybe[[]byte],
160161
proof *merkledb.RangeProof,
161162
) error {
162163
_, err := c.client.CommitRangeProof(ctx, &pb.CommitRangeProofRequest{
163164
StartKey: &pb.MaybeBytes{
164165
IsNothing: startKey.IsNothing(),
165166
Value: startKey.Value(),
166167
},
168+
EndKey: &pb.MaybeBytes{
169+
IsNothing: endKey.IsNothing(),
170+
Value: endKey.Value(),
171+
},
167172
RangeProof: proof.ToProto(),
168173
})
169174
return err

x/sync/g_db/db_server.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,11 @@ func (s *DBServer) CommitRangeProof(
206206
start = maybe.Some(req.StartKey.Value)
207207
}
208208

209-
err := s.db.CommitRangeProof(ctx, start, &proof)
209+
end := maybe.Nothing[[]byte]()
210+
if req.EndKey != nil && !req.EndKey.IsNothing {
211+
end = maybe.Some(req.EndKey.Value)
212+
}
213+
214+
err := s.db.CommitRangeProof(ctx, start, end, &proof)
210215
return &emptypb.Empty{}, err
211216
}

x/sync/manager.go

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,7 @@ func (m *Manager) getAndApplyChangeProof(ctx context.Context, work *workItem) {
309309
largestHandledKey := work.end
310310
if len(rangeProof.KeyValues) > 0 {
311311
// Add all the key-value pairs we got to the database.
312-
if err := m.config.DB.CommitRangeProof(ctx, work.start, rangeProof); err != nil {
312+
if err := m.config.DB.CommitRangeProof(ctx, work.start, work.end, rangeProof); err != nil {
313313
m.setError(err)
314314
return
315315
}
@@ -351,13 +351,14 @@ func (m *Manager) getAndApplyRangeProof(ctx context.Context, work *workItem) {
351351
}
352352

353353
largestHandledKey := work.end
354-
if len(proof.KeyValues) > 0 {
355-
// Add all the key-value pairs we got to the database.
356-
if err := m.config.DB.CommitRangeProof(ctx, work.start, proof); err != nil {
357-
m.setError(err)
358-
return
359-
}
360354

355+
// Replace all the key-value pairs in the DB from start to end with values from the response.
356+
if err := m.config.DB.CommitRangeProof(ctx, work.start, work.end, proof); err != nil {
357+
m.setError(err)
358+
return
359+
}
360+
361+
if len(proof.KeyValues) > 0 {
361362
largestHandledKey = maybe.Some(proof.KeyValues[len(proof.KeyValues)-1].Key)
362363
}
363364

x/sync/sync_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -648,6 +648,7 @@ func TestFindNextKeyRandom(t *testing.T) {
648648
require.NoError(localDB.CommitRangeProof(
649649
context.Background(),
650650
startKey,
651+
endKey,
651652
remoteProof,
652653
))
653654

0 commit comments

Comments
 (0)