@@ -149,22 +149,25 @@ type downloadTester struct {
149149 peerReceipts map [string ]map [common.Hash ]types.Receipts // Receipts belonging to different test peers
150150 peerChainTds map [string ]map [common.Hash ]* big.Int // Total difficulties of the blocks in the peer chains
151151
152+ peerMissingStates map [string ]map [common.Hash ]bool // State entries that fast sync should not return
153+
152154 lock sync.RWMutex
153155}
154156
155157// newTester creates a new downloader test mocker.
156158func newTester () * downloadTester {
157159 tester := & downloadTester {
158- ownHashes : []common.Hash {genesis .Hash ()},
159- ownHeaders : map [common.Hash ]* types.Header {genesis .Hash (): genesis .Header ()},
160- ownBlocks : map [common.Hash ]* types.Block {genesis .Hash (): genesis },
161- ownReceipts : map [common.Hash ]types.Receipts {genesis .Hash (): nil },
162- ownChainTd : map [common.Hash ]* big.Int {genesis .Hash (): genesis .Difficulty ()},
163- peerHashes : make (map [string ][]common.Hash ),
164- peerHeaders : make (map [string ]map [common.Hash ]* types.Header ),
165- peerBlocks : make (map [string ]map [common.Hash ]* types.Block ),
166- peerReceipts : make (map [string ]map [common.Hash ]types.Receipts ),
167- peerChainTds : make (map [string ]map [common.Hash ]* big.Int ),
160+ ownHashes : []common.Hash {genesis .Hash ()},
161+ ownHeaders : map [common.Hash ]* types.Header {genesis .Hash (): genesis .Header ()},
162+ ownBlocks : map [common.Hash ]* types.Block {genesis .Hash (): genesis },
163+ ownReceipts : map [common.Hash ]types.Receipts {genesis .Hash (): nil },
164+ ownChainTd : map [common.Hash ]* big.Int {genesis .Hash (): genesis .Difficulty ()},
165+ peerHashes : make (map [string ][]common.Hash ),
166+ peerHeaders : make (map [string ]map [common.Hash ]* types.Header ),
167+ peerBlocks : make (map [string ]map [common.Hash ]* types.Block ),
168+ peerReceipts : make (map [string ]map [common.Hash ]types.Receipts ),
169+ peerChainTds : make (map [string ]map [common.Hash ]* big.Int ),
170+ peerMissingStates : make (map [string ]map [common.Hash ]bool ),
168171 }
169172 tester .stateDb , _ = ethdb .NewMemDatabase ()
170173 tester .stateDb .Put (genesis .Root ().Bytes (), []byte {0x00 })
@@ -408,6 +411,7 @@ func (dl *downloadTester) newSlowPeer(id string, version int, hashes []common.Ha
408411 dl .peerBlocks [id ] = make (map [common.Hash ]* types.Block )
409412 dl .peerReceipts [id ] = make (map [common.Hash ]types.Receipts )
410413 dl .peerChainTds [id ] = make (map [common.Hash ]* big.Int )
414+ dl .peerMissingStates [id ] = make (map [common.Hash ]bool )
411415
412416 genesis := hashes [len (hashes )- 1 ]
413417 if header := headers [genesis ]; header != nil {
@@ -648,7 +652,9 @@ func (dl *downloadTester) peerGetNodeDataFn(id string, delay time.Duration) func
648652 results := make ([][]byte , 0 , len (hashes ))
649653 for _ , hash := range hashes {
650654 if data , err := testdb .Get (hash .Bytes ()); err == nil {
651- results = append (results , data )
655+ if ! dl.peerMissingStates [id ][hash ] {
656+ results = append (results , data )
657+ }
652658 }
653659 }
654660 go dl .downloader .DeliverNodeData (id , results )
@@ -1288,7 +1294,7 @@ func testInvalidHeaderRollback(t *testing.T, protocol int, mode SyncMode) {
12881294 tester .newPeer ("withhold-attack" , protocol , hashes , headers , blocks , receipts )
12891295 missing = 3 * fsHeaderSafetyNet + MaxHeaderFetch + 1
12901296
1291- tester .downloader .noFast = false
1297+ tester .downloader .fsPivotFails = 0
12921298 tester .downloader .syncInitHook = func (uint64 , uint64 ) {
12931299 for i := missing ; i <= len (hashes ); i ++ {
12941300 delete (tester .peerHeaders ["withhold-attack" ], hashes [len (hashes )- i ])
@@ -1307,6 +1313,8 @@ func testInvalidHeaderRollback(t *testing.T, protocol int, mode SyncMode) {
13071313 t .Errorf ("fast sync pivot block #%d not rolled back" , head )
13081314 }
13091315 }
1316+ tester .downloader .fsPivotFails = fsCriticalTrials
1317+
13101318 // Synchronise with the valid peer and make sure sync succeeds. Since the last
13111319 // rollback should also disable fast syncing for this process, verify that we
13121320 // did a fresh full sync. Note, we can't assert anything about the receipts
@@ -1749,3 +1757,41 @@ func testDeliverHeadersHang(t *testing.T, protocol int, mode SyncMode) {
17491757 }
17501758 }
17511759}
1760+
1761+ // Tests that if fast sync aborts in the critical section, it can restart a few
1762+ // times before giving up.
1763+ func TestFastCriticalRestarts63 (t * testing.T ) { testFastCriticalRestarts (t , 63 ) }
1764+ func TestFastCriticalRestarts64 (t * testing.T ) { testFastCriticalRestarts (t , 64 ) }
1765+
1766+ func testFastCriticalRestarts (t * testing.T , protocol int ) {
1767+ t .Parallel ()
1768+
1769+ // Create a large enough blockchin to actually fast sync on
1770+ targetBlocks := fsMinFullBlocks + 2 * fsPivotInterval - 15
1771+ hashes , headers , blocks , receipts := makeChain (targetBlocks , 0 , genesis , nil , false )
1772+
1773+ // Create a tester peer with the critical section state roots missing (force failures)
1774+ tester := newTester ()
1775+ tester .newPeer ("peer" , protocol , hashes , headers , blocks , receipts )
1776+
1777+ for i := 0 ; i < fsPivotInterval ; i ++ {
1778+ tester .peerMissingStates ["peer" ][headers [hashes [fsMinFullBlocks + i ]].Root ] = true
1779+ }
1780+ // Synchronise with the peer a few times and make sure they fail until the retry limit
1781+ for i := 0 ; i < fsCriticalTrials ; i ++ {
1782+ // Attempt a sync and ensure it fails properly
1783+ if err := tester .sync ("peer" , nil , FastSync ); err == nil {
1784+ t .Fatalf ("failing fast sync succeeded: %v" , err )
1785+ }
1786+ // If it's the first failure, pivot should be locked => reenable all others to detect pivot changes
1787+ if i == 0 {
1788+ tester .peerMissingStates ["peer" ] = map [common.Hash ]bool {tester .downloader .fsPivotLock .Root : true }
1789+ }
1790+ time .Sleep (100 * time .Millisecond ) // Make sure no in-flight requests remain
1791+ }
1792+ // Retry limit exhausted, downloader will switch to full sync, should succeed
1793+ if err := tester .sync ("peer" , nil , FastSync ); err != nil {
1794+ t .Fatalf ("failed to synchronise blocks in slow sync: %v" , err )
1795+ }
1796+ assertOwnChain (t , tester , targetBlocks + 1 )
1797+ }
0 commit comments