Handles case when no peers with finalized blocks are found (#6679)

* Handles case when no peers with finalized blocks are found
* more tests
* Merge branch 'master' into handle-zero-block-init-sync
* Merge branch 'master' into handle-zero-block-init-sync
This commit is contained in:
Victor Farazdagi 2020-07-22 21:14:24 +03:00 committed by GitHub
parent 17f845dcb0
commit 4400321081
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 62 additions and 9 deletions

View File

@ -461,7 +461,16 @@ func (f *blocksFetcher) nonSkippedSlotAfter(ctx context.Context, slot uint64) (u
defer span.End()
headEpoch := helpers.SlotToEpoch(f.headFetcher.HeadSlot())
epoch, peers := f.p2p.Peers().BestFinalized(params.BeaconConfig().MaxPeersToSync, headEpoch)
finalizedEpoch, peers := f.p2p.Peers().BestFinalized(params.BeaconConfig().MaxPeersToSync, headEpoch)
log.WithFields(logrus.Fields{
"start": slot,
"headEpoch": headEpoch,
"finalizedEpoch": finalizedEpoch,
}).Debug("Searching for non-skipped slot")
// Exit early, if no peers with high enough finalized epoch are found.
if finalizedEpoch <= headEpoch {
return 0, errSlotIsTooHigh
}
var err error
peers, err = f.filterPeers(peers, peersPercentagePerRequest)
if err != nil {
@ -511,7 +520,7 @@ func (f *blocksFetcher) nonSkippedSlotAfter(ctx context.Context, slot uint64) (u
// Quickly find the close enough epoch where a non-empty slot definitely exists.
// Only single random slot per epoch is checked - allowing to move forward relatively quickly.
slot = slot + nonSkippedSlotsFullSearchEpochs*slotsPerEpoch
upperBoundSlot := helpers.StartSlot(epoch + 1)
upperBoundSlot := helpers.StartSlot(finalizedEpoch + 1)
for ind := slot + 1; ind < upperBoundSlot; ind += (slotsPerEpoch * slotsPerEpoch) / 2 {
start := ind + uint64(f.rand.Intn(int(slotsPerEpoch)))
nextSlot, err := fetch(peers[pidInd%len(peers)], start, slotsPerEpoch/2, slotsPerEpoch)
@ -534,7 +543,7 @@ func (f *blocksFetcher) nonSkippedSlotAfter(ctx context.Context, slot uint64) (u
if err != nil {
return 0, err
}
if nextSlot < slot || helpers.StartSlot(epoch+1) < nextSlot {
if nextSlot < slot || helpers.StartSlot(finalizedEpoch+1) < nextSlot {
return 0, errors.New("invalid range for non-skipped slot")
}
return nextSlot, nil

View File

@ -29,6 +29,7 @@ var (
errQueueTakesTooLongToStop = errors.New("queue takes too long to stop")
errInvalidInitialState = errors.New("invalid initial state")
errInputNotFetchRequestParams = errors.New("input data is not type *fetchRequestParams")
errNoPeersWithFinalizedBlocks = errors.New("no peers with finalized blocks are found")
)
// blocksQueueConfig is a config to setup block queue service.
@ -151,11 +152,16 @@ func (q *blocksQueue) loop() {
fsm := q.smm.machines[key]
if err := fsm.trigger(eventTick, nil); err != nil {
log.WithFields(logrus.Fields{
"event": eventTick,
"epoch": helpers.SlotToEpoch(fsm.start),
"start": fsm.start,
"error": err.Error(),
"highestExpectedSlot": q.highestExpectedSlot,
"event": eventTick,
"epoch": helpers.SlotToEpoch(fsm.start),
"start": fsm.start,
"error": err.Error(),
}).Debug("Can not trigger event")
if err == errNoPeersWithFinalizedBlocks {
q.cancel()
continue
}
}
// Do garbage collection, and advance sliding window forward.
if q.headFetcher.HeadSlot() >= fsm.start+blocksPerRequest-1 {
@ -315,6 +321,11 @@ func (q *blocksQueue) onProcessSkippedEvent(ctx context.Context) eventHandlerFn
return m.state, nil
}
// Check if we have enough peers to progress, or sync needs to halt (due to no peers available).
if q.blocksFetcher.bestFinalizedSlot() <= q.headFetcher.HeadSlot() {
return stateSkipped, errNoPeersWithFinalizedBlocks
}
// Shift start position of all the machines except for the last one.
startSlot := q.headFetcher.HeadSlot() + 1
blocksPerRequest := q.blocksFetcher.blocksPerSecond
@ -326,8 +337,7 @@ func (q *blocksQueue) onProcessSkippedEvent(ctx context.Context) eventHandlerFn
}
// Replace the last (currently activated) state machine.
nonSkippedSlot, err := q.blocksFetcher.nonSkippedSlotAfter(
ctx, startSlot+blocksPerRequest*(lookaheadSteps-1)-1)
nonSkippedSlot, err := q.blocksFetcher.nonSkippedSlotAfter(ctx, startSlot+blocksPerRequest*(lookaheadSteps-1)-1)
if err != nil {
return stateSkipped, err
}

View File

@ -32,6 +32,40 @@ func TestService_roundRobinSync(t *testing.T) {
expectedBlockSlots []uint64
peers []*peerData
}{
{
name: "Single peer with no finalized blocks",
currentSlot: 2,
expectedBlockSlots: makeSequence(1, 2),
peers: []*peerData{
{
blocks: makeSequence(1, 2),
finalizedEpoch: 0,
headSlot: 2,
},
},
},
{
name: "Multiple peers with no finalized blocks",
currentSlot: 2,
expectedBlockSlots: makeSequence(1, 2),
peers: []*peerData{
{
blocks: makeSequence(1, 2),
finalizedEpoch: 0,
headSlot: 2,
},
{
blocks: makeSequence(1, 2),
finalizedEpoch: 0,
headSlot: 2,
},
{
blocks: makeSequence(1, 2),
finalizedEpoch: 0,
headSlot: 2,
},
},
},
{
name: "Single peer with all blocks",
currentSlot: 131,