github.com/MetalBlockchain/metalgo@v1.11.9/x/sync/sync_test.go (about) 1 // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package sync 5 6 import ( 7 "bytes" 8 "context" 9 "math/rand" 10 "slices" 11 "testing" 12 "time" 13 14 "github.com/stretchr/testify/require" 15 "go.uber.org/mock/gomock" 16 17 "github.com/MetalBlockchain/metalgo/database" 18 "github.com/MetalBlockchain/metalgo/database/memdb" 19 "github.com/MetalBlockchain/metalgo/ids" 20 "github.com/MetalBlockchain/metalgo/utils/logging" 21 "github.com/MetalBlockchain/metalgo/utils/maybe" 22 "github.com/MetalBlockchain/metalgo/x/merkledb" 23 24 pb "github.com/MetalBlockchain/metalgo/proto/pb/sync" 25 ) 26 27 func newCallthroughSyncClient(ctrl *gomock.Controller, db merkledb.MerkleDB) *MockClient { 28 syncClient := NewMockClient(ctrl) 29 syncClient.EXPECT().GetRangeProof(gomock.Any(), gomock.Any()).DoAndReturn( 30 func(_ context.Context, request *pb.SyncGetRangeProofRequest) (*merkledb.RangeProof, error) { 31 return db.GetRangeProof( 32 context.Background(), 33 maybeBytesToMaybe(request.StartKey), 34 maybeBytesToMaybe(request.EndKey), 35 int(request.KeyLimit), 36 ) 37 }).AnyTimes() 38 syncClient.EXPECT().GetChangeProof(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn( 39 func(_ context.Context, request *pb.SyncGetChangeProofRequest, _ DB) (*merkledb.ChangeOrRangeProof, error) { 40 startRoot, err := ids.ToID(request.StartRootHash) 41 if err != nil { 42 return nil, err 43 } 44 45 endRoot, err := ids.ToID(request.EndRootHash) 46 if err != nil { 47 return nil, err 48 } 49 50 changeProof, err := db.GetChangeProof( 51 context.Background(), 52 startRoot, 53 endRoot, 54 maybeBytesToMaybe(request.StartKey), 55 maybeBytesToMaybe(request.EndKey), 56 int(request.KeyLimit), 57 ) 58 if err != nil { 59 return nil, err 60 } 61 return &merkledb.ChangeOrRangeProof{ 62 ChangeProof: changeProof, 63 }, nil 64 }).AnyTimes() 65 return syncClient 66 } 67 68 func Test_Creation(t *testing.T) { 69 require := require.New(t) 70 ctrl := gomock.NewController(t) 71 defer ctrl.Finish() 72 73 db, err := merkledb.New( 74 context.Background(), 75 memdb.New(), 76 newDefaultDBConfig(), 77 ) 78 require.NoError(err) 79 80 syncer, err := NewManager(ManagerConfig{ 81 DB: db, 82 Client: NewMockClient(ctrl), 83 TargetRoot: ids.Empty, 84 SimultaneousWorkLimit: 5, 85 Log: logging.NoLog{}, 86 BranchFactor: merkledb.BranchFactor16, 87 }) 88 require.NoError(err) 89 require.NotNil(syncer) 90 } 91 92 func Test_Completion(t *testing.T) { 93 require := require.New(t) 94 ctrl := gomock.NewController(t) 95 defer ctrl.Finish() 96 97 emptyDB, err := merkledb.New( 98 context.Background(), 99 memdb.New(), 100 newDefaultDBConfig(), 101 ) 102 require.NoError(err) 103 104 emptyRoot, err := emptyDB.GetMerkleRoot(context.Background()) 105 require.NoError(err) 106 107 db, err := merkledb.New( 108 context.Background(), 109 memdb.New(), 110 newDefaultDBConfig(), 111 ) 112 require.NoError(err) 113 114 syncer, err := NewManager(ManagerConfig{ 115 DB: db, 116 Client: newCallthroughSyncClient(ctrl, emptyDB), 117 TargetRoot: emptyRoot, 118 SimultaneousWorkLimit: 5, 119 Log: logging.NoLog{}, 120 BranchFactor: merkledb.BranchFactor16, 121 }) 122 require.NoError(err) 123 require.NotNil(syncer) 124 125 require.NoError(syncer.Start(context.Background())) 126 require.NoError(syncer.Wait(context.Background())) 127 128 syncer.workLock.Lock() 129 require.Zero(syncer.unprocessedWork.Len()) 130 require.Equal(1, syncer.processedWork.Len()) 131 syncer.workLock.Unlock() 132 } 133 134 func Test_Midpoint(t *testing.T) { 135 require := require.New(t) 136 137 mid := midPoint(maybe.Some([]byte{1, 255}), maybe.Some([]byte{2, 1})) 138 require.Equal(maybe.Some([]byte{2, 0}), mid) 139 140 mid = midPoint(maybe.Nothing[[]byte](), maybe.Some([]byte{255, 255, 0})) 141 require.Equal(maybe.Some([]byte{127, 255, 128}), mid) 142 143 mid = midPoint(maybe.Some([]byte{255, 255, 255}), maybe.Some([]byte{255, 255})) 144 require.Equal(maybe.Some([]byte{255, 255, 127, 128}), mid) 145 146 mid = midPoint(maybe.Nothing[[]byte](), maybe.Some([]byte{255})) 147 require.Equal(maybe.Some([]byte{127, 127}), mid) 148 149 mid = midPoint(maybe.Some([]byte{1, 255}), maybe.Some([]byte{255, 1})) 150 require.Equal(maybe.Some([]byte{128, 128}), mid) 151 152 mid = midPoint(maybe.Some([]byte{140, 255}), maybe.Some([]byte{141, 0})) 153 require.Equal(maybe.Some([]byte{140, 255, 127}), mid) 154 155 mid = midPoint(maybe.Some([]byte{126, 255}), maybe.Some([]byte{127})) 156 require.Equal(maybe.Some([]byte{126, 255, 127}), mid) 157 158 mid = midPoint(maybe.Nothing[[]byte](), maybe.Nothing[[]byte]()) 159 require.Equal(maybe.Some([]byte{127}), mid) 160 161 low := midPoint(maybe.Nothing[[]byte](), mid) 162 require.Equal(maybe.Some([]byte{63, 127}), low) 163 164 high := midPoint(mid, maybe.Nothing[[]byte]()) 165 require.Equal(maybe.Some([]byte{191}), high) 166 167 mid = midPoint(maybe.Some([]byte{255, 255}), maybe.Nothing[[]byte]()) 168 require.Equal(maybe.Some([]byte{255, 255, 127, 127}), mid) 169 170 mid = midPoint(maybe.Some([]byte{255}), maybe.Nothing[[]byte]()) 171 require.Equal(maybe.Some([]byte{255, 127, 127}), mid) 172 173 for i := 0; i < 5000; i++ { 174 r := rand.New(rand.NewSource(int64(i))) // #nosec G404 175 176 start := make([]byte, r.Intn(99)+1) 177 _, err := r.Read(start) 178 require.NoError(err) 179 180 end := make([]byte, r.Intn(99)+1) 181 _, err = r.Read(end) 182 require.NoError(err) 183 184 for bytes.Equal(start, end) { 185 _, err = r.Read(end) 186 require.NoError(err) 187 } 188 189 if bytes.Compare(start, end) == 1 { 190 start, end = end, start 191 } 192 193 mid = midPoint(maybe.Some(start), maybe.Some(end)) 194 require.Equal(-1, bytes.Compare(start, mid.Value())) 195 require.Equal(-1, bytes.Compare(mid.Value(), end)) 196 } 197 } 198 199 func Test_Sync_FindNextKey_InSync(t *testing.T) { 200 require := require.New(t) 201 ctrl := gomock.NewController(t) 202 defer ctrl.Finish() 203 204 now := time.Now().UnixNano() 205 t.Logf("seed: %d", now) 206 r := rand.New(rand.NewSource(now)) // #nosec G404 207 dbToSync, err := generateTrie(t, r, 1000) 208 require.NoError(err) 209 syncRoot, err := dbToSync.GetMerkleRoot(context.Background()) 210 require.NoError(err) 211 212 db, err := merkledb.New( 213 context.Background(), 214 memdb.New(), 215 newDefaultDBConfig(), 216 ) 217 require.NoError(err) 218 219 syncer, err := NewManager(ManagerConfig{ 220 DB: db, 221 Client: newCallthroughSyncClient(ctrl, dbToSync), 222 TargetRoot: syncRoot, 223 SimultaneousWorkLimit: 5, 224 Log: logging.NoLog{}, 225 BranchFactor: merkledb.BranchFactor16, 226 }) 227 require.NoError(err) 228 require.NotNil(syncer) 229 230 require.NoError(syncer.Start(context.Background())) 231 require.NoError(syncer.Wait(context.Background())) 232 233 proof, err := dbToSync.GetRangeProof(context.Background(), maybe.Nothing[[]byte](), maybe.Nothing[[]byte](), 500) 234 require.NoError(err) 235 236 // the two dbs should be in sync, so next key should be nil 237 lastKey := proof.KeyValues[len(proof.KeyValues)-1].Key 238 nextKey, err := syncer.findNextKey(context.Background(), lastKey, maybe.Nothing[[]byte](), proof.EndProof) 239 require.NoError(err) 240 require.True(nextKey.IsNothing()) 241 242 // add an extra value to sync db past the last key returned 243 newKey := midPoint(maybe.Some(lastKey), maybe.Nothing[[]byte]()) 244 newKeyVal := newKey.Value() 245 require.NoError(db.Put(newKeyVal, []byte{1})) 246 247 // create a range endpoint that is before the newly added key, but after the last key 248 endPointBeforeNewKey := make([]byte, 0, 2) 249 for i := 0; i < len(newKeyVal); i++ { 250 endPointBeforeNewKey = append(endPointBeforeNewKey, newKeyVal[i]) 251 252 // we need the new key to be after the last key 253 // don't subtract anything from the current byte if newkey and lastkey are equal 254 if lastKey[i] == newKeyVal[i] { 255 continue 256 } 257 258 // if the first nibble is > 0, subtract "1" from it 259 if endPointBeforeNewKey[i] >= 16 { 260 endPointBeforeNewKey[i] -= 16 261 break 262 } 263 // if the second nibble > 0, subtract 1 from it 264 if endPointBeforeNewKey[i] > 0 { 265 endPointBeforeNewKey[i] -= 1 266 break 267 } 268 // both nibbles were 0, so move onto the next byte 269 } 270 271 nextKey, err = syncer.findNextKey(context.Background(), lastKey, maybe.Some(endPointBeforeNewKey), proof.EndProof) 272 require.NoError(err) 273 274 // next key would be after the end of the range, so it returns Nothing instead 275 require.True(nextKey.IsNothing()) 276 } 277 278 func Test_Sync_FindNextKey_Deleted(t *testing.T) { 279 require := require.New(t) 280 ctrl := gomock.NewController(t) 281 defer ctrl.Finish() 282 283 db, err := merkledb.New( 284 context.Background(), 285 memdb.New(), 286 newDefaultDBConfig(), 287 ) 288 require.NoError(err) 289 require.NoError(db.Put([]byte{0x10}, []byte{1})) 290 require.NoError(db.Put([]byte{0x11, 0x11}, []byte{2})) 291 292 syncRoot, err := db.GetMerkleRoot(context.Background()) 293 require.NoError(err) 294 295 syncer, err := NewManager(ManagerConfig{ 296 DB: db, 297 Client: NewMockClient(ctrl), 298 TargetRoot: syncRoot, 299 SimultaneousWorkLimit: 5, 300 Log: logging.NoLog{}, 301 BranchFactor: merkledb.BranchFactor16, 302 }) 303 require.NoError(err) 304 305 // 0x12 was "deleted" and there should be no extra node in the proof since there was nothing with a common prefix 306 noExtraNodeProof, err := db.GetProof(context.Background(), []byte{0x12}) 307 require.NoError(err) 308 309 // 0x11 was "deleted" and 0x11.0x11 should be in the exclusion proof 310 extraNodeProof, err := db.GetProof(context.Background(), []byte{0x11}) 311 require.NoError(err) 312 313 // there is now another value in the range that needs to be sync'ed 314 require.NoError(db.Put([]byte{0x13}, []byte{3})) 315 316 nextKey, err := syncer.findNextKey(context.Background(), []byte{0x12}, maybe.Some([]byte{0x20}), noExtraNodeProof.Path) 317 require.NoError(err) 318 require.Equal(maybe.Some([]byte{0x13}), nextKey) 319 320 nextKey, err = syncer.findNextKey(context.Background(), []byte{0x11}, maybe.Some([]byte{0x20}), extraNodeProof.Path) 321 require.NoError(err) 322 require.Equal(maybe.Some([]byte{0x13}), nextKey) 323 } 324 325 func Test_Sync_FindNextKey_BranchInLocal(t *testing.T) { 326 require := require.New(t) 327 ctrl := gomock.NewController(t) 328 329 db, err := merkledb.New( 330 context.Background(), 331 memdb.New(), 332 newDefaultDBConfig(), 333 ) 334 require.NoError(err) 335 require.NoError(db.Put([]byte{0x11}, []byte{1})) 336 require.NoError(db.Put([]byte{0x11, 0x11}, []byte{2})) 337 338 targetRoot, err := db.GetMerkleRoot(context.Background()) 339 require.NoError(err) 340 341 proof, err := db.GetProof(context.Background(), []byte{0x11, 0x11}) 342 require.NoError(err) 343 344 syncer, err := NewManager(ManagerConfig{ 345 DB: db, 346 Client: NewMockClient(ctrl), 347 TargetRoot: targetRoot, 348 SimultaneousWorkLimit: 5, 349 Log: logging.NoLog{}, 350 BranchFactor: merkledb.BranchFactor16, 351 }) 352 require.NoError(err) 353 require.NoError(db.Put([]byte{0x11, 0x15}, []byte{4})) 354 355 nextKey, err := syncer.findNextKey(context.Background(), []byte{0x11, 0x11}, maybe.Some([]byte{0x20}), proof.Path) 356 require.NoError(err) 357 require.Equal(maybe.Some([]byte{0x11, 0x15}), nextKey) 358 } 359 360 func Test_Sync_FindNextKey_BranchInReceived(t *testing.T) { 361 require := require.New(t) 362 ctrl := gomock.NewController(t) 363 364 db, err := merkledb.New( 365 context.Background(), 366 memdb.New(), 367 newDefaultDBConfig(), 368 ) 369 require.NoError(err) 370 require.NoError(db.Put([]byte{0x11}, []byte{1})) 371 require.NoError(db.Put([]byte{0x12}, []byte{2})) 372 require.NoError(db.Put([]byte{0x12, 0xA0}, []byte{4})) 373 374 targetRoot, err := db.GetMerkleRoot(context.Background()) 375 require.NoError(err) 376 377 proof, err := db.GetProof(context.Background(), []byte{0x12}) 378 require.NoError(err) 379 380 syncer, err := NewManager(ManagerConfig{ 381 DB: db, 382 Client: NewMockClient(ctrl), 383 TargetRoot: targetRoot, 384 SimultaneousWorkLimit: 5, 385 Log: logging.NoLog{}, 386 BranchFactor: merkledb.BranchFactor16, 387 }) 388 require.NoError(err) 389 require.NoError(db.Delete([]byte{0x12, 0xA0})) 390 391 nextKey, err := syncer.findNextKey(context.Background(), []byte{0x12}, maybe.Some([]byte{0x20}), proof.Path) 392 require.NoError(err) 393 require.Equal(maybe.Some([]byte{0x12, 0xA0}), nextKey) 394 } 395 396 func Test_Sync_FindNextKey_ExtraValues(t *testing.T) { 397 require := require.New(t) 398 ctrl := gomock.NewController(t) 399 defer ctrl.Finish() 400 401 now := time.Now().UnixNano() 402 t.Logf("seed: %d", now) 403 r := rand.New(rand.NewSource(now)) // #nosec G404 404 dbToSync, err := generateTrie(t, r, 1000) 405 require.NoError(err) 406 syncRoot, err := dbToSync.GetMerkleRoot(context.Background()) 407 require.NoError(err) 408 409 db, err := merkledb.New( 410 context.Background(), 411 memdb.New(), 412 newDefaultDBConfig(), 413 ) 414 require.NoError(err) 415 syncer, err := NewManager(ManagerConfig{ 416 DB: db, 417 Client: newCallthroughSyncClient(ctrl, dbToSync), 418 TargetRoot: syncRoot, 419 SimultaneousWorkLimit: 5, 420 Log: logging.NoLog{}, 421 BranchFactor: merkledb.BranchFactor16, 422 }) 423 require.NoError(err) 424 require.NotNil(syncer) 425 426 require.NoError(syncer.Start(context.Background())) 427 require.NoError(syncer.Wait(context.Background())) 428 429 proof, err := dbToSync.GetRangeProof(context.Background(), maybe.Nothing[[]byte](), maybe.Nothing[[]byte](), 500) 430 require.NoError(err) 431 432 // add an extra value to local db 433 lastKey := proof.KeyValues[len(proof.KeyValues)-1].Key 434 midpoint := midPoint(maybe.Some(lastKey), maybe.Nothing[[]byte]()) 435 midPointVal := midpoint.Value() 436 437 require.NoError(db.Put(midPointVal, []byte{1})) 438 439 // next key at prefix of newly added point 440 nextKey, err := syncer.findNextKey(context.Background(), lastKey, maybe.Nothing[[]byte](), proof.EndProof) 441 require.NoError(err) 442 require.True(nextKey.HasValue()) 443 444 require.True(isPrefix(midPointVal, nextKey.Value())) 445 446 require.NoError(db.Delete(midPointVal)) 447 448 require.NoError(dbToSync.Put(midPointVal, []byte{1})) 449 450 proof, err = dbToSync.GetRangeProof(context.Background(), maybe.Nothing[[]byte](), maybe.Some(lastKey), 500) 451 require.NoError(err) 452 453 // next key at prefix of newly added point 454 nextKey, err = syncer.findNextKey(context.Background(), lastKey, maybe.Nothing[[]byte](), proof.EndProof) 455 require.NoError(err) 456 require.True(nextKey.HasValue()) 457 458 // deal with odd length key 459 require.True(isPrefix(midPointVal, nextKey.Value())) 460 } 461 462 func TestFindNextKeyEmptyEndProof(t *testing.T) { 463 require := require.New(t) 464 now := time.Now().UnixNano() 465 t.Logf("seed: %d", now) 466 r := rand.New(rand.NewSource(now)) // #nosec G404 467 ctrl := gomock.NewController(t) 468 defer ctrl.Finish() 469 470 db, err := merkledb.New( 471 context.Background(), 472 memdb.New(), 473 newDefaultDBConfig(), 474 ) 475 require.NoError(err) 476 477 syncer, err := NewManager(ManagerConfig{ 478 DB: db, 479 Client: NewMockClient(ctrl), 480 TargetRoot: ids.Empty, 481 SimultaneousWorkLimit: 5, 482 Log: logging.NoLog{}, 483 BranchFactor: merkledb.BranchFactor16, 484 }) 485 require.NoError(err) 486 require.NotNil(syncer) 487 488 for i := 0; i < 100; i++ { 489 lastReceivedKeyLen := r.Intn(16) 490 lastReceivedKey := make([]byte, lastReceivedKeyLen) 491 _, _ = r.Read(lastReceivedKey) // #nosec G404 492 493 rangeEndLen := r.Intn(16) 494 rangeEndBytes := make([]byte, rangeEndLen) 495 _, _ = r.Read(rangeEndBytes) // #nosec G404 496 497 rangeEnd := maybe.Nothing[[]byte]() 498 if rangeEndLen > 0 { 499 rangeEnd = maybe.Some(rangeEndBytes) 500 } 501 502 nextKey, err := syncer.findNextKey( 503 context.Background(), 504 lastReceivedKey, 505 rangeEnd, 506 nil, /* endProof */ 507 ) 508 require.NoError(err) 509 require.Equal(maybe.Some(append(lastReceivedKey, 0)), nextKey) 510 } 511 } 512 513 func isPrefix(data []byte, prefix []byte) bool { 514 if prefix[len(prefix)-1]%16 == 0 { 515 index := 0 516 for ; index < len(prefix)-1; index++ { 517 if data[index] != prefix[index] { 518 return false 519 } 520 } 521 return data[index]>>4 == prefix[index]>>4 522 } 523 return bytes.HasPrefix(data, prefix) 524 } 525 526 func Test_Sync_FindNextKey_DifferentChild(t *testing.T) { 527 require := require.New(t) 528 ctrl := gomock.NewController(t) 529 defer ctrl.Finish() 530 531 now := time.Now().UnixNano() 532 t.Logf("seed: %d", now) 533 r := rand.New(rand.NewSource(now)) // #nosec G404 534 dbToSync, err := generateTrie(t, r, 500) 535 require.NoError(err) 536 syncRoot, err := dbToSync.GetMerkleRoot(context.Background()) 537 require.NoError(err) 538 539 db, err := merkledb.New( 540 context.Background(), 541 memdb.New(), 542 newDefaultDBConfig(), 543 ) 544 require.NoError(err) 545 syncer, err := NewManager(ManagerConfig{ 546 DB: db, 547 Client: newCallthroughSyncClient(ctrl, dbToSync), 548 TargetRoot: syncRoot, 549 SimultaneousWorkLimit: 5, 550 Log: logging.NoLog{}, 551 BranchFactor: merkledb.BranchFactor16, 552 }) 553 require.NoError(err) 554 require.NotNil(syncer) 555 require.NoError(syncer.Start(context.Background())) 556 require.NoError(syncer.Wait(context.Background())) 557 558 proof, err := dbToSync.GetRangeProof(context.Background(), maybe.Nothing[[]byte](), maybe.Nothing[[]byte](), 100) 559 require.NoError(err) 560 lastKey := proof.KeyValues[len(proof.KeyValues)-1].Key 561 562 // local db has a different child than remote db 563 lastKey = append(lastKey, 16) 564 require.NoError(db.Put(lastKey, []byte{1})) 565 566 require.NoError(dbToSync.Put(lastKey, []byte{2})) 567 568 proof, err = dbToSync.GetRangeProof(context.Background(), maybe.Nothing[[]byte](), maybe.Some(proof.KeyValues[len(proof.KeyValues)-1].Key), 100) 569 require.NoError(err) 570 571 nextKey, err := syncer.findNextKey(context.Background(), proof.KeyValues[len(proof.KeyValues)-1].Key, maybe.Nothing[[]byte](), proof.EndProof) 572 require.NoError(err) 573 require.True(nextKey.HasValue()) 574 require.Equal(lastKey, nextKey.Value()) 575 } 576 577 // Test findNextKey by computing the expected result in a naive, inefficient 578 // way and comparing it to the actual result 579 func TestFindNextKeyRandom(t *testing.T) { 580 now := time.Now().UnixNano() 581 t.Logf("seed: %d", now) 582 rand := rand.New(rand.NewSource(now)) // #nosec G404 583 require := require.New(t) 584 ctrl := gomock.NewController(t) 585 defer ctrl.Finish() 586 587 // Create a "remote" database and "local" database 588 remoteDB, err := merkledb.New( 589 context.Background(), 590 memdb.New(), 591 newDefaultDBConfig(), 592 ) 593 require.NoError(err) 594 595 config := newDefaultDBConfig() 596 localDB, err := merkledb.New( 597 context.Background(), 598 memdb.New(), 599 config, 600 ) 601 require.NoError(err) 602 603 var ( 604 numProofsToTest = 250 605 numKeyValues = 250 606 maxKeyLen = 256 607 maxValLen = 256 608 maxRangeStartLen = 8 609 maxRangeEndLen = 8 610 maxProofLen = 128 611 ) 612 613 // Put random keys into the databases 614 for _, db := range []database.Database{remoteDB, localDB} { 615 for i := 0; i < numKeyValues; i++ { 616 key := make([]byte, rand.Intn(maxKeyLen)) 617 _, _ = rand.Read(key) 618 val := make([]byte, rand.Intn(maxValLen)) 619 _, _ = rand.Read(val) 620 require.NoError(db.Put(key, val)) 621 } 622 } 623 624 // Repeatedly generate end proofs from the remote database and compare 625 // the result of findNextKey to the expected result. 626 for proofIndex := 0; proofIndex < numProofsToTest; proofIndex++ { 627 // Generate a proof for a random key 628 var ( 629 rangeStart []byte 630 rangeEnd []byte 631 ) 632 // Generate a valid range start and end 633 for rangeStart == nil || bytes.Compare(rangeStart, rangeEnd) == 1 { 634 rangeStart = make([]byte, rand.Intn(maxRangeStartLen)+1) 635 _, _ = rand.Read(rangeStart) 636 rangeEnd = make([]byte, rand.Intn(maxRangeEndLen)+1) 637 _, _ = rand.Read(rangeEnd) 638 } 639 640 startKey := maybe.Nothing[[]byte]() 641 if len(rangeStart) > 0 { 642 startKey = maybe.Some(rangeStart) 643 } 644 endKey := maybe.Nothing[[]byte]() 645 if len(rangeEnd) > 0 { 646 endKey = maybe.Some(rangeEnd) 647 } 648 649 remoteProof, err := remoteDB.GetRangeProof( 650 context.Background(), 651 startKey, 652 endKey, 653 rand.Intn(maxProofLen)+1, 654 ) 655 require.NoError(err) 656 657 if len(remoteProof.KeyValues) == 0 { 658 continue 659 } 660 lastReceivedKey := remoteProof.KeyValues[len(remoteProof.KeyValues)-1].Key 661 662 // Commit the proof to the local database as we do 663 // in the actual syncer. 664 require.NoError(localDB.CommitRangeProof( 665 context.Background(), 666 startKey, 667 endKey, 668 remoteProof, 669 )) 670 671 localProof, err := localDB.GetProof( 672 context.Background(), 673 lastReceivedKey, 674 ) 675 require.NoError(err) 676 677 type keyAndID struct { 678 key merkledb.Key 679 id ids.ID 680 } 681 682 // Set of key prefix/ID pairs proven by the remote database's end proof. 683 remoteKeyIDs := []keyAndID{} 684 for _, node := range remoteProof.EndProof { 685 for childIdx, childID := range node.Children { 686 remoteKeyIDs = append(remoteKeyIDs, keyAndID{ 687 key: node.Key.Extend(merkledb.ToToken(childIdx, merkledb.BranchFactorToTokenSize[config.BranchFactor])), 688 id: childID, 689 }) 690 } 691 } 692 693 // Set of key prefix/ID pairs proven by the local database's proof. 694 localKeyIDs := []keyAndID{} 695 for _, node := range localProof.Path { 696 for childIdx, childID := range node.Children { 697 localKeyIDs = append(localKeyIDs, keyAndID{ 698 key: node.Key.Extend(merkledb.ToToken(childIdx, merkledb.BranchFactorToTokenSize[config.BranchFactor])), 699 id: childID, 700 }) 701 } 702 } 703 704 // Sort in ascending order by key prefix. 705 serializedPathCompare := func(i, j keyAndID) int { 706 return i.key.Compare(j.key) 707 } 708 slices.SortFunc(remoteKeyIDs, serializedPathCompare) 709 slices.SortFunc(localKeyIDs, serializedPathCompare) 710 711 // Filter out keys that are before the last received key 712 findBounds := func(keyIDs []keyAndID) (int, int) { 713 var ( 714 firstIdxInRange = len(keyIDs) 715 firstIdxInRangeFound = false 716 firstIdxOutOfRange = len(keyIDs) 717 ) 718 for i, keyID := range keyIDs { 719 if !firstIdxInRangeFound && bytes.Compare(keyID.key.Bytes(), lastReceivedKey) > 0 { 720 firstIdxInRange = i 721 firstIdxInRangeFound = true 722 continue 723 } 724 if bytes.Compare(keyID.key.Bytes(), rangeEnd) > 0 { 725 firstIdxOutOfRange = i 726 break 727 } 728 } 729 return firstIdxInRange, firstIdxOutOfRange 730 } 731 732 remoteFirstIdxAfterLastReceived, remoteFirstIdxAfterEnd := findBounds(remoteKeyIDs) 733 remoteKeyIDs = remoteKeyIDs[remoteFirstIdxAfterLastReceived:remoteFirstIdxAfterEnd] 734 735 localFirstIdxAfterLastReceived, localFirstIdxAfterEnd := findBounds(localKeyIDs) 736 localKeyIDs = localKeyIDs[localFirstIdxAfterLastReceived:localFirstIdxAfterEnd] 737 738 // Find smallest difference between the set of key/ID pairs proven by 739 // the remote/local proofs for key/ID pairs after the last received key. 740 var ( 741 smallestDiffKey merkledb.Key 742 foundDiff bool 743 ) 744 for i := 0; i < len(remoteKeyIDs) && i < len(localKeyIDs); i++ { 745 // See if the keys are different. 746 smaller, bigger := remoteKeyIDs[i], localKeyIDs[i] 747 if serializedPathCompare(localKeyIDs[i], remoteKeyIDs[i]) == -1 { 748 smaller, bigger = localKeyIDs[i], remoteKeyIDs[i] 749 } 750 751 if smaller.key != bigger.key || smaller.id != bigger.id { 752 smallestDiffKey = smaller.key 753 foundDiff = true 754 break 755 } 756 } 757 if !foundDiff { 758 // All the keys were equal. The smallest diff is the next key 759 // in the longer of the lists (if they're not same length.) 760 if len(remoteKeyIDs) < len(localKeyIDs) { 761 smallestDiffKey = localKeyIDs[len(remoteKeyIDs)].key 762 } else if len(remoteKeyIDs) > len(localKeyIDs) { 763 smallestDiffKey = remoteKeyIDs[len(localKeyIDs)].key 764 } 765 } 766 767 // Get the actual value from the syncer 768 syncer, err := NewManager(ManagerConfig{ 769 DB: localDB, 770 Client: NewMockClient(ctrl), 771 TargetRoot: ids.GenerateTestID(), 772 SimultaneousWorkLimit: 5, 773 Log: logging.NoLog{}, 774 BranchFactor: merkledb.BranchFactor16, 775 }) 776 require.NoError(err) 777 778 gotFirstDiff, err := syncer.findNextKey( 779 context.Background(), 780 lastReceivedKey, 781 endKey, 782 remoteProof.EndProof, 783 ) 784 require.NoError(err) 785 786 if bytes.Compare(smallestDiffKey.Bytes(), rangeEnd) >= 0 { 787 // The smallest key which differs is after the range end so the 788 // next key to get should be nil because we're done fetching the range. 789 require.True(gotFirstDiff.IsNothing()) 790 } else { 791 require.Equal(smallestDiffKey.Bytes(), gotFirstDiff.Value()) 792 } 793 } 794 } 795 796 func Test_Sync_Result_Correct_Root(t *testing.T) { 797 require := require.New(t) 798 ctrl := gomock.NewController(t) 799 defer ctrl.Finish() 800 801 now := time.Now().UnixNano() 802 t.Logf("seed: %d", now) 803 r := rand.New(rand.NewSource(now)) // #nosec G404 804 dbToSync, err := generateTrie(t, r, 1000) 805 require.NoError(err) 806 syncRoot, err := dbToSync.GetMerkleRoot(context.Background()) 807 require.NoError(err) 808 809 db, err := merkledb.New( 810 context.Background(), 811 memdb.New(), 812 newDefaultDBConfig(), 813 ) 814 require.NoError(err) 815 syncer, err := NewManager(ManagerConfig{ 816 DB: db, 817 Client: newCallthroughSyncClient(ctrl, dbToSync), 818 TargetRoot: syncRoot, 819 SimultaneousWorkLimit: 5, 820 Log: logging.NoLog{}, 821 BranchFactor: merkledb.BranchFactor16, 822 }) 823 require.NoError(err) 824 require.NotNil(syncer) 825 require.NoError(syncer.Start(context.Background())) 826 827 require.NoError(syncer.Wait(context.Background())) 828 require.NoError(syncer.Error()) 829 830 // new db has fully sync'ed and should be at the same root as the original db 831 newRoot, err := db.GetMerkleRoot(context.Background()) 832 require.NoError(err) 833 require.Equal(syncRoot, newRoot) 834 835 // make sure they stay in sync 836 addkey := make([]byte, r.Intn(50)) 837 _, err = r.Read(addkey) 838 require.NoError(err) 839 val := make([]byte, r.Intn(50)) 840 _, err = r.Read(val) 841 require.NoError(err) 842 843 require.NoError(db.Put(addkey, val)) 844 845 require.NoError(dbToSync.Put(addkey, val)) 846 847 syncRoot, err = dbToSync.GetMerkleRoot(context.Background()) 848 require.NoError(err) 849 850 newRoot, err = db.GetMerkleRoot(context.Background()) 851 require.NoError(err) 852 require.Equal(syncRoot, newRoot) 853 } 854 855 func Test_Sync_Result_Correct_Root_With_Sync_Restart(t *testing.T) { 856 require := require.New(t) 857 ctrl := gomock.NewController(t) 858 859 now := time.Now().UnixNano() 860 t.Logf("seed: %d", now) 861 r := rand.New(rand.NewSource(now)) // #nosec G404 862 dbToSync, err := generateTrie(t, r, 3*maxKeyValuesLimit) 863 require.NoError(err) 864 syncRoot, err := dbToSync.GetMerkleRoot(context.Background()) 865 require.NoError(err) 866 867 db, err := merkledb.New( 868 context.Background(), 869 memdb.New(), 870 newDefaultDBConfig(), 871 ) 872 require.NoError(err) 873 874 syncer, err := NewManager(ManagerConfig{ 875 DB: db, 876 Client: newCallthroughSyncClient(ctrl, dbToSync), 877 TargetRoot: syncRoot, 878 SimultaneousWorkLimit: 5, 879 Log: logging.NoLog{}, 880 BranchFactor: merkledb.BranchFactor16, 881 }) 882 require.NoError(err) 883 require.NotNil(syncer) 884 require.NoError(syncer.Start(context.Background())) 885 886 // Wait until we've processed some work 887 // before updating the sync target. 888 require.Eventually( 889 func() bool { 890 syncer.workLock.Lock() 891 defer syncer.workLock.Unlock() 892 893 return syncer.processedWork.Len() > 0 894 }, 895 5*time.Second, 896 5*time.Millisecond, 897 ) 898 syncer.Close() 899 900 newSyncer, err := NewManager(ManagerConfig{ 901 DB: db, 902 Client: newCallthroughSyncClient(ctrl, dbToSync), 903 TargetRoot: syncRoot, 904 SimultaneousWorkLimit: 5, 905 Log: logging.NoLog{}, 906 BranchFactor: merkledb.BranchFactor16, 907 }) 908 require.NoError(err) 909 require.NotNil(newSyncer) 910 911 require.NoError(newSyncer.Start(context.Background())) 912 require.NoError(newSyncer.Error()) 913 require.NoError(newSyncer.Wait(context.Background())) 914 915 newRoot, err := db.GetMerkleRoot(context.Background()) 916 require.NoError(err) 917 require.Equal(syncRoot, newRoot) 918 } 919 920 func Test_Sync_Error_During_Sync(t *testing.T) { 921 require := require.New(t) 922 ctrl := gomock.NewController(t) 923 now := time.Now().UnixNano() 924 t.Logf("seed: %d", now) 925 r := rand.New(rand.NewSource(now)) // #nosec G404 926 927 dbToSync, err := generateTrie(t, r, 100) 928 require.NoError(err) 929 930 syncRoot, err := dbToSync.GetMerkleRoot(context.Background()) 931 require.NoError(err) 932 933 db, err := merkledb.New( 934 context.Background(), 935 memdb.New(), 936 newDefaultDBConfig(), 937 ) 938 require.NoError(err) 939 940 client := NewMockClient(ctrl) 941 client.EXPECT().GetRangeProof(gomock.Any(), gomock.Any()).DoAndReturn( 942 func(context.Context, *pb.SyncGetRangeProofRequest) (*merkledb.RangeProof, error) { 943 return nil, errInvalidRangeProof 944 }, 945 ).AnyTimes() 946 client.EXPECT().GetChangeProof(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn( 947 func(ctx context.Context, request *pb.SyncGetChangeProofRequest, _ DB) (*merkledb.ChangeOrRangeProof, error) { 948 startRoot, err := ids.ToID(request.StartRootHash) 949 require.NoError(err) 950 951 endRoot, err := ids.ToID(request.EndRootHash) 952 require.NoError(err) 953 954 changeProof, err := dbToSync.GetChangeProof(ctx, startRoot, endRoot, maybeBytesToMaybe(request.StartKey), maybeBytesToMaybe(request.EndKey), int(request.KeyLimit)) 955 if err != nil { 956 return nil, err 957 } 958 959 return &merkledb.ChangeOrRangeProof{ 960 ChangeProof: changeProof, 961 }, nil 962 }, 963 ).AnyTimes() 964 965 syncer, err := NewManager(ManagerConfig{ 966 DB: db, 967 Client: client, 968 TargetRoot: syncRoot, 969 SimultaneousWorkLimit: 5, 970 Log: logging.NoLog{}, 971 BranchFactor: merkledb.BranchFactor16, 972 }) 973 require.NoError(err) 974 require.NotNil(syncer) 975 976 require.NoError(syncer.Start(context.Background())) 977 978 err = syncer.Wait(context.Background()) 979 require.ErrorIs(err, errInvalidRangeProof) 980 } 981 982 func Test_Sync_Result_Correct_Root_Update_Root_During(t *testing.T) { 983 require := require.New(t) 984 ctrl := gomock.NewController(t) 985 986 now := time.Now().UnixNano() 987 t.Logf("seed: %d", now) 988 r := rand.New(rand.NewSource(now)) // #nosec G404 989 990 dbToSync, err := generateTrie(t, r, 3*maxKeyValuesLimit) 991 require.NoError(err) 992 993 firstSyncRoot, err := dbToSync.GetMerkleRoot(context.Background()) 994 require.NoError(err) 995 996 for x := 0; x < 100; x++ { 997 key := make([]byte, r.Intn(50)) 998 _, err = r.Read(key) 999 require.NoError(err) 1000 1001 val := make([]byte, r.Intn(50)) 1002 _, err = r.Read(val) 1003 require.NoError(err) 1004 1005 require.NoError(dbToSync.Put(key, val)) 1006 1007 deleteKeyStart := make([]byte, r.Intn(50)) 1008 _, err = r.Read(deleteKeyStart) 1009 require.NoError(err) 1010 1011 it := dbToSync.NewIteratorWithStart(deleteKeyStart) 1012 if it.Next() { 1013 require.NoError(dbToSync.Delete(it.Key())) 1014 } 1015 require.NoError(it.Error()) 1016 it.Release() 1017 } 1018 1019 secondSyncRoot, err := dbToSync.GetMerkleRoot(context.Background()) 1020 require.NoError(err) 1021 1022 db, err := merkledb.New( 1023 context.Background(), 1024 memdb.New(), 1025 newDefaultDBConfig(), 1026 ) 1027 require.NoError(err) 1028 1029 // Only let one response go through until we update the root. 1030 updatedRootChan := make(chan struct{}, 1) 1031 updatedRootChan <- struct{}{} 1032 1033 client := NewMockClient(ctrl) 1034 client.EXPECT().GetRangeProof(gomock.Any(), gomock.Any()).DoAndReturn( 1035 func(ctx context.Context, request *pb.SyncGetRangeProofRequest) (*merkledb.RangeProof, error) { 1036 <-updatedRootChan 1037 root, err := ids.ToID(request.RootHash) 1038 require.NoError(err) 1039 return dbToSync.GetRangeProofAtRoot(ctx, root, maybeBytesToMaybe(request.StartKey), maybeBytesToMaybe(request.EndKey), int(request.KeyLimit)) 1040 }, 1041 ).AnyTimes() 1042 client.EXPECT().GetChangeProof(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn( 1043 func(ctx context.Context, request *pb.SyncGetChangeProofRequest, _ DB) (*merkledb.ChangeOrRangeProof, error) { 1044 <-updatedRootChan 1045 1046 startRoot, err := ids.ToID(request.StartRootHash) 1047 require.NoError(err) 1048 1049 endRoot, err := ids.ToID(request.EndRootHash) 1050 require.NoError(err) 1051 1052 changeProof, err := dbToSync.GetChangeProof(ctx, startRoot, endRoot, maybeBytesToMaybe(request.StartKey), maybeBytesToMaybe(request.EndKey), int(request.KeyLimit)) 1053 if err != nil { 1054 return nil, err 1055 } 1056 1057 return &merkledb.ChangeOrRangeProof{ 1058 ChangeProof: changeProof, 1059 }, nil 1060 }, 1061 ).AnyTimes() 1062 1063 syncer, err := NewManager(ManagerConfig{ 1064 DB: db, 1065 Client: client, 1066 TargetRoot: firstSyncRoot, 1067 SimultaneousWorkLimit: 5, 1068 Log: logging.NoLog{}, 1069 BranchFactor: merkledb.BranchFactor16, 1070 }) 1071 require.NoError(err) 1072 require.NotNil(syncer) 1073 1074 require.NoError(syncer.Start(context.Background())) 1075 1076 // Wait until we've processed some work 1077 // before updating the sync target. 1078 require.Eventually( 1079 func() bool { 1080 syncer.workLock.Lock() 1081 defer syncer.workLock.Unlock() 1082 1083 return syncer.processedWork.Len() > 0 1084 }, 1085 5*time.Second, 1086 10*time.Millisecond, 1087 ) 1088 require.NoError(syncer.UpdateSyncTarget(secondSyncRoot)) 1089 close(updatedRootChan) 1090 1091 require.NoError(syncer.Wait(context.Background())) 1092 require.NoError(syncer.Error()) 1093 1094 newRoot, err := db.GetMerkleRoot(context.Background()) 1095 require.NoError(err) 1096 require.Equal(secondSyncRoot, newRoot) 1097 } 1098 1099 func Test_Sync_UpdateSyncTarget(t *testing.T) { 1100 require := require.New(t) 1101 ctrl := gomock.NewController(t) 1102 1103 m, err := NewManager(ManagerConfig{ 1104 DB: merkledb.NewMockMerkleDB(ctrl), // Not used 1105 Client: NewMockClient(ctrl), // Not used 1106 TargetRoot: ids.Empty, 1107 SimultaneousWorkLimit: 5, 1108 Log: logging.NoLog{}, 1109 BranchFactor: merkledb.BranchFactor16, 1110 }) 1111 require.NoError(err) 1112 1113 // Populate [m.processWork] to ensure that UpdateSyncTarget 1114 // moves the work to [m.unprocessedWork]. 1115 item := &workItem{ 1116 start: maybe.Some([]byte{1}), 1117 end: maybe.Some([]byte{2}), 1118 localRootID: ids.GenerateTestID(), 1119 } 1120 m.processedWork.Insert(item) 1121 1122 // Make sure that [m.unprocessedWorkCond] is signaled. 1123 gotSignalChan := make(chan struct{}) 1124 // Don't UpdateSyncTarget until we're waiting for the signal. 1125 startedWaiting := make(chan struct{}) 1126 go func() { 1127 m.workLock.Lock() 1128 defer m.workLock.Unlock() 1129 1130 close(startedWaiting) 1131 m.unprocessedWorkCond.Wait() 1132 close(gotSignalChan) 1133 }() 1134 1135 <-startedWaiting 1136 newSyncRoot := ids.GenerateTestID() 1137 require.NoError(m.UpdateSyncTarget(newSyncRoot)) 1138 <-gotSignalChan 1139 1140 require.Equal(newSyncRoot, m.config.TargetRoot) 1141 require.Zero(m.processedWork.Len()) 1142 require.Equal(1, m.unprocessedWork.Len()) 1143 } 1144 1145 func generateTrie(t *testing.T, r *rand.Rand, count int) (merkledb.MerkleDB, error) { 1146 db, _, err := generateTrieWithMinKeyLen(t, r, count, 0) 1147 return db, err 1148 } 1149 1150 func generateTrieWithMinKeyLen(t *testing.T, r *rand.Rand, count int, minKeyLen int) (merkledb.MerkleDB, [][]byte, error) { 1151 require := require.New(t) 1152 1153 db, err := merkledb.New( 1154 context.Background(), 1155 memdb.New(), 1156 newDefaultDBConfig(), 1157 ) 1158 if err != nil { 1159 return nil, nil, err 1160 } 1161 var ( 1162 allKeys [][]byte 1163 seenKeys = make(map[string]struct{}) 1164 batch = db.NewBatch() 1165 ) 1166 genKey := func() []byte { 1167 // new prefixed key 1168 if len(allKeys) > 2 && r.Intn(25) < 10 { 1169 prefix := allKeys[r.Intn(len(allKeys))] 1170 key := make([]byte, r.Intn(50)+len(prefix)) 1171 copy(key, prefix) 1172 _, err := r.Read(key[len(prefix):]) 1173 require.NoError(err) 1174 return key 1175 } 1176 1177 // new key 1178 key := make([]byte, r.Intn(50)+minKeyLen) 1179 _, err = r.Read(key) 1180 require.NoError(err) 1181 return key 1182 } 1183 1184 for i := 0; i < count; { 1185 value := make([]byte, r.Intn(51)) 1186 if len(value) == 0 { 1187 value = nil 1188 } else { 1189 _, err = r.Read(value) 1190 require.NoError(err) 1191 } 1192 key := genKey() 1193 if _, seen := seenKeys[string(key)]; seen { 1194 continue // avoid duplicate keys so we always get the count 1195 } 1196 allKeys = append(allKeys, key) 1197 seenKeys[string(key)] = struct{}{} 1198 if err = batch.Put(key, value); err != nil { 1199 return db, nil, err 1200 } 1201 i++ 1202 } 1203 slices.SortFunc(allKeys, bytes.Compare) 1204 return db, allKeys, batch.Write() 1205 }