Tests that a peer is unable to use unbounded memory with sending infinite block announcements to a node, but that even in the face of such an attack, the fetcher remains operational.
(t *testing.T)
| 641 | // block announcements to a node, but that even in the face of such an attack, |
| 642 | // the fetcher remains operational. |
| 643 | func TestHashMemoryExhaustionAttack(t *testing.T) { |
| 644 | // Create a tester with instrumented import hooks |
| 645 | tester := newTester() |
| 646 | |
| 647 | imported, announces := make(chan *types.Block), int32(0) |
| 648 | tester.fetcher.importedHook = func(block *types.Block) { imported <- block } |
| 649 | tester.fetcher.announceChangeHook = func(hash common.Hash, added bool) { |
| 650 | if added { |
| 651 | atomic.AddInt32(&announces, 1) |
| 652 | } else { |
| 653 | atomic.AddInt32(&announces, -1) |
| 654 | } |
| 655 | } |
| 656 | // Create a valid chain and an infinite junk chain |
| 657 | targetBlocks := hashLimit + 2*maxQueueDist |
| 658 | hashes, blocks := makeChain(targetBlocks, 0, genesis) |
| 659 | validHeaderFetcher := tester.makeHeaderFetcher("valid", blocks, -gatherSlack) |
| 660 | validBodyFetcher := tester.makeBodyFetcher("valid", blocks, 0) |
| 661 | |
| 662 | attack, _ := makeChain(targetBlocks, 0, unknownBlock) |
| 663 | attackerHeaderFetcher := tester.makeHeaderFetcher("attacker", nil, -gatherSlack) |
| 664 | attackerBodyFetcher := tester.makeBodyFetcher("attacker", nil, 0) |
| 665 | |
| 666 | // Feed the tester a huge hashset from the attacker, and a limited from the valid peer |
| 667 | for i := 0; i < len(attack); i++ { |
| 668 | if i < maxQueueDist { |
| 669 | tester.fetcher.Notify("valid", hashes[len(hashes)-2-i], uint64(i+1), time.Now(), validHeaderFetcher, validBodyFetcher) |
| 670 | } |
| 671 | tester.fetcher.Notify("attacker", attack[i], 1 /* don't distance drop */, time.Now(), attackerHeaderFetcher, attackerBodyFetcher) |
| 672 | } |
| 673 | if count := atomic.LoadInt32(&announces); count != hashLimit+maxQueueDist { |
| 674 | t.Fatalf("queued announce count mismatch: have %d, want %d", count, hashLimit+maxQueueDist) |
| 675 | } |
| 676 | // Wait for fetches to complete |
| 677 | verifyImportCount(t, imported, maxQueueDist) |
| 678 | |
| 679 | // Feed the remaining valid hashes to ensure DOS protection state remains clean |
| 680 | for i := len(hashes) - maxQueueDist - 2; i >= 0; i-- { |
| 681 | tester.fetcher.Notify("valid", hashes[i], uint64(len(hashes)-i-1), time.Now().Add(-arriveTimeout), validHeaderFetcher, validBodyFetcher) |
| 682 | verifyImportEvent(t, imported, true) |
| 683 | } |
| 684 | verifyImportDone(t, imported) |
| 685 | } |
| 686 | |
| 687 | // Tests that blocks sent to the fetcher (either through propagation or via hash |
| 688 | // announces and retrievals) don't pile up indefinitely, exhausting available |
nothing calls this directly
no test coverage detected