github.com/ethereum/go-ethereum@v1.16.1/eth/downloader/downloader_test.go (about) 1 // Copyright 2015 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package downloader 18 19 import ( 20 "fmt" 21 "math/big" 22 "sync" 23 "sync/atomic" 24 "testing" 25 "time" 26 27 "github.com/ethereum/go-ethereum" 28 "github.com/ethereum/go-ethereum/common" 29 "github.com/ethereum/go-ethereum/consensus/ethash" 30 "github.com/ethereum/go-ethereum/core" 31 "github.com/ethereum/go-ethereum/core/rawdb" 32 "github.com/ethereum/go-ethereum/core/types" 33 "github.com/ethereum/go-ethereum/eth/protocols/eth" 34 "github.com/ethereum/go-ethereum/eth/protocols/snap" 35 "github.com/ethereum/go-ethereum/event" 36 "github.com/ethereum/go-ethereum/log" 37 "github.com/ethereum/go-ethereum/params" 38 "github.com/ethereum/go-ethereum/rlp" 39 "github.com/ethereum/go-ethereum/trie" 40 ) 41 42 // downloadTester is a test simulator for mocking out local block chain. 43 type downloadTester struct { 44 chain *core.BlockChain 45 downloader *Downloader 46 47 peers map[string]*downloadTesterPeer 48 lock sync.RWMutex 49 } 50 51 // newTester creates a new downloader test mocker. 52 func newTester(t *testing.T) *downloadTester { 53 return newTesterWithNotification(t, nil) 54 } 55 56 // newTesterWithNotification creates a new downloader test mocker. 57 func newTesterWithNotification(t *testing.T, success func()) *downloadTester { 58 db, err := rawdb.Open(rawdb.NewMemoryDatabase(), rawdb.OpenOptions{}) 59 if err != nil { 60 panic(err) 61 } 62 t.Cleanup(func() { 63 db.Close() 64 }) 65 gspec := &core.Genesis{ 66 Config: params.TestChainConfig, 67 Alloc: types.GenesisAlloc{testAddress: {Balance: big.NewInt(1000000000000000)}}, 68 BaseFee: big.NewInt(params.InitialBaseFee), 69 } 70 chain, err := core.NewBlockChain(db, gspec, ethash.NewFaker(), nil) 71 if err != nil { 72 panic(err) 73 } 74 tester := &downloadTester{ 75 chain: chain, 76 peers: make(map[string]*downloadTesterPeer), 77 } 78 tester.downloader = New(db, new(event.TypeMux), tester.chain, tester.dropPeer, success) 79 return tester 80 } 81 82 // terminate aborts any operations on the embedded downloader and releases all 83 // held resources. 84 func (dl *downloadTester) terminate() { 85 dl.downloader.Terminate() 86 dl.chain.Stop() 87 } 88 89 // newPeer registers a new block download source into the downloader. 90 func (dl *downloadTester) newPeer(id string, version uint, blocks []*types.Block) *downloadTesterPeer { 91 dl.lock.Lock() 92 defer dl.lock.Unlock() 93 94 peer := &downloadTesterPeer{ 95 dl: dl, 96 id: id, 97 chain: newTestBlockchain(blocks), 98 withholdBodies: make(map[common.Hash]struct{}), 99 } 100 dl.peers[id] = peer 101 102 if err := dl.downloader.RegisterPeer(id, version, peer); err != nil { 103 panic(err) 104 } 105 if err := dl.downloader.SnapSyncer.Register(peer); err != nil { 106 panic(err) 107 } 108 return peer 109 } 110 111 // dropPeer simulates a hard peer removal from the connection pool. 112 func (dl *downloadTester) dropPeer(id string) { 113 dl.lock.Lock() 114 defer dl.lock.Unlock() 115 116 delete(dl.peers, id) 117 dl.downloader.SnapSyncer.Unregister(id) 118 dl.downloader.UnregisterPeer(id) 119 } 120 121 type downloadTesterPeer struct { 122 dl *downloadTester 123 withholdBodies map[common.Hash]struct{} 124 id string 125 chain *core.BlockChain 126 } 127 128 func unmarshalRlpHeaders(rlpdata []rlp.RawValue) []*types.Header { 129 var headers = make([]*types.Header, len(rlpdata)) 130 for i, data := range rlpdata { 131 var h types.Header 132 if err := rlp.DecodeBytes(data, &h); err != nil { 133 panic(err) 134 } 135 headers[i] = &h 136 } 137 return headers 138 } 139 140 // RequestHeadersByHash constructs a GetBlockHeaders function based on a hashed 141 // origin; associated with a particular peer in the download tester. The returned 142 // function can be used to retrieve batches of headers from the particular peer. 143 func (dlp *downloadTesterPeer) RequestHeadersByHash(origin common.Hash, amount int, skip int, reverse bool, sink chan *eth.Response) (*eth.Request, error) { 144 // Service the header query via the live handler code 145 rlpHeaders := eth.ServiceGetBlockHeadersQuery(dlp.chain, ð.GetBlockHeadersRequest{ 146 Origin: eth.HashOrNumber{ 147 Hash: origin, 148 }, 149 Amount: uint64(amount), 150 Skip: uint64(skip), 151 Reverse: reverse, 152 }, nil) 153 headers := unmarshalRlpHeaders(rlpHeaders) 154 hashes := make([]common.Hash, len(headers)) 155 for i, header := range headers { 156 hashes[i] = header.Hash() 157 } 158 // Deliver the headers to the downloader 159 req := ð.Request{ 160 Peer: dlp.id, 161 } 162 res := ð.Response{ 163 Req: req, 164 Res: (*eth.BlockHeadersRequest)(&headers), 165 Meta: hashes, 166 Time: 1, 167 Done: make(chan error, 1), // Ignore the returned status 168 } 169 go func() { 170 sink <- res 171 }() 172 return req, nil 173 } 174 175 // RequestHeadersByNumber constructs a GetBlockHeaders function based on a numbered 176 // origin; associated with a particular peer in the download tester. The returned 177 // function can be used to retrieve batches of headers from the particular peer. 178 func (dlp *downloadTesterPeer) RequestHeadersByNumber(origin uint64, amount int, skip int, reverse bool, sink chan *eth.Response) (*eth.Request, error) { 179 // Service the header query via the live handler code 180 rlpHeaders := eth.ServiceGetBlockHeadersQuery(dlp.chain, ð.GetBlockHeadersRequest{ 181 Origin: eth.HashOrNumber{ 182 Number: origin, 183 }, 184 Amount: uint64(amount), 185 Skip: uint64(skip), 186 Reverse: reverse, 187 }, nil) 188 headers := unmarshalRlpHeaders(rlpHeaders) 189 hashes := make([]common.Hash, len(headers)) 190 for i, header := range headers { 191 hashes[i] = header.Hash() 192 } 193 // Deliver the headers to the downloader 194 req := ð.Request{ 195 Peer: dlp.id, 196 } 197 res := ð.Response{ 198 Req: req, 199 Res: (*eth.BlockHeadersRequest)(&headers), 200 Meta: hashes, 201 Time: 1, 202 Done: make(chan error, 1), // Ignore the returned status 203 } 204 go func() { 205 sink <- res 206 }() 207 return req, nil 208 } 209 210 // RequestBodies constructs a getBlockBodies method associated with a particular 211 // peer in the download tester. The returned function can be used to retrieve 212 // batches of block bodies from the particularly requested peer. 213 func (dlp *downloadTesterPeer) RequestBodies(hashes []common.Hash, sink chan *eth.Response) (*eth.Request, error) { 214 blobs := eth.ServiceGetBlockBodiesQuery(dlp.chain, hashes) 215 216 bodies := make([]*eth.BlockBody, len(blobs)) 217 for i, blob := range blobs { 218 bodies[i] = new(eth.BlockBody) 219 rlp.DecodeBytes(blob, bodies[i]) 220 } 221 var ( 222 txsHashes = make([]common.Hash, len(bodies)) 223 uncleHashes = make([]common.Hash, len(bodies)) 224 withdrawalHashes = make([]common.Hash, len(bodies)) 225 ) 226 hasher := trie.NewStackTrie(nil) 227 for i, body := range bodies { 228 hash := types.DeriveSha(types.Transactions(body.Transactions), hasher) 229 if _, ok := dlp.withholdBodies[hash]; ok { 230 txsHashes = append(txsHashes[:i], txsHashes[i+1:]...) 231 uncleHashes = append(uncleHashes[:i], uncleHashes[i+1:]...) 232 continue 233 } 234 txsHashes[i] = hash 235 uncleHashes[i] = types.CalcUncleHash(body.Uncles) 236 } 237 req := ð.Request{ 238 Peer: dlp.id, 239 } 240 res := ð.Response{ 241 Req: req, 242 Res: (*eth.BlockBodiesResponse)(&bodies), 243 Meta: [][]common.Hash{txsHashes, uncleHashes, withdrawalHashes}, 244 Time: 1, 245 Done: make(chan error, 1), // Ignore the returned status 246 } 247 go func() { 248 sink <- res 249 }() 250 return req, nil 251 } 252 253 // RequestReceipts constructs a getReceipts method associated with a particular 254 // peer in the download tester. The returned function can be used to retrieve 255 // batches of block receipts from the particularly requested peer. 256 func (dlp *downloadTesterPeer) RequestReceipts(hashes []common.Hash, sink chan *eth.Response) (*eth.Request, error) { 257 blobs := eth.ServiceGetReceiptsQuery68(dlp.chain, hashes) 258 259 receipts := make([]types.Receipts, len(blobs)) 260 for i, blob := range blobs { 261 rlp.DecodeBytes(blob, &receipts[i]) 262 } 263 hasher := trie.NewStackTrie(nil) 264 hashes = make([]common.Hash, len(receipts)) 265 for i, receipt := range receipts { 266 hashes[i] = types.DeriveSha(receipt, hasher) 267 } 268 req := ð.Request{ 269 Peer: dlp.id, 270 } 271 resp := eth.ReceiptsRLPResponse(types.EncodeBlockReceiptLists(receipts)) 272 res := ð.Response{ 273 Req: req, 274 Res: &resp, 275 Meta: hashes, 276 Time: 1, 277 Done: make(chan error, 1), // Ignore the returned status 278 } 279 go func() { 280 sink <- res 281 }() 282 return req, nil 283 } 284 285 // ID retrieves the peer's unique identifier. 286 func (dlp *downloadTesterPeer) ID() string { 287 return dlp.id 288 } 289 290 // RequestAccountRange fetches a batch of accounts rooted in a specific account 291 // trie, starting with the origin. 292 func (dlp *downloadTesterPeer) RequestAccountRange(id uint64, root, origin, limit common.Hash, bytes uint64) error { 293 // Create the request and service it 294 req := &snap.GetAccountRangePacket{ 295 ID: id, 296 Root: root, 297 Origin: origin, 298 Limit: limit, 299 Bytes: bytes, 300 } 301 slimaccs, proofs := snap.ServiceGetAccountRangeQuery(dlp.chain, req) 302 303 // We need to convert to non-slim format, delegate to the packet code 304 res := &snap.AccountRangePacket{ 305 ID: id, 306 Accounts: slimaccs, 307 Proof: proofs, 308 } 309 hashes, accounts, _ := res.Unpack() 310 311 go dlp.dl.downloader.SnapSyncer.OnAccounts(dlp, id, hashes, accounts, proofs) 312 return nil 313 } 314 315 // RequestStorageRanges fetches a batch of storage slots belonging to one or 316 // more accounts. If slots from only one account is requested, an origin marker 317 // may also be used to retrieve from there. 318 func (dlp *downloadTesterPeer) RequestStorageRanges(id uint64, root common.Hash, accounts []common.Hash, origin, limit []byte, bytes uint64) error { 319 // Create the request and service it 320 req := &snap.GetStorageRangesPacket{ 321 ID: id, 322 Accounts: accounts, 323 Root: root, 324 Origin: origin, 325 Limit: limit, 326 Bytes: bytes, 327 } 328 storage, proofs := snap.ServiceGetStorageRangesQuery(dlp.chain, req) 329 330 // We need to convert to demultiplex, delegate to the packet code 331 res := &snap.StorageRangesPacket{ 332 ID: id, 333 Slots: storage, 334 Proof: proofs, 335 } 336 hashes, slots := res.Unpack() 337 338 go dlp.dl.downloader.SnapSyncer.OnStorage(dlp, id, hashes, slots, proofs) 339 return nil 340 } 341 342 // RequestByteCodes fetches a batch of bytecodes by hash. 343 func (dlp *downloadTesterPeer) RequestByteCodes(id uint64, hashes []common.Hash, bytes uint64) error { 344 req := &snap.GetByteCodesPacket{ 345 ID: id, 346 Hashes: hashes, 347 Bytes: bytes, 348 } 349 codes := snap.ServiceGetByteCodesQuery(dlp.chain, req) 350 go dlp.dl.downloader.SnapSyncer.OnByteCodes(dlp, id, codes) 351 return nil 352 } 353 354 // RequestTrieNodes fetches a batch of account or storage trie nodes rooted in 355 // a specific state trie. 356 func (dlp *downloadTesterPeer) RequestTrieNodes(id uint64, root common.Hash, paths []snap.TrieNodePathSet, bytes uint64) error { 357 req := &snap.GetTrieNodesPacket{ 358 ID: id, 359 Root: root, 360 Paths: paths, 361 Bytes: bytes, 362 } 363 nodes, _ := snap.ServiceGetTrieNodesQuery(dlp.chain, req, time.Now()) 364 go dlp.dl.downloader.SnapSyncer.OnTrieNodes(dlp, id, nodes) 365 return nil 366 } 367 368 // Log retrieves the peer's own contextual logger. 369 func (dlp *downloadTesterPeer) Log() log.Logger { 370 return log.New("peer", dlp.id) 371 } 372 373 // assertOwnChain checks if the local chain contains the correct number of items 374 // of the various chain components. 375 func assertOwnChain(t *testing.T, tester *downloadTester, length int) { 376 // Mark this method as a helper to report errors at callsite, not in here 377 t.Helper() 378 379 headers, blocks, receipts := length, length, length 380 if hs := int(tester.chain.CurrentHeader().Number.Uint64()) + 1; hs != headers { 381 t.Fatalf("synchronised headers mismatch: have %v, want %v", hs, headers) 382 } 383 if bs := int(tester.chain.CurrentBlock().Number.Uint64()) + 1; bs != blocks { 384 t.Fatalf("synchronised blocks mismatch: have %v, want %v", bs, blocks) 385 } 386 if rs := int(tester.chain.CurrentSnapBlock().Number.Uint64()) + 1; rs != receipts { 387 t.Fatalf("synchronised receipts mismatch: have %v, want %v", rs, receipts) 388 } 389 } 390 391 func TestCanonicalSynchronisation68Full(t *testing.T) { testCanonSync(t, eth.ETH68, FullSync) } 392 func TestCanonicalSynchronisation68Snap(t *testing.T) { testCanonSync(t, eth.ETH68, SnapSync) } 393 394 func testCanonSync(t *testing.T, protocol uint, mode SyncMode) { 395 success := make(chan struct{}) 396 tester := newTesterWithNotification(t, func() { 397 close(success) 398 }) 399 defer tester.terminate() 400 401 // Create a small enough block chain to download 402 chain := testChainBase.shorten(blockCacheMaxItems - 15) 403 tester.newPeer("peer", protocol, chain.blocks[1:]) 404 405 // Synchronise with the peer and make sure all relevant data was retrieved 406 if err := tester.downloader.BeaconSync(mode, chain.blocks[len(chain.blocks)-1].Header(), nil); err != nil { 407 t.Fatalf("failed to beacon-sync chain: %v", err) 408 } 409 select { 410 case <-success: 411 assertOwnChain(t, tester, len(chain.blocks)) 412 case <-time.NewTimer(time.Second * 3).C: 413 t.Fatalf("Failed to sync chain in three seconds") 414 } 415 } 416 417 // Tests that if a large batch of blocks are being downloaded, it is throttled 418 // until the cached blocks are retrieved. 419 func TestThrottling68Full(t *testing.T) { testThrottling(t, eth.ETH68, FullSync) } 420 func TestThrottling68Snap(t *testing.T) { testThrottling(t, eth.ETH68, SnapSync) } 421 422 func testThrottling(t *testing.T, protocol uint, mode SyncMode) { 423 tester := newTester(t) 424 defer tester.terminate() 425 426 // Create a long block chain to download and the tester 427 targetBlocks := len(testChainBase.blocks) - 1 428 tester.newPeer("peer", protocol, testChainBase.blocks[1:]) 429 430 // Wrap the importer to allow stepping 431 var blocked atomic.Uint32 432 proceed := make(chan struct{}) 433 tester.downloader.chainInsertHook = func(results []*fetchResult) { 434 blocked.Store(uint32(len(results))) 435 <-proceed 436 } 437 // Start a synchronisation concurrently 438 errc := make(chan error, 1) 439 go func() { 440 errc <- tester.downloader.BeaconSync(mode, testChainBase.blocks[len(testChainBase.blocks)-1].Header(), nil) 441 }() 442 // Iteratively take some blocks, always checking the retrieval count 443 for { 444 // Check the retrieval count synchronously (! reason for this ugly block) 445 tester.lock.RLock() 446 retrieved := int(tester.chain.CurrentSnapBlock().Number.Uint64()) + 1 447 tester.lock.RUnlock() 448 if retrieved >= targetBlocks+1 { 449 break 450 } 451 // Wait a bit for sync to throttle itself 452 var cached, frozen int 453 for start := time.Now(); time.Since(start) < 3*time.Second; { 454 time.Sleep(25 * time.Millisecond) 455 456 tester.lock.Lock() 457 tester.downloader.queue.lock.Lock() 458 tester.downloader.queue.resultCache.lock.Lock() 459 { 460 cached = tester.downloader.queue.resultCache.countCompleted() 461 frozen = int(blocked.Load()) 462 retrieved = int(tester.chain.CurrentSnapBlock().Number.Uint64()) + 1 463 } 464 tester.downloader.queue.resultCache.lock.Unlock() 465 tester.downloader.queue.lock.Unlock() 466 tester.lock.Unlock() 467 468 if cached == blockCacheMaxItems || 469 cached == blockCacheMaxItems-reorgProtHeaderDelay || 470 retrieved+cached+frozen == targetBlocks+1 || 471 retrieved+cached+frozen == targetBlocks+1-reorgProtHeaderDelay { 472 break 473 } 474 } 475 // Make sure we filled up the cache, then exhaust it 476 time.Sleep(25 * time.Millisecond) // give it a chance to screw up 477 tester.lock.RLock() 478 retrieved = int(tester.chain.CurrentSnapBlock().Number.Uint64()) + 1 479 tester.lock.RUnlock() 480 if cached != blockCacheMaxItems && cached != blockCacheMaxItems-reorgProtHeaderDelay && retrieved+cached+frozen != targetBlocks+1 && retrieved+cached+frozen != targetBlocks+1-reorgProtHeaderDelay { 481 t.Fatalf("block count mismatch: have %v, want %v (owned %v, blocked %v, target %v)", cached, blockCacheMaxItems, retrieved, frozen, targetBlocks+1) 482 } 483 // Permit the blocked blocks to import 484 if blocked.Load() > 0 { 485 blocked.Store(uint32(0)) 486 proceed <- struct{}{} 487 } 488 } 489 // Check that we haven't pulled more blocks than available 490 assertOwnChain(t, tester, targetBlocks+1) 491 if err := <-errc; err != nil { 492 t.Fatalf("block synchronization failed: %v", err) 493 } 494 } 495 496 // Tests that a canceled download wipes all previously accumulated state. 497 func TestCancel68Full(t *testing.T) { testCancel(t, eth.ETH68, FullSync) } 498 func TestCancel68Snap(t *testing.T) { testCancel(t, eth.ETH68, SnapSync) } 499 500 func testCancel(t *testing.T, protocol uint, mode SyncMode) { 501 complete := make(chan struct{}) 502 success := func() { 503 close(complete) 504 } 505 tester := newTesterWithNotification(t, success) 506 defer tester.terminate() 507 508 chain := testChainBase.shorten(MaxHeaderFetch) 509 tester.newPeer("peer", protocol, chain.blocks[1:]) 510 511 // Make sure canceling works with a pristine downloader 512 tester.downloader.Cancel() 513 if !tester.downloader.queue.Idle() { 514 t.Errorf("download queue not idle") 515 } 516 // Synchronise with the peer, but cancel afterwards 517 if err := tester.downloader.BeaconSync(mode, chain.blocks[len(chain.blocks)-1].Header(), nil); err != nil { 518 t.Fatalf("failed to synchronise blocks: %v", err) 519 } 520 <-complete 521 tester.downloader.Cancel() 522 if !tester.downloader.queue.Idle() { 523 t.Errorf("download queue not idle") 524 } 525 } 526 527 // Tests that synchronisations behave well in multi-version protocol environments 528 // and not wreak havoc on other nodes in the network. 529 func TestMultiProtoSynchronisation68Full(t *testing.T) { testMultiProtoSync(t, eth.ETH68, FullSync) } 530 func TestMultiProtoSynchronisation68Snap(t *testing.T) { testMultiProtoSync(t, eth.ETH68, SnapSync) } 531 532 func testMultiProtoSync(t *testing.T, protocol uint, mode SyncMode) { 533 complete := make(chan struct{}) 534 success := func() { 535 close(complete) 536 } 537 tester := newTesterWithNotification(t, success) 538 defer tester.terminate() 539 540 // Create a small enough block chain to download 541 chain := testChainBase.shorten(blockCacheMaxItems - 15) 542 543 // Create peers of every type 544 tester.newPeer("peer 68", eth.ETH68, chain.blocks[1:]) 545 546 if err := tester.downloader.BeaconSync(mode, chain.blocks[len(chain.blocks)-1].Header(), nil); err != nil { 547 t.Fatalf("failed to start beacon sync: #{err}") 548 } 549 select { 550 case <-complete: 551 break 552 case <-time.NewTimer(time.Second * 3).C: 553 t.Fatalf("Failed to sync chain in three seconds") 554 } 555 assertOwnChain(t, tester, len(chain.blocks)) 556 557 // Check that no peers have been dropped off 558 for _, version := range []int{68} { 559 peer := fmt.Sprintf("peer %d", version) 560 if _, ok := tester.peers[peer]; !ok { 561 t.Errorf("%s dropped", peer) 562 } 563 } 564 } 565 566 // Tests that if a block is empty (e.g. header only), no body request should be 567 // made, and instead the header should be assembled into a whole block in itself. 568 func TestEmptyShortCircuit68Full(t *testing.T) { testEmptyShortCircuit(t, eth.ETH68, FullSync) } 569 func TestEmptyShortCircuit68Snap(t *testing.T) { testEmptyShortCircuit(t, eth.ETH68, SnapSync) } 570 571 func testEmptyShortCircuit(t *testing.T, protocol uint, mode SyncMode) { 572 success := make(chan struct{}) 573 tester := newTesterWithNotification(t, func() { 574 close(success) 575 }) 576 defer tester.terminate() 577 578 // Create a block chain to download 579 chain := testChainBase 580 tester.newPeer("peer", protocol, chain.blocks[1:]) 581 582 // Instrument the downloader to signal body requests 583 var bodiesHave, receiptsHave atomic.Int32 584 tester.downloader.bodyFetchHook = func(headers []*types.Header) { 585 bodiesHave.Add(int32(len(headers))) 586 } 587 tester.downloader.receiptFetchHook = func(headers []*types.Header) { 588 receiptsHave.Add(int32(len(headers))) 589 } 590 591 if err := tester.downloader.BeaconSync(mode, chain.blocks[len(chain.blocks)-1].Header(), nil); err != nil { 592 t.Fatalf("failed to synchronise blocks: %v", err) 593 } 594 select { 595 case <-success: 596 checkProgress(t, tester.downloader, "initial", ethereum.SyncProgress{ 597 HighestBlock: uint64(len(chain.blocks) - 1), 598 CurrentBlock: uint64(len(chain.blocks) - 1), 599 }) 600 case <-time.NewTimer(time.Second * 3).C: 601 t.Fatalf("Failed to sync chain in three seconds") 602 } 603 assertOwnChain(t, tester, len(chain.blocks)) 604 605 // Validate the number of block bodies that should have been requested 606 bodiesNeeded, receiptsNeeded := 0, 0 607 for _, block := range chain.blocks[1:] { 608 if len(block.Transactions()) > 0 || len(block.Uncles()) > 0 { 609 bodiesNeeded++ 610 } 611 } 612 for _, block := range chain.blocks[1:] { 613 if mode == SnapSync && len(block.Transactions()) > 0 { 614 receiptsNeeded++ 615 } 616 } 617 if int(bodiesHave.Load()) != bodiesNeeded { 618 t.Errorf("body retrieval count mismatch: have %v, want %v", bodiesHave.Load(), bodiesNeeded) 619 } 620 if int(receiptsHave.Load()) != receiptsNeeded { 621 t.Errorf("receipt retrieval count mismatch: have %v, want %v", receiptsHave.Load(), receiptsNeeded) 622 } 623 } 624 625 func checkProgress(t *testing.T, d *Downloader, stage string, want ethereum.SyncProgress) { 626 // Mark this method as a helper to report errors at callsite, not in here 627 t.Helper() 628 629 p := d.Progress() 630 if p.StartingBlock != want.StartingBlock || p.CurrentBlock != want.CurrentBlock || p.HighestBlock != want.HighestBlock { 631 t.Fatalf("%s progress mismatch:\nhave %+v\nwant %+v", stage, p, want) 632 } 633 } 634 635 // Tests that peers below a pre-configured checkpoint block are prevented from 636 // being fast-synced from, avoiding potential cheap eclipse attacks. 637 func TestBeaconSync68Full(t *testing.T) { testBeaconSync(t, eth.ETH68, FullSync) } 638 func TestBeaconSync68Snap(t *testing.T) { testBeaconSync(t, eth.ETH68, SnapSync) } 639 640 func testBeaconSync(t *testing.T, protocol uint, mode SyncMode) { 641 var cases = []struct { 642 name string // The name of testing scenario 643 local int // The length of local chain(canonical chain assumed), 0 means genesis is the head 644 }{ 645 {name: "Beacon sync since genesis", local: 0}, 646 {name: "Beacon sync with short local chain", local: 1}, 647 {name: "Beacon sync with long local chain", local: blockCacheMaxItems - 15 - fsMinFullBlocks/2}, 648 {name: "Beacon sync with full local chain", local: blockCacheMaxItems - 15 - 1}, 649 } 650 for _, c := range cases { 651 t.Run(c.name, func(t *testing.T) { 652 success := make(chan struct{}) 653 tester := newTesterWithNotification(t, func() { 654 close(success) 655 }) 656 defer tester.terminate() 657 658 chain := testChainBase.shorten(blockCacheMaxItems - 15) 659 tester.newPeer("peer", protocol, chain.blocks[1:]) 660 661 // Build the local chain segment if it's required 662 if c.local > 0 { 663 tester.chain.InsertChain(chain.blocks[1 : c.local+1]) 664 } 665 if err := tester.downloader.BeaconSync(mode, chain.blocks[len(chain.blocks)-1].Header(), nil); err != nil { 666 t.Fatalf("Failed to beacon sync chain %v %v", c.name, err) 667 } 668 select { 669 case <-success: 670 // Ok, downloader fully cancelled after sync cycle 671 if bs := int(tester.chain.CurrentBlock().Number.Uint64()) + 1; bs != len(chain.blocks) { 672 t.Fatalf("synchronised blocks mismatch: have %v, want %v", bs, len(chain.blocks)) 673 } 674 case <-time.NewTimer(time.Second * 3).C: 675 t.Fatalf("Failed to sync chain in three seconds") 676 } 677 }) 678 } 679 } 680 681 // Tests that synchronisation progress (origin block number, current block number 682 // and highest block number) is tracked and updated correctly. 683 func TestSyncProgress68Full(t *testing.T) { testSyncProgress(t, eth.ETH68, FullSync) } 684 func TestSyncProgress68Snap(t *testing.T) { testSyncProgress(t, eth.ETH68, SnapSync) } 685 686 func testSyncProgress(t *testing.T, protocol uint, mode SyncMode) { 687 success := make(chan struct{}) 688 tester := newTesterWithNotification(t, func() { 689 success <- struct{}{} 690 }) 691 defer tester.terminate() 692 checkProgress(t, tester.downloader, "pristine", ethereum.SyncProgress{}) 693 694 chain := testChainBase.shorten(blockCacheMaxItems - 15) 695 shortChain := chain.shorten(len(chain.blocks) / 2).blocks[1:] 696 697 // Connect to peer that provides all headers and part of the bodies 698 faultyPeer := tester.newPeer("peer-half", protocol, shortChain) 699 for _, header := range shortChain { 700 faultyPeer.withholdBodies[header.Hash()] = struct{}{} 701 } 702 703 if err := tester.downloader.BeaconSync(mode, chain.blocks[len(chain.blocks)/2-1].Header(), nil); err != nil { 704 t.Fatalf("failed to beacon-sync chain: %v", err) 705 } 706 select { 707 case <-success: 708 // Ok, downloader fully cancelled after sync cycle 709 checkProgress(t, tester.downloader, "peer-half", ethereum.SyncProgress{ 710 CurrentBlock: uint64(len(chain.blocks)/2 - 1), 711 HighestBlock: uint64(len(chain.blocks)/2 - 1), 712 }) 713 case <-time.NewTimer(time.Second * 3).C: 714 t.Fatalf("Failed to sync chain in three seconds") 715 } 716 717 // Synchronise all the blocks and check continuation progress 718 tester.newPeer("peer-full", protocol, chain.blocks[1:]) 719 if err := tester.downloader.BeaconSync(mode, chain.blocks[len(chain.blocks)-1].Header(), nil); err != nil { 720 t.Fatalf("failed to beacon-sync chain: %v", err) 721 } 722 startingBlock := uint64(len(chain.blocks)/2 - 1) 723 724 select { 725 case <-success: 726 // Ok, downloader fully cancelled after sync cycle 727 checkProgress(t, tester.downloader, "peer-full", ethereum.SyncProgress{ 728 StartingBlock: startingBlock, 729 CurrentBlock: uint64(len(chain.blocks) - 1), 730 HighestBlock: uint64(len(chain.blocks) - 1), 731 }) 732 case <-time.NewTimer(time.Second * 3).C: 733 t.Fatalf("Failed to sync chain in three seconds") 734 } 735 }