@@ -149,22 +149,25 @@ type downloadTester struct {
149
149
peerReceipts map [string ]map [common.Hash ]types.Receipts // Receipts belonging to different test peers
150
150
peerChainTds map [string ]map [common.Hash ]* big.Int // Total difficulties of the blocks in the peer chains
151
151
152
+ peerMissingStates map [string ]map [common.Hash ]bool // State entries that fast sync should not return
153
+
152
154
lock sync.RWMutex
153
155
}
154
156
155
157
// newTester creates a new downloader test mocker.
156
158
func newTester () * downloadTester {
157
159
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 ),
168
171
}
169
172
tester .stateDb , _ = ethdb .NewMemDatabase ()
170
173
tester .stateDb .Put (genesis .Root ().Bytes (), []byte {0x00 })
@@ -408,6 +411,7 @@ func (dl *downloadTester) newSlowPeer(id string, version int, hashes []common.Ha
408
411
dl .peerBlocks [id ] = make (map [common.Hash ]* types.Block )
409
412
dl .peerReceipts [id ] = make (map [common.Hash ]types.Receipts )
410
413
dl .peerChainTds [id ] = make (map [common.Hash ]* big.Int )
414
+ dl .peerMissingStates [id ] = make (map [common.Hash ]bool )
411
415
412
416
genesis := hashes [len (hashes )- 1 ]
413
417
if header := headers [genesis ]; header != nil {
@@ -648,7 +652,9 @@ func (dl *downloadTester) peerGetNodeDataFn(id string, delay time.Duration) func
648
652
results := make ([][]byte , 0 , len (hashes ))
649
653
for _ , hash := range hashes {
650
654
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
+ }
652
658
}
653
659
}
654
660
go dl .downloader .DeliverNodeData (id , results )
@@ -1288,7 +1294,7 @@ func testInvalidHeaderRollback(t *testing.T, protocol int, mode SyncMode) {
1288
1294
tester .newPeer ("withhold-attack" , protocol , hashes , headers , blocks , receipts )
1289
1295
missing = 3 * fsHeaderSafetyNet + MaxHeaderFetch + 1
1290
1296
1291
- tester .downloader .noFast = false
1297
+ tester .downloader .fsPivotFails = 0
1292
1298
tester .downloader .syncInitHook = func (uint64 , uint64 ) {
1293
1299
for i := missing ; i <= len (hashes ); i ++ {
1294
1300
delete (tester .peerHeaders ["withhold-attack" ], hashes [len (hashes )- i ])
@@ -1307,6 +1313,8 @@ func testInvalidHeaderRollback(t *testing.T, protocol int, mode SyncMode) {
1307
1313
t .Errorf ("fast sync pivot block #%d not rolled back" , head )
1308
1314
}
1309
1315
}
1316
+ tester .downloader .fsPivotFails = fsCriticalTrials
1317
+
1310
1318
// Synchronise with the valid peer and make sure sync succeeds. Since the last
1311
1319
// rollback should also disable fast syncing for this process, verify that we
1312
1320
// 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) {
1749
1757
}
1750
1758
}
1751
1759
}
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