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