github.com/LampardNguyen234/go-ethereum@v1.10.16-0.20220117140830-b6a3b0260724/eth/fetcher/block_fetcher_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 fetcher 18 19 import ( 20 "errors" 21 "math/big" 22 "sync" 23 "sync/atomic" 24 "testing" 25 "time" 26 27 "github.com/LampardNguyen234/go-ethereum/common" 28 "github.com/LampardNguyen234/go-ethereum/consensus/ethash" 29 "github.com/LampardNguyen234/go-ethereum/core" 30 "github.com/LampardNguyen234/go-ethereum/core/rawdb" 31 "github.com/LampardNguyen234/go-ethereum/core/types" 32 "github.com/LampardNguyen234/go-ethereum/crypto" 33 "github.com/LampardNguyen234/go-ethereum/eth/protocols/eth" 34 "github.com/LampardNguyen234/go-ethereum/params" 35 "github.com/LampardNguyen234/go-ethereum/trie" 36 ) 37 38 var ( 39 testdb = rawdb.NewMemoryDatabase() 40 testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") 41 testAddress = crypto.PubkeyToAddress(testKey.PublicKey) 42 genesis = core.GenesisBlockForTesting(testdb, testAddress, big.NewInt(1000000000000000)) 43 unknownBlock = types.NewBlock(&types.Header{GasLimit: params.GenesisGasLimit, BaseFee: big.NewInt(params.InitialBaseFee)}, nil, nil, nil, trie.NewStackTrie(nil)) 44 ) 45 46 // makeChain creates a chain of n blocks starting at and including parent. 47 // the returned hash chain is ordered head->parent. In addition, every 3rd block 48 // contains a transaction and every 5th an uncle to allow testing correct block 49 // reassembly. 50 func makeChain(n int, seed byte, parent *types.Block) ([]common.Hash, map[common.Hash]*types.Block) { 51 blocks, _ := core.GenerateChain(params.TestChainConfig, parent, ethash.NewFaker(), testdb, n, func(i int, block *core.BlockGen) { 52 block.SetCoinbase(common.Address{seed}) 53 54 // If the block number is multiple of 3, send a bonus transaction to the miner 55 if parent == genesis && i%3 == 0 { 56 signer := types.MakeSigner(params.TestChainConfig, block.Number()) 57 tx, err := types.SignTx(types.NewTransaction(block.TxNonce(testAddress), common.Address{seed}, big.NewInt(1000), params.TxGas, block.BaseFee(), nil), signer, testKey) 58 if err != nil { 59 panic(err) 60 } 61 block.AddTx(tx) 62 } 63 // If the block number is a multiple of 5, add a bonus uncle to the block 64 if i > 0 && i%5 == 0 { 65 block.AddUncle(&types.Header{ParentHash: block.PrevBlock(i - 2).Hash(), Number: big.NewInt(int64(i - 1))}) 66 } 67 }) 68 hashes := make([]common.Hash, n+1) 69 hashes[len(hashes)-1] = parent.Hash() 70 blockm := make(map[common.Hash]*types.Block, n+1) 71 blockm[parent.Hash()] = parent 72 for i, b := range blocks { 73 hashes[len(hashes)-i-2] = b.Hash() 74 blockm[b.Hash()] = b 75 } 76 return hashes, blockm 77 } 78 79 // fetcherTester is a test simulator for mocking out local block chain. 80 type fetcherTester struct { 81 fetcher *BlockFetcher 82 83 hashes []common.Hash // Hash chain belonging to the tester 84 headers map[common.Hash]*types.Header // Headers belonging to the tester 85 blocks map[common.Hash]*types.Block // Blocks belonging to the tester 86 drops map[string]bool // Map of peers dropped by the fetcher 87 88 lock sync.RWMutex 89 } 90 91 // newTester creates a new fetcher test mocker. 92 func newTester(light bool) *fetcherTester { 93 tester := &fetcherTester{ 94 hashes: []common.Hash{genesis.Hash()}, 95 headers: map[common.Hash]*types.Header{genesis.Hash(): genesis.Header()}, 96 blocks: map[common.Hash]*types.Block{genesis.Hash(): genesis}, 97 drops: make(map[string]bool), 98 } 99 tester.fetcher = NewBlockFetcher(light, tester.getHeader, tester.getBlock, tester.verifyHeader, tester.broadcastBlock, tester.chainHeight, tester.insertHeaders, tester.insertChain, tester.dropPeer) 100 tester.fetcher.Start() 101 102 return tester 103 } 104 105 // getHeader retrieves a header from the tester's block chain. 106 func (f *fetcherTester) getHeader(hash common.Hash) *types.Header { 107 f.lock.RLock() 108 defer f.lock.RUnlock() 109 110 return f.headers[hash] 111 } 112 113 // getBlock retrieves a block from the tester's block chain. 114 func (f *fetcherTester) getBlock(hash common.Hash) *types.Block { 115 f.lock.RLock() 116 defer f.lock.RUnlock() 117 118 return f.blocks[hash] 119 } 120 121 // verifyHeader is a nop placeholder for the block header verification. 122 func (f *fetcherTester) verifyHeader(header *types.Header) error { 123 return nil 124 } 125 126 // broadcastBlock is a nop placeholder for the block broadcasting. 127 func (f *fetcherTester) broadcastBlock(block *types.Block, propagate bool) { 128 } 129 130 // chainHeight retrieves the current height (block number) of the chain. 131 func (f *fetcherTester) chainHeight() uint64 { 132 f.lock.RLock() 133 defer f.lock.RUnlock() 134 135 if f.fetcher.light { 136 return f.headers[f.hashes[len(f.hashes)-1]].Number.Uint64() 137 } 138 return f.blocks[f.hashes[len(f.hashes)-1]].NumberU64() 139 } 140 141 // insertChain injects a new headers into the simulated chain. 142 func (f *fetcherTester) insertHeaders(headers []*types.Header) (int, error) { 143 f.lock.Lock() 144 defer f.lock.Unlock() 145 146 for i, header := range headers { 147 // Make sure the parent in known 148 if _, ok := f.headers[header.ParentHash]; !ok { 149 return i, errors.New("unknown parent") 150 } 151 // Discard any new blocks if the same height already exists 152 if header.Number.Uint64() <= f.headers[f.hashes[len(f.hashes)-1]].Number.Uint64() { 153 return i, nil 154 } 155 // Otherwise build our current chain 156 f.hashes = append(f.hashes, header.Hash()) 157 f.headers[header.Hash()] = header 158 } 159 return 0, nil 160 } 161 162 // insertChain injects a new blocks into the simulated chain. 163 func (f *fetcherTester) insertChain(blocks types.Blocks) (int, error) { 164 f.lock.Lock() 165 defer f.lock.Unlock() 166 167 for i, block := range blocks { 168 // Make sure the parent in known 169 if _, ok := f.blocks[block.ParentHash()]; !ok { 170 return i, errors.New("unknown parent") 171 } 172 // Discard any new blocks if the same height already exists 173 if block.NumberU64() <= f.blocks[f.hashes[len(f.hashes)-1]].NumberU64() { 174 return i, nil 175 } 176 // Otherwise build our current chain 177 f.hashes = append(f.hashes, block.Hash()) 178 f.blocks[block.Hash()] = block 179 } 180 return 0, nil 181 } 182 183 // dropPeer is an emulator for the peer removal, simply accumulating the various 184 // peers dropped by the fetcher. 185 func (f *fetcherTester) dropPeer(peer string) { 186 f.lock.Lock() 187 defer f.lock.Unlock() 188 189 f.drops[peer] = true 190 } 191 192 // makeHeaderFetcher retrieves a block header fetcher associated with a simulated peer. 193 func (f *fetcherTester) makeHeaderFetcher(peer string, blocks map[common.Hash]*types.Block, drift time.Duration) headerRequesterFn { 194 closure := make(map[common.Hash]*types.Block) 195 for hash, block := range blocks { 196 closure[hash] = block 197 } 198 // Create a function that return a header from the closure 199 return func(hash common.Hash, sink chan *eth.Response) (*eth.Request, error) { 200 // Gather the blocks to return 201 headers := make([]*types.Header, 0, 1) 202 if block, ok := closure[hash]; ok { 203 headers = append(headers, block.Header()) 204 } 205 // Return on a new thread 206 req := ð.Request{ 207 Peer: peer, 208 } 209 res := ð.Response{ 210 Req: req, 211 Res: (*eth.BlockHeadersPacket)(&headers), 212 Time: drift, 213 Done: make(chan error, 1), // Ignore the returned status 214 } 215 go func() { 216 sink <- res 217 }() 218 return req, nil 219 } 220 } 221 222 // makeBodyFetcher retrieves a block body fetcher associated with a simulated peer. 223 func (f *fetcherTester) makeBodyFetcher(peer string, blocks map[common.Hash]*types.Block, drift time.Duration) bodyRequesterFn { 224 closure := make(map[common.Hash]*types.Block) 225 for hash, block := range blocks { 226 closure[hash] = block 227 } 228 // Create a function that returns blocks from the closure 229 return func(hashes []common.Hash, sink chan *eth.Response) (*eth.Request, error) { 230 // Gather the block bodies to return 231 transactions := make([][]*types.Transaction, 0, len(hashes)) 232 uncles := make([][]*types.Header, 0, len(hashes)) 233 234 for _, hash := range hashes { 235 if block, ok := closure[hash]; ok { 236 transactions = append(transactions, block.Transactions()) 237 uncles = append(uncles, block.Uncles()) 238 } 239 } 240 // Return on a new thread 241 bodies := make([]*eth.BlockBody, len(transactions)) 242 for i, txs := range transactions { 243 bodies[i] = ð.BlockBody{ 244 Transactions: txs, 245 Uncles: uncles[i], 246 } 247 } 248 req := ð.Request{ 249 Peer: peer, 250 } 251 res := ð.Response{ 252 Req: req, 253 Res: (*eth.BlockBodiesPacket)(&bodies), 254 Time: drift, 255 Done: make(chan error, 1), // Ignore the returned status 256 } 257 go func() { 258 sink <- res 259 }() 260 return req, nil 261 } 262 } 263 264 // verifyFetchingEvent verifies that one single event arrive on a fetching channel. 265 func verifyFetchingEvent(t *testing.T, fetching chan []common.Hash, arrive bool) { 266 t.Helper() 267 268 if arrive { 269 select { 270 case <-fetching: 271 case <-time.After(time.Second): 272 t.Fatalf("fetching timeout") 273 } 274 } else { 275 select { 276 case <-fetching: 277 t.Fatalf("fetching invoked") 278 case <-time.After(10 * time.Millisecond): 279 } 280 } 281 } 282 283 // verifyCompletingEvent verifies that one single event arrive on an completing channel. 284 func verifyCompletingEvent(t *testing.T, completing chan []common.Hash, arrive bool) { 285 t.Helper() 286 287 if arrive { 288 select { 289 case <-completing: 290 case <-time.After(time.Second): 291 t.Fatalf("completing timeout") 292 } 293 } else { 294 select { 295 case <-completing: 296 t.Fatalf("completing invoked") 297 case <-time.After(10 * time.Millisecond): 298 } 299 } 300 } 301 302 // verifyImportEvent verifies that one single event arrive on an import channel. 303 func verifyImportEvent(t *testing.T, imported chan interface{}, arrive bool) { 304 t.Helper() 305 306 if arrive { 307 select { 308 case <-imported: 309 case <-time.After(time.Second): 310 t.Fatalf("import timeout") 311 } 312 } else { 313 select { 314 case <-imported: 315 t.Fatalf("import invoked") 316 case <-time.After(20 * time.Millisecond): 317 } 318 } 319 } 320 321 // verifyImportCount verifies that exactly count number of events arrive on an 322 // import hook channel. 323 func verifyImportCount(t *testing.T, imported chan interface{}, count int) { 324 t.Helper() 325 326 for i := 0; i < count; i++ { 327 select { 328 case <-imported: 329 case <-time.After(time.Second): 330 t.Fatalf("block %d: import timeout", i+1) 331 } 332 } 333 verifyImportDone(t, imported) 334 } 335 336 // verifyImportDone verifies that no more events are arriving on an import channel. 337 func verifyImportDone(t *testing.T, imported chan interface{}) { 338 t.Helper() 339 340 select { 341 case <-imported: 342 t.Fatalf("extra block imported") 343 case <-time.After(50 * time.Millisecond): 344 } 345 } 346 347 // verifyChainHeight verifies the chain height is as expected. 348 func verifyChainHeight(t *testing.T, fetcher *fetcherTester, height uint64) { 349 t.Helper() 350 351 if fetcher.chainHeight() != height { 352 t.Fatalf("chain height mismatch, got %d, want %d", fetcher.chainHeight(), height) 353 } 354 } 355 356 // Tests that a fetcher accepts block/header announcements and initiates retrievals 357 // for them, successfully importing into the local chain. 358 func TestFullSequentialAnnouncements(t *testing.T) { testSequentialAnnouncements(t, false) } 359 func TestLightSequentialAnnouncements(t *testing.T) { testSequentialAnnouncements(t, true) } 360 361 func testSequentialAnnouncements(t *testing.T, light bool) { 362 // Create a chain of blocks to import 363 targetBlocks := 4 * hashLimit 364 hashes, blocks := makeChain(targetBlocks, 0, genesis) 365 366 tester := newTester(light) 367 headerFetcher := tester.makeHeaderFetcher("valid", blocks, -gatherSlack) 368 bodyFetcher := tester.makeBodyFetcher("valid", blocks, 0) 369 370 // Iteratively announce blocks until all are imported 371 imported := make(chan interface{}) 372 tester.fetcher.importedHook = func(header *types.Header, block *types.Block) { 373 if light { 374 if header == nil { 375 t.Fatalf("Fetcher try to import empty header") 376 } 377 imported <- header 378 } else { 379 if block == nil { 380 t.Fatalf("Fetcher try to import empty block") 381 } 382 imported <- block 383 } 384 } 385 for i := len(hashes) - 2; i >= 0; i-- { 386 tester.fetcher.Notify("valid", hashes[i], uint64(len(hashes)-i-1), time.Now().Add(-arriveTimeout), headerFetcher, bodyFetcher) 387 verifyImportEvent(t, imported, true) 388 } 389 verifyImportDone(t, imported) 390 verifyChainHeight(t, tester, uint64(len(hashes)-1)) 391 } 392 393 // Tests that if blocks are announced by multiple peers (or even the same buggy 394 // peer), they will only get downloaded at most once. 395 func TestFullConcurrentAnnouncements(t *testing.T) { testConcurrentAnnouncements(t, false) } 396 func TestLightConcurrentAnnouncements(t *testing.T) { testConcurrentAnnouncements(t, true) } 397 398 func testConcurrentAnnouncements(t *testing.T, light bool) { 399 // Create a chain of blocks to import 400 targetBlocks := 4 * hashLimit 401 hashes, blocks := makeChain(targetBlocks, 0, genesis) 402 403 // Assemble a tester with a built in counter for the requests 404 tester := newTester(light) 405 firstHeaderFetcher := tester.makeHeaderFetcher("first", blocks, -gatherSlack) 406 firstBodyFetcher := tester.makeBodyFetcher("first", blocks, 0) 407 secondHeaderFetcher := tester.makeHeaderFetcher("second", blocks, -gatherSlack) 408 secondBodyFetcher := tester.makeBodyFetcher("second", blocks, 0) 409 410 counter := uint32(0) 411 firstHeaderWrapper := func(hash common.Hash, sink chan *eth.Response) (*eth.Request, error) { 412 atomic.AddUint32(&counter, 1) 413 return firstHeaderFetcher(hash, sink) 414 } 415 secondHeaderWrapper := func(hash common.Hash, sink chan *eth.Response) (*eth.Request, error) { 416 atomic.AddUint32(&counter, 1) 417 return secondHeaderFetcher(hash, sink) 418 } 419 // Iteratively announce blocks until all are imported 420 imported := make(chan interface{}) 421 tester.fetcher.importedHook = func(header *types.Header, block *types.Block) { 422 if light { 423 if header == nil { 424 t.Fatalf("Fetcher try to import empty header") 425 } 426 imported <- header 427 } else { 428 if block == nil { 429 t.Fatalf("Fetcher try to import empty block") 430 } 431 imported <- block 432 } 433 } 434 for i := len(hashes) - 2; i >= 0; i-- { 435 tester.fetcher.Notify("first", hashes[i], uint64(len(hashes)-i-1), time.Now().Add(-arriveTimeout), firstHeaderWrapper, firstBodyFetcher) 436 tester.fetcher.Notify("second", hashes[i], uint64(len(hashes)-i-1), time.Now().Add(-arriveTimeout+time.Millisecond), secondHeaderWrapper, secondBodyFetcher) 437 tester.fetcher.Notify("second", hashes[i], uint64(len(hashes)-i-1), time.Now().Add(-arriveTimeout-time.Millisecond), secondHeaderWrapper, secondBodyFetcher) 438 verifyImportEvent(t, imported, true) 439 } 440 verifyImportDone(t, imported) 441 442 // Make sure no blocks were retrieved twice 443 if int(counter) != targetBlocks { 444 t.Fatalf("retrieval count mismatch: have %v, want %v", counter, targetBlocks) 445 } 446 verifyChainHeight(t, tester, uint64(len(hashes)-1)) 447 } 448 449 // Tests that announcements arriving while a previous is being fetched still 450 // results in a valid import. 451 func TestFullOverlappingAnnouncements(t *testing.T) { testOverlappingAnnouncements(t, false) } 452 func TestLightOverlappingAnnouncements(t *testing.T) { testOverlappingAnnouncements(t, true) } 453 454 func testOverlappingAnnouncements(t *testing.T, light bool) { 455 // Create a chain of blocks to import 456 targetBlocks := 4 * hashLimit 457 hashes, blocks := makeChain(targetBlocks, 0, genesis) 458 459 tester := newTester(light) 460 headerFetcher := tester.makeHeaderFetcher("valid", blocks, -gatherSlack) 461 bodyFetcher := tester.makeBodyFetcher("valid", blocks, 0) 462 463 // Iteratively announce blocks, but overlap them continuously 464 overlap := 16 465 imported := make(chan interface{}, len(hashes)-1) 466 for i := 0; i < overlap; i++ { 467 imported <- nil 468 } 469 tester.fetcher.importedHook = func(header *types.Header, block *types.Block) { 470 if light { 471 if header == nil { 472 t.Fatalf("Fetcher try to import empty header") 473 } 474 imported <- header 475 } else { 476 if block == nil { 477 t.Fatalf("Fetcher try to import empty block") 478 } 479 imported <- block 480 } 481 } 482 483 for i := len(hashes) - 2; i >= 0; i-- { 484 tester.fetcher.Notify("valid", hashes[i], uint64(len(hashes)-i-1), time.Now().Add(-arriveTimeout), headerFetcher, bodyFetcher) 485 select { 486 case <-imported: 487 case <-time.After(time.Second): 488 t.Fatalf("block %d: import timeout", len(hashes)-i) 489 } 490 } 491 // Wait for all the imports to complete and check count 492 verifyImportCount(t, imported, overlap) 493 verifyChainHeight(t, tester, uint64(len(hashes)-1)) 494 } 495 496 // Tests that announces already being retrieved will not be duplicated. 497 func TestFullPendingDeduplication(t *testing.T) { testPendingDeduplication(t, false) } 498 func TestLightPendingDeduplication(t *testing.T) { testPendingDeduplication(t, true) } 499 500 func testPendingDeduplication(t *testing.T, light bool) { 501 // Create a hash and corresponding block 502 hashes, blocks := makeChain(1, 0, genesis) 503 504 // Assemble a tester with a built in counter and delayed fetcher 505 tester := newTester(light) 506 headerFetcher := tester.makeHeaderFetcher("repeater", blocks, -gatherSlack) 507 bodyFetcher := tester.makeBodyFetcher("repeater", blocks, 0) 508 509 delay := 50 * time.Millisecond 510 counter := uint32(0) 511 headerWrapper := func(hash common.Hash, sink chan *eth.Response) (*eth.Request, error) { 512 atomic.AddUint32(&counter, 1) 513 514 // Simulate a long running fetch 515 resink := make(chan *eth.Response) 516 req, err := headerFetcher(hash, resink) 517 if err == nil { 518 go func() { 519 res := <-resink 520 time.Sleep(delay) 521 sink <- res 522 }() 523 } 524 return req, err 525 } 526 checkNonExist := func() bool { 527 return tester.getBlock(hashes[0]) == nil 528 } 529 if light { 530 checkNonExist = func() bool { 531 return tester.getHeader(hashes[0]) == nil 532 } 533 } 534 // Announce the same block many times until it's fetched (wait for any pending ops) 535 for checkNonExist() { 536 tester.fetcher.Notify("repeater", hashes[0], 1, time.Now().Add(-arriveTimeout), headerWrapper, bodyFetcher) 537 time.Sleep(time.Millisecond) 538 } 539 time.Sleep(delay) 540 541 // Check that all blocks were imported and none fetched twice 542 if int(counter) != 1 { 543 t.Fatalf("retrieval count mismatch: have %v, want %v", counter, 1) 544 } 545 verifyChainHeight(t, tester, 1) 546 } 547 548 // Tests that announcements retrieved in a random order are cached and eventually 549 // imported when all the gaps are filled in. 550 func TestFullRandomArrivalImport(t *testing.T) { testRandomArrivalImport(t, false) } 551 func TestLightRandomArrivalImport(t *testing.T) { testRandomArrivalImport(t, true) } 552 553 func testRandomArrivalImport(t *testing.T, light bool) { 554 // Create a chain of blocks to import, and choose one to delay 555 targetBlocks := maxQueueDist 556 hashes, blocks := makeChain(targetBlocks, 0, genesis) 557 skip := targetBlocks / 2 558 559 tester := newTester(light) 560 headerFetcher := tester.makeHeaderFetcher("valid", blocks, -gatherSlack) 561 bodyFetcher := tester.makeBodyFetcher("valid", blocks, 0) 562 563 // Iteratively announce blocks, skipping one entry 564 imported := make(chan interface{}, len(hashes)-1) 565 tester.fetcher.importedHook = func(header *types.Header, block *types.Block) { 566 if light { 567 if header == nil { 568 t.Fatalf("Fetcher try to import empty header") 569 } 570 imported <- header 571 } else { 572 if block == nil { 573 t.Fatalf("Fetcher try to import empty block") 574 } 575 imported <- block 576 } 577 } 578 for i := len(hashes) - 1; i >= 0; i-- { 579 if i != skip { 580 tester.fetcher.Notify("valid", hashes[i], uint64(len(hashes)-i-1), time.Now().Add(-arriveTimeout), headerFetcher, bodyFetcher) 581 time.Sleep(time.Millisecond) 582 } 583 } 584 // Finally announce the skipped entry and check full import 585 tester.fetcher.Notify("valid", hashes[skip], uint64(len(hashes)-skip-1), time.Now().Add(-arriveTimeout), headerFetcher, bodyFetcher) 586 verifyImportCount(t, imported, len(hashes)-1) 587 verifyChainHeight(t, tester, uint64(len(hashes)-1)) 588 } 589 590 // Tests that direct block enqueues (due to block propagation vs. hash announce) 591 // are correctly schedule, filling and import queue gaps. 592 func TestQueueGapFill(t *testing.T) { 593 // Create a chain of blocks to import, and choose one to not announce at all 594 targetBlocks := maxQueueDist 595 hashes, blocks := makeChain(targetBlocks, 0, genesis) 596 skip := targetBlocks / 2 597 598 tester := newTester(false) 599 headerFetcher := tester.makeHeaderFetcher("valid", blocks, -gatherSlack) 600 bodyFetcher := tester.makeBodyFetcher("valid", blocks, 0) 601 602 // Iteratively announce blocks, skipping one entry 603 imported := make(chan interface{}, len(hashes)-1) 604 tester.fetcher.importedHook = func(header *types.Header, block *types.Block) { imported <- block } 605 606 for i := len(hashes) - 1; i >= 0; i-- { 607 if i != skip { 608 tester.fetcher.Notify("valid", hashes[i], uint64(len(hashes)-i-1), time.Now().Add(-arriveTimeout), headerFetcher, bodyFetcher) 609 time.Sleep(time.Millisecond) 610 } 611 } 612 // Fill the missing block directly as if propagated 613 tester.fetcher.Enqueue("valid", blocks[hashes[skip]]) 614 verifyImportCount(t, imported, len(hashes)-1) 615 verifyChainHeight(t, tester, uint64(len(hashes)-1)) 616 } 617 618 // Tests that blocks arriving from various sources (multiple propagations, hash 619 // announces, etc) do not get scheduled for import multiple times. 620 func TestImportDeduplication(t *testing.T) { 621 // Create two blocks to import (one for duplication, the other for stalling) 622 hashes, blocks := makeChain(2, 0, genesis) 623 624 // Create the tester and wrap the importer with a counter 625 tester := newTester(false) 626 headerFetcher := tester.makeHeaderFetcher("valid", blocks, -gatherSlack) 627 bodyFetcher := tester.makeBodyFetcher("valid", blocks, 0) 628 629 counter := uint32(0) 630 tester.fetcher.insertChain = func(blocks types.Blocks) (int, error) { 631 atomic.AddUint32(&counter, uint32(len(blocks))) 632 return tester.insertChain(blocks) 633 } 634 // Instrument the fetching and imported events 635 fetching := make(chan []common.Hash) 636 imported := make(chan interface{}, len(hashes)-1) 637 tester.fetcher.fetchingHook = func(hashes []common.Hash) { fetching <- hashes } 638 tester.fetcher.importedHook = func(header *types.Header, block *types.Block) { imported <- block } 639 640 // Announce the duplicating block, wait for retrieval, and also propagate directly 641 tester.fetcher.Notify("valid", hashes[0], 1, time.Now().Add(-arriveTimeout), headerFetcher, bodyFetcher) 642 <-fetching 643 644 tester.fetcher.Enqueue("valid", blocks[hashes[0]]) 645 tester.fetcher.Enqueue("valid", blocks[hashes[0]]) 646 tester.fetcher.Enqueue("valid", blocks[hashes[0]]) 647 648 // Fill the missing block directly as if propagated, and check import uniqueness 649 tester.fetcher.Enqueue("valid", blocks[hashes[1]]) 650 verifyImportCount(t, imported, 2) 651 652 if counter != 2 { 653 t.Fatalf("import invocation count mismatch: have %v, want %v", counter, 2) 654 } 655 } 656 657 // Tests that blocks with numbers much lower or higher than out current head get 658 // discarded to prevent wasting resources on useless blocks from faulty peers. 659 func TestDistantPropagationDiscarding(t *testing.T) { 660 // Create a long chain to import and define the discard boundaries 661 hashes, blocks := makeChain(3*maxQueueDist, 0, genesis) 662 head := hashes[len(hashes)/2] 663 664 low, high := len(hashes)/2+maxUncleDist+1, len(hashes)/2-maxQueueDist-1 665 666 // Create a tester and simulate a head block being the middle of the above chain 667 tester := newTester(false) 668 669 tester.lock.Lock() 670 tester.hashes = []common.Hash{head} 671 tester.blocks = map[common.Hash]*types.Block{head: blocks[head]} 672 tester.lock.Unlock() 673 674 // Ensure that a block with a lower number than the threshold is discarded 675 tester.fetcher.Enqueue("lower", blocks[hashes[low]]) 676 time.Sleep(10 * time.Millisecond) 677 if !tester.fetcher.queue.Empty() { 678 t.Fatalf("fetcher queued stale block") 679 } 680 // Ensure that a block with a higher number than the threshold is discarded 681 tester.fetcher.Enqueue("higher", blocks[hashes[high]]) 682 time.Sleep(10 * time.Millisecond) 683 if !tester.fetcher.queue.Empty() { 684 t.Fatalf("fetcher queued future block") 685 } 686 } 687 688 // Tests that announcements with numbers much lower or higher than out current 689 // head get discarded to prevent wasting resources on useless blocks from faulty 690 // peers. 691 func TestFullDistantAnnouncementDiscarding(t *testing.T) { testDistantAnnouncementDiscarding(t, false) } 692 func TestLightDistantAnnouncementDiscarding(t *testing.T) { testDistantAnnouncementDiscarding(t, true) } 693 694 func testDistantAnnouncementDiscarding(t *testing.T, light bool) { 695 // Create a long chain to import and define the discard boundaries 696 hashes, blocks := makeChain(3*maxQueueDist, 0, genesis) 697 head := hashes[len(hashes)/2] 698 699 low, high := len(hashes)/2+maxUncleDist+1, len(hashes)/2-maxQueueDist-1 700 701 // Create a tester and simulate a head block being the middle of the above chain 702 tester := newTester(light) 703 704 tester.lock.Lock() 705 tester.hashes = []common.Hash{head} 706 tester.headers = map[common.Hash]*types.Header{head: blocks[head].Header()} 707 tester.blocks = map[common.Hash]*types.Block{head: blocks[head]} 708 tester.lock.Unlock() 709 710 headerFetcher := tester.makeHeaderFetcher("lower", blocks, -gatherSlack) 711 bodyFetcher := tester.makeBodyFetcher("lower", blocks, 0) 712 713 fetching := make(chan struct{}, 2) 714 tester.fetcher.fetchingHook = func(hashes []common.Hash) { fetching <- struct{}{} } 715 716 // Ensure that a block with a lower number than the threshold is discarded 717 tester.fetcher.Notify("lower", hashes[low], blocks[hashes[low]].NumberU64(), time.Now().Add(-arriveTimeout), headerFetcher, bodyFetcher) 718 select { 719 case <-time.After(50 * time.Millisecond): 720 case <-fetching: 721 t.Fatalf("fetcher requested stale header") 722 } 723 // Ensure that a block with a higher number than the threshold is discarded 724 tester.fetcher.Notify("higher", hashes[high], blocks[hashes[high]].NumberU64(), time.Now().Add(-arriveTimeout), headerFetcher, bodyFetcher) 725 select { 726 case <-time.After(50 * time.Millisecond): 727 case <-fetching: 728 t.Fatalf("fetcher requested future header") 729 } 730 } 731 732 // Tests that peers announcing blocks with invalid numbers (i.e. not matching 733 // the headers provided afterwards) get dropped as malicious. 734 func TestFullInvalidNumberAnnouncement(t *testing.T) { testInvalidNumberAnnouncement(t, false) } 735 func TestLightInvalidNumberAnnouncement(t *testing.T) { testInvalidNumberAnnouncement(t, true) } 736 737 func testInvalidNumberAnnouncement(t *testing.T, light bool) { 738 // Create a single block to import and check numbers against 739 hashes, blocks := makeChain(1, 0, genesis) 740 741 tester := newTester(light) 742 badHeaderFetcher := tester.makeHeaderFetcher("bad", blocks, -gatherSlack) 743 badBodyFetcher := tester.makeBodyFetcher("bad", blocks, 0) 744 745 imported := make(chan interface{}) 746 announced := make(chan interface{}) 747 tester.fetcher.importedHook = func(header *types.Header, block *types.Block) { 748 if light { 749 if header == nil { 750 t.Fatalf("Fetcher try to import empty header") 751 } 752 imported <- header 753 } else { 754 if block == nil { 755 t.Fatalf("Fetcher try to import empty block") 756 } 757 imported <- block 758 } 759 } 760 // Announce a block with a bad number, check for immediate drop 761 tester.fetcher.announceChangeHook = func(hash common.Hash, b bool) { 762 announced <- nil 763 } 764 tester.fetcher.Notify("bad", hashes[0], 2, time.Now().Add(-arriveTimeout), badHeaderFetcher, badBodyFetcher) 765 verifyAnnounce := func() { 766 for i := 0; i < 2; i++ { 767 select { 768 case <-announced: 769 continue 770 case <-time.After(1 * time.Second): 771 t.Fatal("announce timeout") 772 return 773 } 774 } 775 } 776 verifyAnnounce() 777 verifyImportEvent(t, imported, false) 778 tester.lock.RLock() 779 dropped := tester.drops["bad"] 780 tester.lock.RUnlock() 781 782 if !dropped { 783 t.Fatalf("peer with invalid numbered announcement not dropped") 784 } 785 goodHeaderFetcher := tester.makeHeaderFetcher("good", blocks, -gatherSlack) 786 goodBodyFetcher := tester.makeBodyFetcher("good", blocks, 0) 787 // Make sure a good announcement passes without a drop 788 tester.fetcher.Notify("good", hashes[0], 1, time.Now().Add(-arriveTimeout), goodHeaderFetcher, goodBodyFetcher) 789 verifyAnnounce() 790 verifyImportEvent(t, imported, true) 791 792 tester.lock.RLock() 793 dropped = tester.drops["good"] 794 tester.lock.RUnlock() 795 796 if dropped { 797 t.Fatalf("peer with valid numbered announcement dropped") 798 } 799 verifyImportDone(t, imported) 800 } 801 802 // Tests that if a block is empty (i.e. header only), no body request should be 803 // made, and instead the header should be assembled into a whole block in itself. 804 func TestEmptyBlockShortCircuit(t *testing.T) { 805 // Create a chain of blocks to import 806 hashes, blocks := makeChain(32, 0, genesis) 807 808 tester := newTester(false) 809 headerFetcher := tester.makeHeaderFetcher("valid", blocks, -gatherSlack) 810 bodyFetcher := tester.makeBodyFetcher("valid", blocks, 0) 811 812 // Add a monitoring hook for all internal events 813 fetching := make(chan []common.Hash) 814 tester.fetcher.fetchingHook = func(hashes []common.Hash) { fetching <- hashes } 815 816 completing := make(chan []common.Hash) 817 tester.fetcher.completingHook = func(hashes []common.Hash) { completing <- hashes } 818 819 imported := make(chan interface{}) 820 tester.fetcher.importedHook = func(header *types.Header, block *types.Block) { 821 if block == nil { 822 t.Fatalf("Fetcher try to import empty block") 823 } 824 imported <- block 825 } 826 // Iteratively announce blocks until all are imported 827 for i := len(hashes) - 2; i >= 0; i-- { 828 tester.fetcher.Notify("valid", hashes[i], uint64(len(hashes)-i-1), time.Now().Add(-arriveTimeout), headerFetcher, bodyFetcher) 829 830 // All announces should fetch the header 831 verifyFetchingEvent(t, fetching, true) 832 833 // Only blocks with data contents should request bodies 834 verifyCompletingEvent(t, completing, len(blocks[hashes[i]].Transactions()) > 0 || len(blocks[hashes[i]].Uncles()) > 0) 835 836 // Irrelevant of the construct, import should succeed 837 verifyImportEvent(t, imported, true) 838 } 839 verifyImportDone(t, imported) 840 } 841 842 // Tests that a peer is unable to use unbounded memory with sending infinite 843 // block announcements to a node, but that even in the face of such an attack, 844 // the fetcher remains operational. 845 func TestHashMemoryExhaustionAttack(t *testing.T) { 846 // Create a tester with instrumented import hooks 847 tester := newTester(false) 848 849 imported, announces := make(chan interface{}), int32(0) 850 tester.fetcher.importedHook = func(header *types.Header, block *types.Block) { imported <- block } 851 tester.fetcher.announceChangeHook = func(hash common.Hash, added bool) { 852 if added { 853 atomic.AddInt32(&announces, 1) 854 } else { 855 atomic.AddInt32(&announces, -1) 856 } 857 } 858 // Create a valid chain and an infinite junk chain 859 targetBlocks := hashLimit + 2*maxQueueDist 860 hashes, blocks := makeChain(targetBlocks, 0, genesis) 861 validHeaderFetcher := tester.makeHeaderFetcher("valid", blocks, -gatherSlack) 862 validBodyFetcher := tester.makeBodyFetcher("valid", blocks, 0) 863 864 attack, _ := makeChain(targetBlocks, 0, unknownBlock) 865 attackerHeaderFetcher := tester.makeHeaderFetcher("attacker", nil, -gatherSlack) 866 attackerBodyFetcher := tester.makeBodyFetcher("attacker", nil, 0) 867 868 // Feed the tester a huge hashset from the attacker, and a limited from the valid peer 869 for i := 0; i < len(attack); i++ { 870 if i < maxQueueDist { 871 tester.fetcher.Notify("valid", hashes[len(hashes)-2-i], uint64(i+1), time.Now(), validHeaderFetcher, validBodyFetcher) 872 } 873 tester.fetcher.Notify("attacker", attack[i], 1 /* don't distance drop */, time.Now(), attackerHeaderFetcher, attackerBodyFetcher) 874 } 875 if count := atomic.LoadInt32(&announces); count != hashLimit+maxQueueDist { 876 t.Fatalf("queued announce count mismatch: have %d, want %d", count, hashLimit+maxQueueDist) 877 } 878 // Wait for fetches to complete 879 verifyImportCount(t, imported, maxQueueDist) 880 881 // Feed the remaining valid hashes to ensure DOS protection state remains clean 882 for i := len(hashes) - maxQueueDist - 2; i >= 0; i-- { 883 tester.fetcher.Notify("valid", hashes[i], uint64(len(hashes)-i-1), time.Now().Add(-arriveTimeout), validHeaderFetcher, validBodyFetcher) 884 verifyImportEvent(t, imported, true) 885 } 886 verifyImportDone(t, imported) 887 } 888 889 // Tests that blocks sent to the fetcher (either through propagation or via hash 890 // announces and retrievals) don't pile up indefinitely, exhausting available 891 // system memory. 892 func TestBlockMemoryExhaustionAttack(t *testing.T) { 893 // Create a tester with instrumented import hooks 894 tester := newTester(false) 895 896 imported, enqueued := make(chan interface{}), int32(0) 897 tester.fetcher.importedHook = func(header *types.Header, block *types.Block) { imported <- block } 898 tester.fetcher.queueChangeHook = func(hash common.Hash, added bool) { 899 if added { 900 atomic.AddInt32(&enqueued, 1) 901 } else { 902 atomic.AddInt32(&enqueued, -1) 903 } 904 } 905 // Create a valid chain and a batch of dangling (but in range) blocks 906 targetBlocks := hashLimit + 2*maxQueueDist 907 hashes, blocks := makeChain(targetBlocks, 0, genesis) 908 attack := make(map[common.Hash]*types.Block) 909 for i := byte(0); len(attack) < blockLimit+2*maxQueueDist; i++ { 910 hashes, blocks := makeChain(maxQueueDist-1, i, unknownBlock) 911 for _, hash := range hashes[:maxQueueDist-2] { 912 attack[hash] = blocks[hash] 913 } 914 } 915 // Try to feed all the attacker blocks make sure only a limited batch is accepted 916 for _, block := range attack { 917 tester.fetcher.Enqueue("attacker", block) 918 } 919 time.Sleep(200 * time.Millisecond) 920 if queued := atomic.LoadInt32(&enqueued); queued != blockLimit { 921 t.Fatalf("queued block count mismatch: have %d, want %d", queued, blockLimit) 922 } 923 // Queue up a batch of valid blocks, and check that a new peer is allowed to do so 924 for i := 0; i < maxQueueDist-1; i++ { 925 tester.fetcher.Enqueue("valid", blocks[hashes[len(hashes)-3-i]]) 926 } 927 time.Sleep(100 * time.Millisecond) 928 if queued := atomic.LoadInt32(&enqueued); queued != blockLimit+maxQueueDist-1 { 929 t.Fatalf("queued block count mismatch: have %d, want %d", queued, blockLimit+maxQueueDist-1) 930 } 931 // Insert the missing piece (and sanity check the import) 932 tester.fetcher.Enqueue("valid", blocks[hashes[len(hashes)-2]]) 933 verifyImportCount(t, imported, maxQueueDist) 934 935 // Insert the remaining blocks in chunks to ensure clean DOS protection 936 for i := maxQueueDist; i < len(hashes)-1; i++ { 937 tester.fetcher.Enqueue("valid", blocks[hashes[len(hashes)-2-i]]) 938 verifyImportEvent(t, imported, true) 939 } 940 verifyImportDone(t, imported) 941 }