|
|
@@ -149,22 +149,25 @@ type downloadTester struct {
|
|
|
peerReceipts map[string]map[common.Hash]types.Receipts // Receipts belonging to different test peers
|
|
|
peerChainTds map[string]map[common.Hash]*big.Int // Total difficulties of the blocks in the peer chains
|
|
|
|
|
|
+ peerMissingStates map[string]map[common.Hash]bool // State entries that fast sync should not return
|
|
|
+
|
|
|
lock sync.RWMutex
|
|
|
}
|
|
|
|
|
|
// newTester creates a new downloader test mocker.
|
|
|
func newTester() *downloadTester {
|
|
|
tester := &downloadTester{
|
|
|
- ownHashes: []common.Hash{genesis.Hash()},
|
|
|
- ownHeaders: map[common.Hash]*types.Header{genesis.Hash(): genesis.Header()},
|
|
|
- ownBlocks: map[common.Hash]*types.Block{genesis.Hash(): genesis},
|
|
|
- ownReceipts: map[common.Hash]types.Receipts{genesis.Hash(): nil},
|
|
|
- ownChainTd: map[common.Hash]*big.Int{genesis.Hash(): genesis.Difficulty()},
|
|
|
- peerHashes: make(map[string][]common.Hash),
|
|
|
- peerHeaders: make(map[string]map[common.Hash]*types.Header),
|
|
|
- peerBlocks: make(map[string]map[common.Hash]*types.Block),
|
|
|
- peerReceipts: make(map[string]map[common.Hash]types.Receipts),
|
|
|
- peerChainTds: make(map[string]map[common.Hash]*big.Int),
|
|
|
+ ownHashes: []common.Hash{genesis.Hash()},
|
|
|
+ ownHeaders: map[common.Hash]*types.Header{genesis.Hash(): genesis.Header()},
|
|
|
+ ownBlocks: map[common.Hash]*types.Block{genesis.Hash(): genesis},
|
|
|
+ ownReceipts: map[common.Hash]types.Receipts{genesis.Hash(): nil},
|
|
|
+ ownChainTd: map[common.Hash]*big.Int{genesis.Hash(): genesis.Difficulty()},
|
|
|
+ peerHashes: make(map[string][]common.Hash),
|
|
|
+ peerHeaders: make(map[string]map[common.Hash]*types.Header),
|
|
|
+ peerBlocks: make(map[string]map[common.Hash]*types.Block),
|
|
|
+ peerReceipts: make(map[string]map[common.Hash]types.Receipts),
|
|
|
+ peerChainTds: make(map[string]map[common.Hash]*big.Int),
|
|
|
+ peerMissingStates: make(map[string]map[common.Hash]bool),
|
|
|
}
|
|
|
tester.stateDb, _ = ethdb.NewMemDatabase()
|
|
|
tester.stateDb.Put(genesis.Root().Bytes(), []byte{0x00})
|
|
|
@@ -408,6 +411,7 @@ func (dl *downloadTester) newSlowPeer(id string, version int, hashes []common.Ha
|
|
|
dl.peerBlocks[id] = make(map[common.Hash]*types.Block)
|
|
|
dl.peerReceipts[id] = make(map[common.Hash]types.Receipts)
|
|
|
dl.peerChainTds[id] = make(map[common.Hash]*big.Int)
|
|
|
+ dl.peerMissingStates[id] = make(map[common.Hash]bool)
|
|
|
|
|
|
genesis := hashes[len(hashes)-1]
|
|
|
if header := headers[genesis]; header != nil {
|
|
|
@@ -648,7 +652,9 @@ func (dl *downloadTester) peerGetNodeDataFn(id string, delay time.Duration) func
|
|
|
results := make([][]byte, 0, len(hashes))
|
|
|
for _, hash := range hashes {
|
|
|
if data, err := testdb.Get(hash.Bytes()); err == nil {
|
|
|
- results = append(results, data)
|
|
|
+ if !dl.peerMissingStates[id][hash] {
|
|
|
+ results = append(results, data)
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
go dl.downloader.DeliverNodeData(id, results)
|
|
|
@@ -1288,7 +1294,7 @@ func testInvalidHeaderRollback(t *testing.T, protocol int, mode SyncMode) {
|
|
|
tester.newPeer("withhold-attack", protocol, hashes, headers, blocks, receipts)
|
|
|
missing = 3*fsHeaderSafetyNet + MaxHeaderFetch + 1
|
|
|
|
|
|
- tester.downloader.noFast = false
|
|
|
+ tester.downloader.fsPivotFails = 0
|
|
|
tester.downloader.syncInitHook = func(uint64, uint64) {
|
|
|
for i := missing; i <= len(hashes); i++ {
|
|
|
delete(tester.peerHeaders["withhold-attack"], hashes[len(hashes)-i])
|
|
|
@@ -1307,6 +1313,8 @@ func testInvalidHeaderRollback(t *testing.T, protocol int, mode SyncMode) {
|
|
|
t.Errorf("fast sync pivot block #%d not rolled back", head)
|
|
|
}
|
|
|
}
|
|
|
+ tester.downloader.fsPivotFails = fsCriticalTrials
|
|
|
+
|
|
|
// Synchronise with the valid peer and make sure sync succeeds. Since the last
|
|
|
// rollback should also disable fast syncing for this process, verify that we
|
|
|
// 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) {
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+// Tests that if fast sync aborts in the critical section, it can restart a few
|
|
|
+// times before giving up.
|
|
|
+func TestFastCriticalRestarts63(t *testing.T) { testFastCriticalRestarts(t, 63) }
|
|
|
+func TestFastCriticalRestarts64(t *testing.T) { testFastCriticalRestarts(t, 64) }
|
|
|
+
|
|
|
+func testFastCriticalRestarts(t *testing.T, protocol int) {
|
|
|
+ t.Parallel()
|
|
|
+
|
|
|
+ // Create a large enough blockchin to actually fast sync on
|
|
|
+ targetBlocks := fsMinFullBlocks + 2*fsPivotInterval - 15
|
|
|
+ hashes, headers, blocks, receipts := makeChain(targetBlocks, 0, genesis, nil, false)
|
|
|
+
|
|
|
+ // Create a tester peer with the critical section state roots missing (force failures)
|
|
|
+ tester := newTester()
|
|
|
+ tester.newPeer("peer", protocol, hashes, headers, blocks, receipts)
|
|
|
+
|
|
|
+ for i := 0; i < fsPivotInterval; i++ {
|
|
|
+ tester.peerMissingStates["peer"][headers[hashes[fsMinFullBlocks+i]].Root] = true
|
|
|
+ }
|
|
|
+ // Synchronise with the peer a few times and make sure they fail until the retry limit
|
|
|
+ for i := 0; i < fsCriticalTrials; i++ {
|
|
|
+ // Attempt a sync and ensure it fails properly
|
|
|
+ if err := tester.sync("peer", nil, FastSync); err == nil {
|
|
|
+ t.Fatalf("failing fast sync succeeded: %v", err)
|
|
|
+ }
|
|
|
+ // If it's the first failure, pivot should be locked => reenable all others to detect pivot changes
|
|
|
+ if i == 0 {
|
|
|
+ tester.peerMissingStates["peer"] = map[common.Hash]bool{tester.downloader.fsPivotLock.Root: true}
|
|
|
+ }
|
|
|
+ time.Sleep(100 * time.Millisecond) // Make sure no in-flight requests remain
|
|
|
+ }
|
|
|
+ // Retry limit exhausted, downloader will switch to full sync, should succeed
|
|
|
+ if err := tester.sync("peer", nil, FastSync); err != nil {
|
|
|
+ t.Fatalf("failed to synchronise blocks in slow sync: %v", err)
|
|
|
+ }
|
|
|
+ assertOwnChain(t, tester, targetBlocks+1)
|
|
|
+}
|