github.com/MetalBlockchain/metalgo@v1.11.9/x/sync/client_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 "context" 8 "math/rand" 9 "testing" 10 "time" 11 12 "github.com/prometheus/client_golang/prometheus" 13 "github.com/stretchr/testify/require" 14 "go.uber.org/mock/gomock" 15 "google.golang.org/protobuf/proto" 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/snow/engine/common" 21 "github.com/MetalBlockchain/metalgo/trace" 22 "github.com/MetalBlockchain/metalgo/utils/logging" 23 "github.com/MetalBlockchain/metalgo/utils/maybe" 24 "github.com/MetalBlockchain/metalgo/x/merkledb" 25 26 pb "github.com/MetalBlockchain/metalgo/proto/pb/sync" 27 ) 28 29 func newDefaultDBConfig() merkledb.Config { 30 return merkledb.Config{ 31 IntermediateWriteBatchSize: 100, 32 HistoryLength: defaultRequestKeyLimit, 33 ValueNodeCacheSize: defaultRequestKeyLimit, 34 IntermediateWriteBufferSize: defaultRequestKeyLimit, 35 IntermediateNodeCacheSize: defaultRequestKeyLimit, 36 Reg: prometheus.NewRegistry(), 37 Tracer: trace.Noop, 38 BranchFactor: merkledb.BranchFactor16, 39 } 40 } 41 42 // Create a client and send a range proof request to a server 43 // whose underlying database is [serverDB]. 44 // The server's response is modified with [modifyResponse] before 45 // being returned to the server. 46 // The client makes at most [maxAttempts] attempts to fulfill 47 // the request before returning an error. 48 func sendRangeProofRequest( 49 t *testing.T, 50 serverDB DB, 51 request *pb.SyncGetRangeProofRequest, 52 maxAttempts int, 53 modifyResponse func(*merkledb.RangeProof), 54 ) (*merkledb.RangeProof, error) { 55 t.Helper() 56 57 require := require.New(t) 58 ctrl := gomock.NewController(t) 59 60 var ( 61 // Number of calls from the client to the server so far. 62 numAttempts int 63 64 // Sends messages from server to client. 65 sender = common.NewMockSender(ctrl) 66 67 // Serves the range proof. 68 server = NewNetworkServer(sender, serverDB, logging.NoLog{}) 69 70 clientNodeID, serverNodeID = ids.GenerateTestNodeID(), ids.GenerateTestNodeID() 71 72 // "Sends" the request from the client to the server and 73 // "receives" the response from the server. In reality, 74 // it just invokes the server's method and receives 75 // the response on [serverResponseChan]. 76 networkClient = NewMockNetworkClient(ctrl) 77 78 serverResponseChan = make(chan []byte, 1) 79 80 // The context used in client.GetRangeProof. 81 // Canceled after the first response is received because 82 // the client will keep sending requests until its context 83 // expires or it succeeds. 84 ctx, cancel = context.WithCancel(context.Background()) 85 ) 86 87 defer cancel() 88 89 // The client fetching a range proof. 90 client, err := NewClient(&ClientConfig{ 91 NetworkClient: networkClient, 92 Metrics: &mockMetrics{}, 93 Log: logging.NoLog{}, 94 BranchFactor: merkledb.BranchFactor16, 95 }) 96 require.NoError(err) 97 98 networkClient.EXPECT().RequestAny( 99 gomock.Any(), // ctx 100 gomock.Any(), // request 101 ).DoAndReturn( 102 func(_ context.Context, request []byte) (ids.NodeID, []byte, error) { 103 go func() { 104 // Get response from server 105 require.NoError(server.AppRequest(context.Background(), clientNodeID, 0, time.Now().Add(time.Hour), request)) 106 }() 107 108 // Wait for response from server 109 serverResponse := <-serverResponseChan 110 111 numAttempts++ 112 113 if numAttempts >= maxAttempts { 114 defer cancel() 115 } 116 117 return serverNodeID, serverResponse, nil 118 }, 119 ).AnyTimes() 120 121 // The server should expect to "send" a response to the client. 122 sender.EXPECT().SendAppResponse( 123 gomock.Any(), // ctx 124 clientNodeID, 125 gomock.Any(), // requestID 126 gomock.Any(), // responseBytes 127 ).DoAndReturn( 128 func(_ context.Context, _ ids.NodeID, _ uint32, responseBytes []byte) error { 129 // deserialize the response so we can modify it if needed. 130 var responseProto pb.RangeProof 131 require.NoError(proto.Unmarshal(responseBytes, &responseProto)) 132 133 var response merkledb.RangeProof 134 require.NoError(response.UnmarshalProto(&responseProto)) 135 136 // modify if needed 137 if modifyResponse != nil { 138 modifyResponse(&response) 139 } 140 141 // reserialize the response and pass it to the client to complete the handling. 142 responseBytes, err := proto.Marshal(response.ToProto()) 143 require.NoError(err) 144 145 serverResponseChan <- responseBytes 146 147 return nil 148 }, 149 ).AnyTimes() 150 151 return client.GetRangeProof(ctx, request) 152 } 153 154 func TestGetRangeProof(t *testing.T) { 155 now := time.Now().UnixNano() 156 t.Logf("seed: %d", now) 157 r := rand.New(rand.NewSource(now)) // #nosec G404 158 159 smallTrieKeyCount := defaultRequestKeyLimit 160 smallTrieDB, _, err := generateTrieWithMinKeyLen(t, r, smallTrieKeyCount, 1) 161 require.NoError(t, err) 162 smallTrieRoot, err := smallTrieDB.GetMerkleRoot(context.Background()) 163 require.NoError(t, err) 164 165 largeTrieKeyCount := 3 * defaultRequestKeyLimit 166 largeTrieDB, largeTrieKeys, err := generateTrieWithMinKeyLen(t, r, largeTrieKeyCount, 1) 167 require.NoError(t, err) 168 largeTrieRoot, err := largeTrieDB.GetMerkleRoot(context.Background()) 169 require.NoError(t, err) 170 171 tests := map[string]struct { 172 db DB 173 request *pb.SyncGetRangeProofRequest 174 modifyResponse func(*merkledb.RangeProof) 175 expectedErr error 176 expectedResponseLen int 177 }{ 178 "proof restricted by BytesLimit": { 179 db: smallTrieDB, 180 request: &pb.SyncGetRangeProofRequest{ 181 RootHash: smallTrieRoot[:], 182 KeyLimit: defaultRequestKeyLimit, 183 BytesLimit: 10000, 184 }, 185 }, 186 "full response for small (single request) trie": { 187 db: smallTrieDB, 188 request: &pb.SyncGetRangeProofRequest{ 189 RootHash: smallTrieRoot[:], 190 KeyLimit: defaultRequestKeyLimit, 191 BytesLimit: defaultRequestByteSizeLimit, 192 }, 193 expectedResponseLen: defaultRequestKeyLimit, 194 }, 195 "too many leaves in response": { 196 db: smallTrieDB, 197 request: &pb.SyncGetRangeProofRequest{ 198 RootHash: smallTrieRoot[:], 199 KeyLimit: defaultRequestKeyLimit, 200 BytesLimit: defaultRequestByteSizeLimit, 201 }, 202 modifyResponse: func(response *merkledb.RangeProof) { 203 response.KeyValues = append(response.KeyValues, merkledb.KeyValue{}) 204 }, 205 expectedErr: errTooManyKeys, 206 }, 207 "partial response to request for entire trie (full leaf limit)": { 208 db: largeTrieDB, 209 request: &pb.SyncGetRangeProofRequest{ 210 RootHash: largeTrieRoot[:], 211 KeyLimit: defaultRequestKeyLimit, 212 BytesLimit: defaultRequestByteSizeLimit, 213 }, 214 expectedResponseLen: defaultRequestKeyLimit, 215 }, 216 "full response from near end of trie to end of trie (less than leaf limit)": { 217 db: largeTrieDB, 218 request: &pb.SyncGetRangeProofRequest{ 219 RootHash: largeTrieRoot[:], 220 StartKey: &pb.MaybeBytes{ 221 Value: largeTrieKeys[len(largeTrieKeys)-30], // Set start 30 keys from the end of the large trie 222 IsNothing: false, 223 }, 224 KeyLimit: defaultRequestKeyLimit, 225 BytesLimit: defaultRequestByteSizeLimit, 226 }, 227 expectedResponseLen: 30, 228 }, 229 "full response for intermediate range of trie (less than leaf limit)": { 230 db: largeTrieDB, 231 request: &pb.SyncGetRangeProofRequest{ 232 RootHash: largeTrieRoot[:], 233 StartKey: &pb.MaybeBytes{ 234 Value: largeTrieKeys[1000], // Set the range for 1000 leafs in an intermediate range of the trie 235 IsNothing: false, 236 }, 237 EndKey: &pb.MaybeBytes{Value: largeTrieKeys[1099]}, // (inclusive range) 238 KeyLimit: defaultRequestKeyLimit, 239 BytesLimit: defaultRequestByteSizeLimit, 240 }, 241 expectedResponseLen: 100, 242 }, 243 "removed first key in response": { 244 db: largeTrieDB, 245 request: &pb.SyncGetRangeProofRequest{ 246 RootHash: largeTrieRoot[:], 247 KeyLimit: defaultRequestKeyLimit, 248 BytesLimit: defaultRequestByteSizeLimit, 249 }, 250 modifyResponse: func(response *merkledb.RangeProof) { 251 response.KeyValues = response.KeyValues[1:] 252 }, 253 expectedErr: merkledb.ErrInvalidProof, 254 }, 255 "removed first key in response and replaced proof": { 256 db: largeTrieDB, 257 request: &pb.SyncGetRangeProofRequest{ 258 RootHash: largeTrieRoot[:], 259 KeyLimit: defaultRequestKeyLimit, 260 BytesLimit: defaultRequestByteSizeLimit, 261 }, 262 modifyResponse: func(response *merkledb.RangeProof) { 263 start := maybe.Some(response.KeyValues[1].Key) 264 rootID, err := largeTrieDB.GetMerkleRoot(context.Background()) 265 require.NoError(t, err) 266 proof, err := largeTrieDB.GetRangeProofAtRoot(context.Background(), rootID, start, maybe.Nothing[[]byte](), defaultRequestKeyLimit) 267 require.NoError(t, err) 268 response.KeyValues = proof.KeyValues 269 response.StartProof = proof.StartProof 270 response.EndProof = proof.EndProof 271 }, 272 expectedErr: errInvalidRangeProof, 273 }, 274 "removed key from middle of response": { 275 db: largeTrieDB, 276 request: &pb.SyncGetRangeProofRequest{ 277 RootHash: largeTrieRoot[:], 278 KeyLimit: defaultRequestKeyLimit, 279 BytesLimit: defaultRequestByteSizeLimit, 280 }, 281 modifyResponse: func(response *merkledb.RangeProof) { 282 response.KeyValues = append(response.KeyValues[:100], response.KeyValues[101:]...) 283 }, 284 expectedErr: merkledb.ErrInvalidProof, 285 }, 286 "start and end proof nodes removed": { 287 db: largeTrieDB, 288 request: &pb.SyncGetRangeProofRequest{ 289 RootHash: largeTrieRoot[:], 290 KeyLimit: defaultRequestKeyLimit, 291 BytesLimit: defaultRequestByteSizeLimit, 292 }, 293 modifyResponse: func(response *merkledb.RangeProof) { 294 response.StartProof = nil 295 response.EndProof = nil 296 }, 297 expectedErr: merkledb.ErrNoEndProof, 298 }, 299 "end proof removed": { 300 db: largeTrieDB, 301 request: &pb.SyncGetRangeProofRequest{ 302 RootHash: largeTrieRoot[:], 303 KeyLimit: defaultRequestKeyLimit, 304 BytesLimit: defaultRequestByteSizeLimit, 305 }, 306 modifyResponse: func(response *merkledb.RangeProof) { 307 response.EndProof = nil 308 }, 309 expectedErr: merkledb.ErrNoEndProof, 310 }, 311 "empty proof": { 312 db: largeTrieDB, 313 request: &pb.SyncGetRangeProofRequest{ 314 RootHash: largeTrieRoot[:], 315 KeyLimit: defaultRequestKeyLimit, 316 BytesLimit: defaultRequestByteSizeLimit, 317 }, 318 modifyResponse: func(response *merkledb.RangeProof) { 319 response.StartProof = nil 320 response.EndProof = nil 321 response.KeyValues = nil 322 }, 323 expectedErr: merkledb.ErrEmptyProof, 324 }, 325 } 326 327 for name, test := range tests { 328 t.Run(name, func(t *testing.T) { 329 require := require.New(t) 330 proof, err := sendRangeProofRequest(t, test.db, test.request, 1, test.modifyResponse) 331 require.ErrorIs(err, test.expectedErr) 332 if test.expectedErr != nil { 333 return 334 } 335 if test.expectedResponseLen > 0 { 336 require.Len(proof.KeyValues, test.expectedResponseLen) 337 } 338 bytes, err := proto.Marshal(proof.ToProto()) 339 require.NoError(err) 340 require.Less(len(bytes), int(test.request.BytesLimit)) 341 }) 342 } 343 } 344 345 func sendChangeProofRequest( 346 t *testing.T, 347 serverDB DB, 348 clientDB DB, 349 request *pb.SyncGetChangeProofRequest, 350 maxAttempts int, 351 modifyChangeProof func(*merkledb.ChangeProof), 352 modifyRangeProof func(*merkledb.RangeProof), 353 ) (*merkledb.ChangeOrRangeProof, error) { 354 t.Helper() 355 356 require := require.New(t) 357 ctrl := gomock.NewController(t) 358 359 var ( 360 // Number of calls from the client to the server so far. 361 numAttempts int 362 363 // Sends messages from server to client. 364 sender = common.NewMockSender(ctrl) 365 366 // Serves the change proof. 367 server = NewNetworkServer(sender, serverDB, logging.NoLog{}) 368 369 clientNodeID, serverNodeID = ids.GenerateTestNodeID(), ids.GenerateTestNodeID() 370 371 // "Sends" the request from the client to the server and 372 // "receives" the response from the server. In reality, 373 // it just invokes the server's method and receives 374 // the response on [serverResponseChan]. 375 networkClient = NewMockNetworkClient(ctrl) 376 377 serverResponseChan = make(chan []byte, 1) 378 379 // The context used in client.GetChangeProof. 380 // Canceled after the first response is received because 381 // the client will keep sending requests until its context 382 // expires or it succeeds. 383 ctx, cancel = context.WithCancel(context.Background()) 384 ) 385 386 // The client fetching a change proof. 387 client, err := NewClient(&ClientConfig{ 388 NetworkClient: networkClient, 389 Metrics: &mockMetrics{}, 390 Log: logging.NoLog{}, 391 BranchFactor: merkledb.BranchFactor16, 392 }) 393 require.NoError(err) 394 395 defer cancel() // avoid leaking a goroutine 396 397 networkClient.EXPECT().RequestAny( 398 gomock.Any(), // ctx 399 gomock.Any(), // request 400 ).DoAndReturn( 401 func(_ context.Context, request []byte) (ids.NodeID, []byte, error) { 402 go func() { 403 // Get response from server 404 require.NoError(server.AppRequest(context.Background(), clientNodeID, 0, time.Now().Add(time.Hour), request)) 405 }() 406 407 // Wait for response from server 408 serverResponse := <-serverResponseChan 409 410 numAttempts++ 411 412 if numAttempts >= maxAttempts { 413 defer cancel() 414 } 415 416 return serverNodeID, serverResponse, nil 417 }, 418 ).AnyTimes() 419 420 // Expect server (serverDB) to send app response to client (clientDB) 421 sender.EXPECT().SendAppResponse( 422 gomock.Any(), // ctx 423 clientNodeID, 424 gomock.Any(), // requestID 425 gomock.Any(), // responseBytes 426 ).DoAndReturn( 427 func(_ context.Context, _ ids.NodeID, _ uint32, responseBytes []byte) error { 428 // deserialize the response so we can modify it if needed. 429 var responseProto pb.SyncGetChangeProofResponse 430 require.NoError(proto.Unmarshal(responseBytes, &responseProto)) 431 432 if responseProto.GetChangeProof() != nil { 433 // Server responded with a change proof 434 var changeProof merkledb.ChangeProof 435 require.NoError(changeProof.UnmarshalProto(responseProto.GetChangeProof())) 436 437 // modify if needed 438 if modifyChangeProof != nil { 439 modifyChangeProof(&changeProof) 440 } 441 442 // reserialize the response and pass it to the client to complete the handling. 443 responseBytes, err := proto.Marshal(&pb.SyncGetChangeProofResponse{ 444 Response: &pb.SyncGetChangeProofResponse_ChangeProof{ 445 ChangeProof: changeProof.ToProto(), 446 }, 447 }) 448 require.NoError(err) 449 450 serverResponseChan <- responseBytes 451 452 return nil 453 } 454 455 // Server responded with a range proof 456 var rangeProof merkledb.RangeProof 457 require.NoError(rangeProof.UnmarshalProto(responseProto.GetRangeProof())) 458 459 // modify if needed 460 if modifyRangeProof != nil { 461 modifyRangeProof(&rangeProof) 462 } 463 464 // reserialize the response and pass it to the client to complete the handling. 465 responseBytes, err := proto.Marshal(&pb.SyncGetChangeProofResponse{ 466 Response: &pb.SyncGetChangeProofResponse_RangeProof{ 467 RangeProof: rangeProof.ToProto(), 468 }, 469 }) 470 require.NoError(err) 471 472 serverResponseChan <- responseBytes 473 474 return nil 475 }, 476 ).AnyTimes() 477 478 return client.GetChangeProof(ctx, request, clientDB) 479 } 480 481 func TestGetChangeProof(t *testing.T) { 482 now := time.Now().UnixNano() 483 t.Logf("seed: %d", now) 484 r := rand.New(rand.NewSource(now)) // #nosec G404 485 486 serverDB, err := merkledb.New( 487 context.Background(), 488 memdb.New(), 489 newDefaultDBConfig(), 490 ) 491 require.NoError(t, err) 492 493 clientDB, err := merkledb.New( 494 context.Background(), 495 memdb.New(), 496 newDefaultDBConfig(), 497 ) 498 require.NoError(t, err) 499 startRoot, err := serverDB.GetMerkleRoot(context.Background()) 500 require.NoError(t, err) 501 502 // create changes 503 for x := 0; x < defaultRequestKeyLimit/2; x++ { 504 ops := make([]database.BatchOp, 0, 11) 505 // add some key/values 506 for i := 0; i < 10; i++ { 507 key := make([]byte, r.Intn(100)) 508 _, err = r.Read(key) 509 require.NoError(t, err) 510 511 val := make([]byte, r.Intn(100)) 512 _, err = r.Read(val) 513 require.NoError(t, err) 514 515 ops = append(ops, database.BatchOp{Key: key, Value: val}) 516 } 517 518 // delete a key 519 deleteKeyStart := make([]byte, r.Intn(10)) 520 _, err = r.Read(deleteKeyStart) 521 require.NoError(t, err) 522 523 it := serverDB.NewIteratorWithStart(deleteKeyStart) 524 if it.Next() { 525 ops = append(ops, database.BatchOp{Key: it.Key(), Delete: true}) 526 } 527 require.NoError(t, it.Error()) 528 it.Release() 529 530 view, err := serverDB.NewView( 531 context.Background(), 532 merkledb.ViewChanges{BatchOps: ops}, 533 ) 534 require.NoError(t, err) 535 require.NoError(t, view.CommitToDB(context.Background())) 536 } 537 538 endRoot, err := serverDB.GetMerkleRoot(context.Background()) 539 require.NoError(t, err) 540 541 fakeRootID := ids.GenerateTestID() 542 543 tests := map[string]struct { 544 db DB 545 request *pb.SyncGetChangeProofRequest 546 modifyChangeProofResponse func(*merkledb.ChangeProof) 547 modifyRangeProofResponse func(*merkledb.RangeProof) 548 expectedErr error 549 expectedResponseLen int 550 expectRangeProof bool // Otherwise expect change proof 551 }{ 552 "proof restricted by BytesLimit": { 553 request: &pb.SyncGetChangeProofRequest{ 554 StartRootHash: startRoot[:], 555 EndRootHash: endRoot[:], 556 KeyLimit: defaultRequestKeyLimit, 557 BytesLimit: 10000, 558 }, 559 }, 560 "full response for small (single request) trie": { 561 request: &pb.SyncGetChangeProofRequest{ 562 StartRootHash: startRoot[:], 563 EndRootHash: endRoot[:], 564 KeyLimit: defaultRequestKeyLimit, 565 BytesLimit: defaultRequestByteSizeLimit, 566 }, 567 expectedResponseLen: defaultRequestKeyLimit, 568 }, 569 "too many keys in response": { 570 request: &pb.SyncGetChangeProofRequest{ 571 StartRootHash: startRoot[:], 572 EndRootHash: endRoot[:], 573 KeyLimit: defaultRequestKeyLimit, 574 BytesLimit: defaultRequestByteSizeLimit, 575 }, 576 modifyChangeProofResponse: func(response *merkledb.ChangeProof) { 577 response.KeyChanges = append(response.KeyChanges, make([]merkledb.KeyChange, defaultRequestKeyLimit)...) 578 }, 579 expectedErr: errTooManyKeys, 580 }, 581 "partial response to request for entire trie (full leaf limit)": { 582 request: &pb.SyncGetChangeProofRequest{ 583 StartRootHash: startRoot[:], 584 EndRootHash: endRoot[:], 585 KeyLimit: defaultRequestKeyLimit, 586 BytesLimit: defaultRequestByteSizeLimit, 587 }, 588 expectedResponseLen: defaultRequestKeyLimit, 589 }, 590 "removed first key in response": { 591 request: &pb.SyncGetChangeProofRequest{ 592 StartRootHash: startRoot[:], 593 EndRootHash: endRoot[:], 594 KeyLimit: defaultRequestKeyLimit, 595 BytesLimit: defaultRequestByteSizeLimit, 596 }, 597 modifyChangeProofResponse: func(response *merkledb.ChangeProof) { 598 response.KeyChanges = response.KeyChanges[1:] 599 }, 600 expectedErr: errInvalidChangeProof, 601 }, 602 "removed key from middle of response": { 603 request: &pb.SyncGetChangeProofRequest{ 604 StartRootHash: startRoot[:], 605 EndRootHash: endRoot[:], 606 KeyLimit: defaultRequestKeyLimit, 607 BytesLimit: defaultRequestByteSizeLimit, 608 }, 609 modifyChangeProofResponse: func(response *merkledb.ChangeProof) { 610 response.KeyChanges = append(response.KeyChanges[:100], response.KeyChanges[101:]...) 611 }, 612 expectedErr: merkledb.ErrInvalidProof, 613 }, 614 "all proof keys removed from response": { 615 request: &pb.SyncGetChangeProofRequest{ 616 StartRootHash: startRoot[:], 617 EndRootHash: endRoot[:], 618 KeyLimit: defaultRequestKeyLimit, 619 BytesLimit: defaultRequestByteSizeLimit, 620 }, 621 modifyChangeProofResponse: func(response *merkledb.ChangeProof) { 622 response.StartProof = nil 623 response.EndProof = nil 624 }, 625 expectedErr: merkledb.ErrInvalidProof, 626 }, 627 "range proof response; remove first key": { 628 request: &pb.SyncGetChangeProofRequest{ 629 // Server doesn't have the (non-existent) start root 630 // so should respond with range proof. 631 StartRootHash: fakeRootID[:], 632 EndRootHash: endRoot[:], 633 KeyLimit: defaultRequestKeyLimit, 634 BytesLimit: defaultRequestByteSizeLimit, 635 }, 636 modifyChangeProofResponse: nil, 637 modifyRangeProofResponse: func(response *merkledb.RangeProof) { 638 response.KeyValues = response.KeyValues[1:] 639 }, 640 expectedErr: errInvalidRangeProof, 641 expectRangeProof: true, 642 }, 643 } 644 645 for name, test := range tests { 646 t.Run(name, func(t *testing.T) { 647 require := require.New(t) 648 649 // Ensure test is well-formed. 650 if test.expectRangeProof { 651 require.Nil(test.modifyChangeProofResponse) 652 } else { 653 require.Nil(test.modifyRangeProofResponse) 654 } 655 656 changeOrRangeProof, err := sendChangeProofRequest( 657 t, 658 serverDB, 659 clientDB, 660 test.request, 661 1, 662 test.modifyChangeProofResponse, 663 test.modifyRangeProofResponse, 664 ) 665 require.ErrorIs(err, test.expectedErr) 666 if test.expectedErr != nil { 667 return 668 } 669 670 if test.expectRangeProof { 671 require.NotNil(changeOrRangeProof.RangeProof) 672 require.Nil(changeOrRangeProof.ChangeProof) 673 } else { 674 require.NotNil(changeOrRangeProof.ChangeProof) 675 require.Nil(changeOrRangeProof.RangeProof) 676 } 677 678 if test.expectedResponseLen > 0 { 679 if test.expectRangeProof { 680 require.LessOrEqual(len(changeOrRangeProof.RangeProof.KeyValues), test.expectedResponseLen) 681 } else { 682 require.LessOrEqual(len(changeOrRangeProof.ChangeProof.KeyChanges), test.expectedResponseLen) 683 } 684 } 685 686 var bytes []byte 687 if test.expectRangeProof { 688 bytes, err = proto.Marshal(&pb.SyncGetChangeProofResponse{ 689 Response: &pb.SyncGetChangeProofResponse_RangeProof{ 690 RangeProof: changeOrRangeProof.RangeProof.ToProto(), 691 }, 692 }) 693 } else { 694 bytes, err = proto.Marshal(&pb.SyncGetChangeProofResponse{ 695 Response: &pb.SyncGetChangeProofResponse_ChangeProof{ 696 ChangeProof: changeOrRangeProof.ChangeProof.ToProto(), 697 }, 698 }) 699 } 700 require.NoError(err) 701 require.LessOrEqual(len(bytes), int(test.request.BytesLimit)) 702 }) 703 } 704 } 705 706 func TestRangeProofRetries(t *testing.T) { 707 now := time.Now().UnixNano() 708 t.Logf("seed: %d", now) 709 r := rand.New(rand.NewSource(now)) // #nosec G404 710 require := require.New(t) 711 712 keyCount := defaultRequestKeyLimit 713 db, _, err := generateTrieWithMinKeyLen(t, r, keyCount, 1) 714 require.NoError(err) 715 root, err := db.GetMerkleRoot(context.Background()) 716 require.NoError(err) 717 718 maxRequests := 4 719 request := &pb.SyncGetRangeProofRequest{ 720 RootHash: root[:], 721 KeyLimit: uint32(keyCount), 722 BytesLimit: defaultRequestByteSizeLimit, 723 } 724 725 responseCount := 0 726 modifyResponse := func(response *merkledb.RangeProof) { 727 responseCount++ 728 if responseCount < maxRequests { 729 // corrupt the first [maxRequests] responses, to force the client to retry. 730 response.KeyValues = nil 731 } 732 } 733 proof, err := sendRangeProofRequest(t, db, request, maxRequests, modifyResponse) 734 require.NoError(err) 735 require.Len(proof.KeyValues, keyCount) 736 737 require.Equal(responseCount, maxRequests) // check the client performed retries. 738 } 739 740 // Test that a failure to send an AppRequest is propagated 741 // and returned by GetRangeProof and GetChangeProof. 742 func TestAppRequestSendFailed(t *testing.T) { 743 require := require.New(t) 744 ctrl := gomock.NewController(t) 745 defer ctrl.Finish() 746 747 networkClient := NewMockNetworkClient(ctrl) 748 749 client, err := NewClient( 750 &ClientConfig{ 751 NetworkClient: networkClient, 752 Log: logging.NoLog{}, 753 Metrics: &mockMetrics{}, 754 BranchFactor: merkledb.BranchFactor16, 755 }, 756 ) 757 require.NoError(err) 758 759 // Mock failure to send app request 760 networkClient.EXPECT().RequestAny( 761 gomock.Any(), 762 gomock.Any(), 763 ).Return(ids.EmptyNodeID, nil, errAppSendFailed).Times(2) 764 765 _, err = client.GetChangeProof( 766 context.Background(), 767 &pb.SyncGetChangeProofRequest{}, 768 nil, // database is unused 769 ) 770 require.ErrorIs(err, errAppSendFailed) 771 772 _, err = client.GetRangeProof( 773 context.Background(), 774 &pb.SyncGetRangeProofRequest{}, 775 ) 776 require.ErrorIs(err, errAppSendFailed) 777 }