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