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