github.com/daethereum/go-dae@v2.2.3+incompatible/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/daethereum/go-dae/common" 28 "github.com/daethereum/go-dae/consensus/ethash" 29 "github.com/daethereum/go-dae/core" 30 "github.com/daethereum/go-dae/core/rawdb" 31 "github.com/daethereum/go-dae/core/types" 32 "github.com/daethereum/go-dae/crypto" 33 "github.com/daethereum/go-dae/eth/protocols/eth" 34 "github.com/daethereum/go-dae/params" 35 "github.com/daethereum/go-dae/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 defer tester.fetcher.Stop() 368 headerFetcher := tester.makeHeaderFetcher("valid", blocks, -gatherSlack) 369 bodyFetcher := tester.makeBodyFetcher("valid", blocks, 0) 370 371 // Iteratively announce blocks until all are imported 372 imported := make(chan interface{}) 373 tester.fetcher.importedHook = func(header *types.Header, block *types.Block) { 374 if light { 375 if header == nil { 376 t.Fatalf("Fetcher try to import empty header") 377 } 378 imported <- header 379 } else { 380 if block == nil { 381 t.Fatalf("Fetcher try to import empty block") 382 } 383 imported <- block 384 } 385 } 386 for i := len(hashes) - 2; i >= 0; i-- { 387 tester.fetcher.Notify("valid", hashes[i], uint64(len(hashes)-i-1), time.Now().Add(-arriveTimeout), headerFetcher, bodyFetcher) 388 verifyImportEvent(t, imported, true) 389 } 390 verifyImportDone(t, imported) 391 verifyChainHeight(t, tester, uint64(len(hashes)-1)) 392 } 393 394 // Tests that if blocks are announced by multiple peers (or even the same buggy 395 // peer), they will only get downloaded at most once. 396 func TestFullConcurrentAnnouncements(t *testing.T) { testConcurrentAnnouncements(t, false) } 397 func TestLightConcurrentAnnouncements(t *testing.T) { testConcurrentAnnouncements(t, true) } 398 399 func testConcurrentAnnouncements(t *testing.T, light bool) { 400 // Create a chain of blocks to import 401 targetBlocks := 4 * hashLimit 402 hashes, blocks := makeChain(targetBlocks, 0, genesis) 403 404 // Assemble a tester with a built in counter for the requests 405 tester := newTester(light) 406 firstHeaderFetcher := tester.makeHeaderFetcher("first", blocks, -gatherSlack) 407 firstBodyFetcher := tester.makeBodyFetcher("first", blocks, 0) 408 secondHeaderFetcher := tester.makeHeaderFetcher("second", blocks, -gatherSlack) 409 secondBodyFetcher := tester.makeBodyFetcher("second", blocks, 0) 410 411 counter := uint32(0) 412 firstHeaderWrapper := func(hash common.Hash, sink chan *eth.Response) (*eth.Request, error) { 413 atomic.AddUint32(&counter, 1) 414 return firstHeaderFetcher(hash, sink) 415 } 416 secondHeaderWrapper := func(hash common.Hash, sink chan *eth.Response) (*eth.Request, error) { 417 atomic.AddUint32(&counter, 1) 418 return secondHeaderFetcher(hash, sink) 419 } 420 // Iteratively announce blocks until all are imported 421 imported := make(chan interface{}) 422 tester.fetcher.importedHook = func(header *types.Header, block *types.Block) { 423 if light { 424 if header == nil { 425 t.Fatalf("Fetcher try to import empty header") 426 } 427 imported <- header 428 } else { 429 if block == nil { 430 t.Fatalf("Fetcher try to import empty block") 431 } 432 imported <- block 433 } 434 } 435 for i := len(hashes) - 2; i >= 0; i-- { 436 tester.fetcher.Notify("first", hashes[i], uint64(len(hashes)-i-1), time.Now().Add(-arriveTimeout), firstHeaderWrapper, firstBodyFetcher) 437 tester.fetcher.Notify("second", hashes[i], uint64(len(hashes)-i-1), time.Now().Add(-arriveTimeout+time.Millisecond), secondHeaderWrapper, secondBodyFetcher) 438 tester.fetcher.Notify("second", hashes[i], uint64(len(hashes)-i-1), time.Now().Add(-arriveTimeout-time.Millisecond), secondHeaderWrapper, secondBodyFetcher) 439 verifyImportEvent(t, imported, true) 440 } 441 verifyImportDone(t, imported) 442 443 // Make sure no blocks were retrieved twice 444 if int(counter) != targetBlocks { 445 t.Fatalf("retrieval count mismatch: have %v, want %v", counter, targetBlocks) 446 } 447 verifyChainHeight(t, tester, uint64(len(hashes)-1)) 448 } 449 450 // Tests that announcements arriving while a previous is being fetched still 451 // results in a valid import. 452 func TestFullOverlappingAnnouncements(t *testing.T) { testOverlappingAnnouncements(t, false) } 453 func TestLightOverlappingAnnouncements(t *testing.T) { testOverlappingAnnouncements(t, true) } 454 455 func testOverlappingAnnouncements(t *testing.T, light bool) { 456 // Create a chain of blocks to import 457 targetBlocks := 4 * hashLimit 458 hashes, blocks := makeChain(targetBlocks, 0, genesis) 459 460 tester := newTester(light) 461 headerFetcher := tester.makeHeaderFetcher("valid", blocks, -gatherSlack) 462 bodyFetcher := tester.makeBodyFetcher("valid", blocks, 0) 463 464 // Iteratively announce blocks, but overlap them continuously 465 overlap := 16 466 imported := make(chan interface{}, len(hashes)-1) 467 for i := 0; i < overlap; i++ { 468 imported <- nil 469 } 470 tester.fetcher.importedHook = func(header *types.Header, block *types.Block) { 471 if light { 472 if header == nil { 473 t.Fatalf("Fetcher try to import empty header") 474 } 475 imported <- header 476 } else { 477 if block == nil { 478 t.Fatalf("Fetcher try to import empty block") 479 } 480 imported <- block 481 } 482 } 483 484 for i := len(hashes) - 2; i >= 0; i-- { 485 tester.fetcher.Notify("valid", hashes[i], uint64(len(hashes)-i-1), time.Now().Add(-arriveTimeout), headerFetcher, bodyFetcher) 486 select { 487 case <-imported: 488 case <-time.After(time.Second): 489 t.Fatalf("block %d: import timeout", len(hashes)-i) 490 } 491 } 492 // Wait for all the imports to complete and check count 493 verifyImportCount(t, imported, overlap) 494 verifyChainHeight(t, tester, uint64(len(hashes)-1)) 495 } 496 497 // Tests that announces already being retrieved will not be duplicated. 498 func TestFullPendingDeduplication(t *testing.T) { testPendingDeduplication(t, false) } 499 func TestLightPendingDeduplication(t *testing.T) { testPendingDeduplication(t, true) } 500 501 func testPendingDeduplication(t *testing.T, light bool) { 502 // Create a hash and corresponding block 503 hashes, blocks := makeChain(1, 0, genesis) 504 505 // Assemble a tester with a built in counter and delayed fetcher 506 tester := newTester(light) 507 headerFetcher := tester.makeHeaderFetcher("repeater", blocks, -gatherSlack) 508 bodyFetcher := tester.makeBodyFetcher("repeater", blocks, 0) 509 510 delay := 50 * time.Millisecond 511 counter := uint32(0) 512 headerWrapper := func(hash common.Hash, sink chan *eth.Response) (*eth.Request, error) { 513 atomic.AddUint32(&counter, 1) 514 515 // Simulate a long running fetch 516 resink := make(chan *eth.Response) 517 req, err := headerFetcher(hash, resink) 518 if err == nil { 519 go func() { 520 res := <-resink 521 time.Sleep(delay) 522 sink <- res 523 }() 524 } 525 return req, err 526 } 527 checkNonExist := func() bool { 528 return tester.getBlock(hashes[0]) == nil 529 } 530 if light { 531 checkNonExist = func() bool { 532 return tester.getHeader(hashes[0]) == nil 533 } 534 } 535 // Announce the same block many times until it's fetched (wait for any pending ops) 536 for checkNonExist() { 537 tester.fetcher.Notify("repeater", hashes[0], 1, time.Now().Add(-arriveTimeout), headerWrapper, bodyFetcher) 538 time.Sleep(time.Millisecond) 539 } 540 time.Sleep(delay) 541 542 // Check that all blocks were imported and none fetched twice 543 if int(counter) != 1 { 544 t.Fatalf("retrieval count mismatch: have %v, want %v", counter, 1) 545 } 546 verifyChainHeight(t, tester, 1) 547 } 548 549 // Tests that announcements retrieved in a random order are cached and eventually 550 // imported when all the gaps are filled in. 551 func TestFullRandomArrivalImport(t *testing.T) { testRandomArrivalImport(t, false) } 552 func TestLightRandomArrivalImport(t *testing.T) { testRandomArrivalImport(t, true) } 553 554 func testRandomArrivalImport(t *testing.T, light bool) { 555 // Create a chain of blocks to import, and choose one to delay 556 targetBlocks := maxQueueDist 557 hashes, blocks := makeChain(targetBlocks, 0, genesis) 558 skip := targetBlocks / 2 559 560 tester := newTester(light) 561 headerFetcher := tester.makeHeaderFetcher("valid", blocks, -gatherSlack) 562 bodyFetcher := tester.makeBodyFetcher("valid", blocks, 0) 563 564 // Iteratively announce blocks, skipping one entry 565 imported := make(chan interface{}, len(hashes)-1) 566 tester.fetcher.importedHook = func(header *types.Header, block *types.Block) { 567 if light { 568 if header == nil { 569 t.Fatalf("Fetcher try to import empty header") 570 } 571 imported <- header 572 } else { 573 if block == nil { 574 t.Fatalf("Fetcher try to import empty block") 575 } 576 imported <- block 577 } 578 } 579 for i := len(hashes) - 1; i >= 0; i-- { 580 if i != skip { 581 tester.fetcher.Notify("valid", hashes[i], uint64(len(hashes)-i-1), time.Now().Add(-arriveTimeout), headerFetcher, bodyFetcher) 582 time.Sleep(time.Millisecond) 583 } 584 } 585 // Finally announce the skipped entry and check full import 586 tester.fetcher.Notify("valid", hashes[skip], uint64(len(hashes)-skip-1), time.Now().Add(-arriveTimeout), headerFetcher, bodyFetcher) 587 verifyImportCount(t, imported, len(hashes)-1) 588 verifyChainHeight(t, tester, uint64(len(hashes)-1)) 589 } 590 591 // Tests that direct block enqueues (due to block propagation vs. hash announce) 592 // are correctly schedule, filling and import queue gaps. 593 func TestQueueGapFill(t *testing.T) { 594 // Create a chain of blocks to import, and choose one to not announce at all 595 targetBlocks := maxQueueDist 596 hashes, blocks := makeChain(targetBlocks, 0, genesis) 597 skip := targetBlocks / 2 598 599 tester := newTester(false) 600 headerFetcher := tester.makeHeaderFetcher("valid", blocks, -gatherSlack) 601 bodyFetcher := tester.makeBodyFetcher("valid", blocks, 0) 602 603 // Iteratively announce blocks, skipping one entry 604 imported := make(chan interface{}, len(hashes)-1) 605 tester.fetcher.importedHook = func(header *types.Header, block *types.Block) { imported <- block } 606 607 for i := len(hashes) - 1; i >= 0; i-- { 608 if i != skip { 609 tester.fetcher.Notify("valid", hashes[i], uint64(len(hashes)-i-1), time.Now().Add(-arriveTimeout), headerFetcher, bodyFetcher) 610 time.Sleep(time.Millisecond) 611 } 612 } 613 // Fill the missing block directly as if propagated 614 tester.fetcher.Enqueue("valid", blocks[hashes[skip]]) 615 verifyImportCount(t, imported, len(hashes)-1) 616 verifyChainHeight(t, tester, uint64(len(hashes)-1)) 617 } 618 619 // Tests that blocks arriving from various sources (multiple propagations, hash 620 // announces, etc) do not get scheduled for import multiple times. 621 func TestImportDeduplication(t *testing.T) { 622 // Create two blocks to import (one for duplication, the other for stalling) 623 hashes, blocks := makeChain(2, 0, genesis) 624 625 // Create the tester and wrap the importer with a counter 626 tester := newTester(false) 627 headerFetcher := tester.makeHeaderFetcher("valid", blocks, -gatherSlack) 628 bodyFetcher := tester.makeBodyFetcher("valid", blocks, 0) 629 630 counter := uint32(0) 631 tester.fetcher.insertChain = func(blocks types.Blocks) (int, error) { 632 atomic.AddUint32(&counter, uint32(len(blocks))) 633 return tester.insertChain(blocks) 634 } 635 // Instrument the fetching and imported events 636 fetching := make(chan []common.Hash) 637 imported := make(chan interface{}, len(hashes)-1) 638 tester.fetcher.fetchingHook = func(hashes []common.Hash) { fetching <- hashes } 639 tester.fetcher.importedHook = func(header *types.Header, block *types.Block) { imported <- block } 640 641 // Announce the duplicating block, wait for retrieval, and also propagate directly 642 tester.fetcher.Notify("valid", hashes[0], 1, time.Now().Add(-arriveTimeout), headerFetcher, bodyFetcher) 643 <-fetching 644 645 tester.fetcher.Enqueue("valid", blocks[hashes[0]]) 646 tester.fetcher.Enqueue("valid", blocks[hashes[0]]) 647 tester.fetcher.Enqueue("valid", blocks[hashes[0]]) 648 649 // Fill the missing block directly as if propagated, and check import uniqueness 650 tester.fetcher.Enqueue("valid", blocks[hashes[1]]) 651 verifyImportCount(t, imported, 2) 652 653 if counter != 2 { 654 t.Fatalf("import invocation count mismatch: have %v, want %v", counter, 2) 655 } 656 } 657 658 // Tests that blocks with numbers much lower or higher than out current head get 659 // discarded to prevent wasting resources on useless blocks from faulty peers. 660 func TestDistantPropagationDiscarding(t *testing.T) { 661 // Create a long chain to import and define the discard boundaries 662 hashes, blocks := makeChain(3*maxQueueDist, 0, genesis) 663 head := hashes[len(hashes)/2] 664 665 low, high := len(hashes)/2+maxUncleDist+1, len(hashes)/2-maxQueueDist-1 666 667 // Create a tester and simulate a head block being the middle of the above chain 668 tester := newTester(false) 669 670 tester.lock.Lock() 671 tester.hashes = []common.Hash{head} 672 tester.blocks = map[common.Hash]*types.Block{head: blocks[head]} 673 tester.lock.Unlock() 674 675 // Ensure that a block with a lower number than the threshold is discarded 676 tester.fetcher.Enqueue("lower", blocks[hashes[low]]) 677 time.Sleep(10 * time.Millisecond) 678 if !tester.fetcher.queue.Empty() { 679 t.Fatalf("fetcher queued stale block") 680 } 681 // Ensure that a block with a higher number than the threshold is discarded 682 tester.fetcher.Enqueue("higher", blocks[hashes[high]]) 683 time.Sleep(10 * time.Millisecond) 684 if !tester.fetcher.queue.Empty() { 685 t.Fatalf("fetcher queued future block") 686 } 687 } 688 689 // Tests that announcements with numbers much lower or higher than out current 690 // head get discarded to prevent wasting resources on useless blocks from faulty 691 // peers. 692 func TestFullDistantAnnouncementDiscarding(t *testing.T) { testDistantAnnouncementDiscarding(t, false) } 693 func TestLightDistantAnnouncementDiscarding(t *testing.T) { testDistantAnnouncementDiscarding(t, true) } 694 695 func testDistantAnnouncementDiscarding(t *testing.T, light bool) { 696 // Create a long chain to import and define the discard boundaries 697 hashes, blocks := makeChain(3*maxQueueDist, 0, genesis) 698 head := hashes[len(hashes)/2] 699 700 low, high := len(hashes)/2+maxUncleDist+1, len(hashes)/2-maxQueueDist-1 701 702 // Create a tester and simulate a head block being the middle of the above chain 703 tester := newTester(light) 704 705 tester.lock.Lock() 706 tester.hashes = []common.Hash{head} 707 tester.headers = map[common.Hash]*types.Header{head: blocks[head].Header()} 708 tester.blocks = map[common.Hash]*types.Block{head: blocks[head]} 709 tester.lock.Unlock() 710 711 headerFetcher := tester.makeHeaderFetcher("lower", blocks, -gatherSlack) 712 bodyFetcher := tester.makeBodyFetcher("lower", blocks, 0) 713 714 fetching := make(chan struct{}, 2) 715 tester.fetcher.fetchingHook = func(hashes []common.Hash) { fetching <- struct{}{} } 716 717 // Ensure that a block with a lower number than the threshold is discarded 718 tester.fetcher.Notify("lower", hashes[low], blocks[hashes[low]].NumberU64(), time.Now().Add(-arriveTimeout), headerFetcher, bodyFetcher) 719 select { 720 case <-time.After(50 * time.Millisecond): 721 case <-fetching: 722 t.Fatalf("fetcher requested stale header") 723 } 724 // Ensure that a block with a higher number than the threshold is discarded 725 tester.fetcher.Notify("higher", hashes[high], blocks[hashes[high]].NumberU64(), time.Now().Add(-arriveTimeout), headerFetcher, bodyFetcher) 726 select { 727 case <-time.After(50 * time.Millisecond): 728 case <-fetching: 729 t.Fatalf("fetcher requested future header") 730 } 731 } 732 733 // Tests that peers announcing blocks with invalid numbers (i.e. not matching 734 // the headers provided afterwards) get dropped as malicious. 735 func TestFullInvalidNumberAnnouncement(t *testing.T) { testInvalidNumberAnnouncement(t, false) } 736 func TestLightInvalidNumberAnnouncement(t *testing.T) { testInvalidNumberAnnouncement(t, true) } 737 738 func testInvalidNumberAnnouncement(t *testing.T, light bool) { 739 // Create a single block to import and check numbers against 740 hashes, blocks := makeChain(1, 0, genesis) 741 742 tester := newTester(light) 743 badHeaderFetcher := tester.makeHeaderFetcher("bad", blocks, -gatherSlack) 744 badBodyFetcher := tester.makeBodyFetcher("bad", blocks, 0) 745 746 imported := make(chan interface{}) 747 announced := make(chan interface{}, 2) 748 tester.fetcher.importedHook = func(header *types.Header, block *types.Block) { 749 if light { 750 if header == nil { 751 t.Fatalf("Fetcher try to import empty header") 752 } 753 imported <- header 754 } else { 755 if block == nil { 756 t.Fatalf("Fetcher try to import empty block") 757 } 758 imported <- block 759 } 760 } 761 // Announce a block with a bad number, check for immediate drop 762 tester.fetcher.announceChangeHook = func(hash common.Hash, b bool) { 763 announced <- nil 764 } 765 tester.fetcher.Notify("bad", hashes[0], 2, time.Now().Add(-arriveTimeout), badHeaderFetcher, badBodyFetcher) 766 verifyAnnounce := func() { 767 for i := 0; i < 2; i++ { 768 select { 769 case <-announced: 770 continue 771 case <-time.After(1 * time.Second): 772 t.Fatal("announce timeout") 773 return 774 } 775 } 776 } 777 verifyAnnounce() 778 verifyImportEvent(t, imported, false) 779 tester.lock.RLock() 780 dropped := tester.drops["bad"] 781 tester.lock.RUnlock() 782 783 if !dropped { 784 t.Fatalf("peer with invalid numbered announcement not dropped") 785 } 786 goodHeaderFetcher := tester.makeHeaderFetcher("good", blocks, -gatherSlack) 787 goodBodyFetcher := tester.makeBodyFetcher("good", blocks, 0) 788 // Make sure a good announcement passes without a drop 789 tester.fetcher.Notify("good", hashes[0], 1, time.Now().Add(-arriveTimeout), goodHeaderFetcher, goodBodyFetcher) 790 verifyAnnounce() 791 verifyImportEvent(t, imported, true) 792 793 tester.lock.RLock() 794 dropped = tester.drops["good"] 795 tester.lock.RUnlock() 796 797 if dropped { 798 t.Fatalf("peer with valid numbered announcement dropped") 799 } 800 verifyImportDone(t, imported) 801 } 802 803 // Tests that if a block is empty (i.e. header only), no body request should be 804 // made, and instead the header should be assembled into a whole block in itself. 805 func TestEmptyBlockShortCircuit(t *testing.T) { 806 // Create a chain of blocks to import 807 hashes, blocks := makeChain(32, 0, genesis) 808 809 tester := newTester(false) 810 defer tester.fetcher.Stop() 811 headerFetcher := tester.makeHeaderFetcher("valid", blocks, -gatherSlack) 812 bodyFetcher := tester.makeBodyFetcher("valid", blocks, 0) 813 814 // Add a monitoring hook for all internal events 815 fetching := make(chan []common.Hash) 816 tester.fetcher.fetchingHook = func(hashes []common.Hash) { fetching <- hashes } 817 818 completing := make(chan []common.Hash) 819 tester.fetcher.completingHook = func(hashes []common.Hash) { completing <- hashes } 820 821 imported := make(chan interface{}) 822 tester.fetcher.importedHook = func(header *types.Header, block *types.Block) { 823 if block == nil { 824 t.Fatalf("Fetcher try to import empty block") 825 } 826 imported <- block 827 } 828 // Iteratively announce blocks until all are imported 829 for i := len(hashes) - 2; i >= 0; i-- { 830 tester.fetcher.Notify("valid", hashes[i], uint64(len(hashes)-i-1), time.Now().Add(-arriveTimeout), headerFetcher, bodyFetcher) 831 832 // All announces should fetch the header 833 verifyFetchingEvent(t, fetching, true) 834 835 // Only blocks with data contents should request bodies 836 verifyCompletingEvent(t, completing, len(blocks[hashes[i]].Transactions()) > 0 || len(blocks[hashes[i]].Uncles()) > 0) 837 838 // Irrelevant of the construct, import should succeed 839 verifyImportEvent(t, imported, true) 840 } 841 verifyImportDone(t, imported) 842 } 843 844 // Tests that a peer is unable to use unbounded memory with sending infinite 845 // block announcements to a node, but that even in the face of such an attack, 846 // the fetcher remains operational. 847 func TestHashMemoryExhaustionAttack(t *testing.T) { 848 // Create a tester with instrumented import hooks 849 tester := newTester(false) 850 851 imported, announces := make(chan interface{}), int32(0) 852 tester.fetcher.importedHook = func(header *types.Header, block *types.Block) { imported <- block } 853 tester.fetcher.announceChangeHook = func(hash common.Hash, added bool) { 854 if added { 855 atomic.AddInt32(&announces, 1) 856 } else { 857 atomic.AddInt32(&announces, -1) 858 } 859 } 860 // Create a valid chain and an infinite junk chain 861 targetBlocks := hashLimit + 2*maxQueueDist 862 hashes, blocks := makeChain(targetBlocks, 0, genesis) 863 validHeaderFetcher := tester.makeHeaderFetcher("valid", blocks, -gatherSlack) 864 validBodyFetcher := tester.makeBodyFetcher("valid", blocks, 0) 865 866 attack, _ := makeChain(targetBlocks, 0, unknownBlock) 867 attackerHeaderFetcher := tester.makeHeaderFetcher("attacker", nil, -gatherSlack) 868 attackerBodyFetcher := tester.makeBodyFetcher("attacker", nil, 0) 869 870 // Feed the tester a huge hashset from the attacker, and a limited from the valid peer 871 for i := 0; i < len(attack); i++ { 872 if i < maxQueueDist { 873 tester.fetcher.Notify("valid", hashes[len(hashes)-2-i], uint64(i+1), time.Now(), validHeaderFetcher, validBodyFetcher) 874 } 875 tester.fetcher.Notify("attacker", attack[i], 1 /* don't distance drop */, time.Now(), attackerHeaderFetcher, attackerBodyFetcher) 876 } 877 if count := atomic.LoadInt32(&announces); count != hashLimit+maxQueueDist { 878 t.Fatalf("queued announce count mismatch: have %d, want %d", count, hashLimit+maxQueueDist) 879 } 880 // Wait for fetches to complete 881 verifyImportCount(t, imported, maxQueueDist) 882 883 // Feed the remaining valid hashes to ensure DOS protection state remains clean 884 for i := len(hashes) - maxQueueDist - 2; i >= 0; i-- { 885 tester.fetcher.Notify("valid", hashes[i], uint64(len(hashes)-i-1), time.Now().Add(-arriveTimeout), validHeaderFetcher, validBodyFetcher) 886 verifyImportEvent(t, imported, true) 887 } 888 verifyImportDone(t, imported) 889 } 890 891 // Tests that blocks sent to the fetcher (either through propagation or via hash 892 // announces and retrievals) don't pile up indefinitely, exhausting available 893 // system memory. 894 func TestBlockMemoryExhaustionAttack(t *testing.T) { 895 // Create a tester with instrumented import hooks 896 tester := newTester(false) 897 898 imported, enqueued := make(chan interface{}), int32(0) 899 tester.fetcher.importedHook = func(header *types.Header, block *types.Block) { imported <- block } 900 tester.fetcher.queueChangeHook = func(hash common.Hash, added bool) { 901 if added { 902 atomic.AddInt32(&enqueued, 1) 903 } else { 904 atomic.AddInt32(&enqueued, -1) 905 } 906 } 907 // Create a valid chain and a batch of dangling (but in range) blocks 908 targetBlocks := hashLimit + 2*maxQueueDist 909 hashes, blocks := makeChain(targetBlocks, 0, genesis) 910 attack := make(map[common.Hash]*types.Block) 911 for i := byte(0); len(attack) < blockLimit+2*maxQueueDist; i++ { 912 hashes, blocks := makeChain(maxQueueDist-1, i, unknownBlock) 913 for _, hash := range hashes[:maxQueueDist-2] { 914 attack[hash] = blocks[hash] 915 } 916 } 917 // Try to feed all the attacker blocks make sure only a limited batch is accepted 918 for _, block := range attack { 919 tester.fetcher.Enqueue("attacker", block) 920 } 921 time.Sleep(200 * time.Millisecond) 922 if queued := atomic.LoadInt32(&enqueued); queued != blockLimit { 923 t.Fatalf("queued block count mismatch: have %d, want %d", queued, blockLimit) 924 } 925 // Queue up a batch of valid blocks, and check that a new peer is allowed to do so 926 for i := 0; i < maxQueueDist-1; i++ { 927 tester.fetcher.Enqueue("valid", blocks[hashes[len(hashes)-3-i]]) 928 } 929 time.Sleep(100 * time.Millisecond) 930 if queued := atomic.LoadInt32(&enqueued); queued != blockLimit+maxQueueDist-1 { 931 t.Fatalf("queued block count mismatch: have %d, want %d", queued, blockLimit+maxQueueDist-1) 932 } 933 // Insert the missing piece (and sanity check the import) 934 tester.fetcher.Enqueue("valid", blocks[hashes[len(hashes)-2]]) 935 verifyImportCount(t, imported, maxQueueDist) 936 937 // Insert the remaining blocks in chunks to ensure clean DOS protection 938 for i := maxQueueDist; i < len(hashes)-1; i++ { 939 tester.fetcher.Enqueue("valid", blocks[hashes[len(hashes)-2-i]]) 940 verifyImportEvent(t, imported, true) 941 } 942 verifyImportDone(t, imported) 943 }