2020-08-11 15:15:18 +00:00
|
|
|
package initialsync
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2020-11-05 07:27:46 +00:00
|
|
|
"fmt"
|
2020-08-11 15:15:18 +00:00
|
|
|
|
2022-10-07 07:24:51 +00:00
|
|
|
"github.com/libp2p/go-libp2p/core/peer"
|
2020-08-11 15:15:18 +00:00
|
|
|
"github.com/pkg/errors"
|
2024-02-15 05:46:47 +00:00
|
|
|
p2pTypes "github.com/prysmaticlabs/prysm/v5/beacon-chain/p2p/types"
|
|
|
|
"github.com/prysmaticlabs/prysm/v5/cmd/beacon-chain/flags"
|
|
|
|
"github.com/prysmaticlabs/prysm/v5/config/params"
|
|
|
|
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
|
|
|
|
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
|
|
|
|
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
|
|
|
|
p2ppb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
|
|
|
|
"github.com/prysmaticlabs/prysm/v5/time/slots"
|
2020-08-11 15:15:18 +00:00
|
|
|
"github.com/sirupsen/logrus"
|
|
|
|
"go.opencensus.io/trace"
|
|
|
|
)
|
|
|
|
|
2020-11-05 07:27:46 +00:00
|
|
|
// forkData represents alternative chain path supported by a given peer.
|
|
|
|
// Blocks are stored in an ascending slot order. The first block is guaranteed to have parent
|
|
|
|
// either in DB or initial sync cache.
|
|
|
|
type forkData struct {
|
2023-07-25 21:57:01 +00:00
|
|
|
peer peer.ID
|
2024-01-06 23:47:09 +00:00
|
|
|
bwb []blocks.BlockWithROBlobs
|
2020-11-05 07:27:46 +00:00
|
|
|
}
|
|
|
|
|
2020-08-11 15:15:18 +00:00
|
|
|
// nonSkippedSlotAfter checks slots after the given one in an attempt to find a non-empty future slot.
|
|
|
|
// For efficiency only one random slot is checked per epoch, so returned slot might not be the first
|
|
|
|
// non-skipped slot. This shouldn't be a problem, as in case of adversary peer, we might get incorrect
|
|
|
|
// data anyway, so code that relies on this function must be robust enough to re-request, if no progress
|
|
|
|
// is possible with a returned value.
|
2023-01-26 14:40:12 +00:00
|
|
|
func (f *blocksFetcher) nonSkippedSlotAfter(ctx context.Context, slot primitives.Slot) (primitives.Slot, error) {
|
2020-08-11 15:15:18 +00:00
|
|
|
ctx, span := trace.StartSpan(ctx, "initialsync.nonSkippedSlotAfter")
|
|
|
|
defer span.End()
|
|
|
|
|
2020-10-31 21:33:57 +00:00
|
|
|
headEpoch, targetEpoch, peers := f.calculateHeadAndTargetEpochs()
|
2020-08-11 15:15:18 +00:00
|
|
|
log.WithFields(logrus.Fields{
|
2020-08-18 02:10:45 +00:00
|
|
|
"start": slot,
|
|
|
|
"headEpoch": headEpoch,
|
|
|
|
"targetEpoch": targetEpoch,
|
2020-08-11 15:15:18 +00:00
|
|
|
}).Debug("Searching for non-skipped slot")
|
2020-10-31 21:33:57 +00:00
|
|
|
|
|
|
|
// Exit early if no peers with epoch higher than our known head are found.
|
2020-08-18 02:10:45 +00:00
|
|
|
if targetEpoch <= headEpoch {
|
2020-08-11 15:15:18 +00:00
|
|
|
return 0, errSlotIsTooHigh
|
|
|
|
}
|
2020-10-31 21:33:57 +00:00
|
|
|
|
|
|
|
// Transform peer list to avoid eclipsing (filter, shuffle, trim).
|
2020-11-04 23:29:50 +00:00
|
|
|
peers = f.filterPeers(ctx, peers, peersPercentagePerRequest)
|
2020-11-05 07:27:46 +00:00
|
|
|
return f.nonSkippedSlotAfterWithPeersTarget(ctx, slot, peers, targetEpoch)
|
|
|
|
}
|
|
|
|
|
|
|
|
// nonSkippedSlotWithPeersTarget traverse peers (supporting a given target epoch), in an attempt
|
|
|
|
// to find non-skipped slot among returned blocks.
|
|
|
|
func (f *blocksFetcher) nonSkippedSlotAfterWithPeersTarget(
|
2023-01-26 14:40:12 +00:00
|
|
|
ctx context.Context, slot primitives.Slot, peers []peer.ID, targetEpoch primitives.Epoch,
|
|
|
|
) (primitives.Slot, error) {
|
2020-11-05 07:27:46 +00:00
|
|
|
// Exit early if no peers are ready.
|
2020-08-11 15:15:18 +00:00
|
|
|
if len(peers) == 0 {
|
|
|
|
return 0, errNoPeersAvailable
|
|
|
|
}
|
|
|
|
|
|
|
|
slotsPerEpoch := params.BeaconConfig().SlotsPerEpoch
|
|
|
|
pidInd := 0
|
|
|
|
|
2023-01-26 14:40:12 +00:00
|
|
|
fetch := func(pid peer.ID, start primitives.Slot, count, step uint64) (primitives.Slot, error) {
|
2020-08-11 15:15:18 +00:00
|
|
|
req := &p2ppb.BeaconBlocksByRangeRequest{
|
|
|
|
StartSlot: start,
|
|
|
|
Count: count,
|
|
|
|
Step: step,
|
|
|
|
}
|
|
|
|
blocks, err := f.requestBlocks(ctx, req, pid)
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
if len(blocks) > 0 {
|
|
|
|
for _, block := range blocks {
|
2021-05-26 16:19:54 +00:00
|
|
|
if block.Block().Slot() > slot {
|
|
|
|
return block.Block().Slot(), nil
|
2020-08-11 15:15:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Start by checking several epochs fully, w/o resorting to random sampling.
|
|
|
|
start := slot + 1
|
|
|
|
end := start + nonSkippedSlotsFullSearchEpochs*slotsPerEpoch
|
|
|
|
for ind := start; ind < end; ind += slotsPerEpoch {
|
2021-02-16 07:45:34 +00:00
|
|
|
nextSlot, err := fetch(peers[pidInd%len(peers)], ind, uint64(slotsPerEpoch), 1)
|
2020-08-11 15:15:18 +00:00
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
if nextSlot > slot {
|
|
|
|
return nextSlot, nil
|
|
|
|
}
|
|
|
|
pidInd++
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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.
|
2022-06-23 21:48:28 +00:00
|
|
|
// This method has been changed to account for our spec change where step can only be 1 in a
|
|
|
|
// block by range request. https://github.com/ethereum/consensus-specs/pull/2856
|
|
|
|
// The downside is that this method will be less effective during periods without
|
|
|
|
// finality.
|
2020-12-18 14:31:15 +00:00
|
|
|
slot += nonSkippedSlotsFullSearchEpochs * slotsPerEpoch
|
2021-10-01 20:17:57 +00:00
|
|
|
upperBoundSlot, err := slots.EpochStart(targetEpoch + 1)
|
2020-09-02 02:52:36 +00:00
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
2022-06-23 21:48:28 +00:00
|
|
|
for ind := slot + 1; ind < upperBoundSlot; ind += slotsPerEpoch {
|
|
|
|
nextSlot, err := fetch(peers[pidInd%len(peers)], ind, uint64(slotsPerEpoch), 1)
|
2020-08-11 15:15:18 +00:00
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
pidInd++
|
|
|
|
if nextSlot > slot && upperBoundSlot >= nextSlot {
|
|
|
|
upperBoundSlot = nextSlot
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Epoch with non-empty slot is located. Check all slots within two nearby epochs.
|
|
|
|
if upperBoundSlot > slotsPerEpoch {
|
|
|
|
upperBoundSlot -= slotsPerEpoch
|
|
|
|
}
|
2021-10-01 20:17:57 +00:00
|
|
|
upperBoundSlot, err = slots.EpochStart(slots.ToEpoch(upperBoundSlot))
|
2020-09-02 02:52:36 +00:00
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
2021-02-16 07:45:34 +00:00
|
|
|
nextSlot, err := fetch(peers[pidInd%len(peers)], upperBoundSlot, uint64(slotsPerEpoch*2), 1)
|
2020-08-11 15:15:18 +00:00
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
2021-10-01 20:17:57 +00:00
|
|
|
s, err := slots.EpochStart(targetEpoch + 1)
|
2020-09-02 02:52:36 +00:00
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
if nextSlot < slot || s < nextSlot {
|
2020-08-11 15:15:18 +00:00
|
|
|
return 0, errors.New("invalid range for non-skipped slot")
|
|
|
|
}
|
|
|
|
return nextSlot, nil
|
|
|
|
}
|
|
|
|
|
2020-11-05 07:27:46 +00:00
|
|
|
// findFork queries all peers that have higher head slot, in an attempt to find
|
|
|
|
// ones that feature blocks from alternative branches. Once found, peer is further queried
|
|
|
|
// to find common ancestor slot. On success, all obtained blocks and peer is returned.
|
2023-01-26 14:40:12 +00:00
|
|
|
func (f *blocksFetcher) findFork(ctx context.Context, slot primitives.Slot) (*forkData, error) {
|
2020-11-05 07:27:46 +00:00
|
|
|
ctx, span := trace.StartSpan(ctx, "initialsync.findFork")
|
|
|
|
defer span.End()
|
|
|
|
|
|
|
|
// Safe-guard, since previous epoch is used when calculating.
|
|
|
|
slotsPerEpoch := params.BeaconConfig().SlotsPerEpoch
|
|
|
|
if slot < slotsPerEpoch*2 {
|
|
|
|
return nil, fmt.Errorf("slot is too low to backtrack, min. expected %d", slotsPerEpoch*2)
|
|
|
|
}
|
|
|
|
|
|
|
|
// The current slot's epoch must be after the finalization epoch,
|
|
|
|
// triggering backtracking on earlier epochs is unnecessary.
|
2022-06-25 03:57:52 +00:00
|
|
|
cp := f.chain.FinalizedCheckpt()
|
2022-05-20 18:41:33 +00:00
|
|
|
finalizedEpoch := cp.Epoch
|
2021-10-01 20:17:57 +00:00
|
|
|
epoch := slots.ToEpoch(slot)
|
2020-11-05 07:27:46 +00:00
|
|
|
if epoch <= finalizedEpoch {
|
|
|
|
return nil, errors.New("slot is not after the finalized epoch, no backtracking is necessary")
|
|
|
|
}
|
|
|
|
// Update slot to the beginning of the current epoch (preserve original slot for comparison).
|
2022-06-25 03:57:52 +00:00
|
|
|
slot, err := slots.EpochStart(epoch)
|
2020-11-05 07:27:46 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Select peers that have higher head slot, and potentially blocks from more favourable fork.
|
|
|
|
// Exit early if no peers are ready.
|
|
|
|
_, peers := f.p2p.Peers().BestNonFinalized(1, epoch+1)
|
|
|
|
if len(peers) == 0 {
|
|
|
|
return nil, errNoPeersAvailable
|
|
|
|
}
|
|
|
|
f.rand.Shuffle(len(peers), func(i, j int) {
|
|
|
|
peers[i], peers[j] = peers[j], peers[i]
|
|
|
|
})
|
|
|
|
|
|
|
|
// Query all found peers, stop on peer with alternative blocks, and try backtracking.
|
|
|
|
for i, pid := range peers {
|
|
|
|
log.WithFields(logrus.Fields{
|
|
|
|
"peer": pid,
|
|
|
|
"step": fmt.Sprintf("%d/%d", i+1, len(peers)),
|
|
|
|
}).Debug("Searching for alternative blocks")
|
|
|
|
fork, err := f.findForkWithPeer(ctx, pid, slot)
|
|
|
|
if err != nil {
|
|
|
|
log.WithFields(logrus.Fields{
|
|
|
|
"peer": pid,
|
|
|
|
"error": err.Error(),
|
|
|
|
}).Debug("No alternative blocks found for peer")
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
return fork, nil
|
|
|
|
}
|
|
|
|
return nil, errNoPeersWithAltBlocks
|
|
|
|
}
|
|
|
|
|
2023-07-25 21:57:01 +00:00
|
|
|
var errNoAlternateBlocks = errors.New("no alternative blocks exist within scanned range")
|
|
|
|
|
|
|
|
func findForkReqRangeSize() uint64 {
|
|
|
|
return uint64(params.BeaconConfig().SlotsPerEpoch.Mul(2))
|
|
|
|
}
|
|
|
|
|
2020-11-05 07:27:46 +00:00
|
|
|
// findForkWithPeer loads some blocks from a peer in an attempt to find alternative blocks.
|
2023-01-26 14:40:12 +00:00
|
|
|
func (f *blocksFetcher) findForkWithPeer(ctx context.Context, pid peer.ID, slot primitives.Slot) (*forkData, error) {
|
2023-07-25 21:57:01 +00:00
|
|
|
reqCount := findForkReqRangeSize()
|
2020-11-05 07:27:46 +00:00
|
|
|
// Safe-guard, since previous epoch is used when calculating.
|
2023-07-25 21:57:01 +00:00
|
|
|
if uint64(slot) < reqCount {
|
|
|
|
return nil, fmt.Errorf("slot is too low to backtrack, min. expected %d", reqCount)
|
2020-11-05 07:27:46 +00:00
|
|
|
}
|
2023-07-25 21:57:01 +00:00
|
|
|
slotsPerEpoch := params.BeaconConfig().SlotsPerEpoch
|
2020-11-05 07:27:46 +00:00
|
|
|
|
|
|
|
// Locate non-skipped slot, supported by a given peer (can survive long periods of empty slots).
|
|
|
|
// When searching for non-empty slot, start an epoch earlier - for those blocks we
|
|
|
|
// definitely have roots. So, spotting a fork will be easier. It is not a problem if unknown
|
|
|
|
// block of the current fork is found: we are searching for forks when FSMs are stuck, so
|
|
|
|
// being able to progress on any fork is good.
|
|
|
|
pidState, err := f.p2p.Peers().ChainState(pid)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("cannot obtain peer's status: %w", err)
|
|
|
|
}
|
|
|
|
nonSkippedSlot, err := f.nonSkippedSlotAfterWithPeersTarget(
|
2021-10-01 20:17:57 +00:00
|
|
|
ctx, slot-slotsPerEpoch, []peer.ID{pid}, slots.ToEpoch(pidState.HeadSlot))
|
2020-11-05 07:27:46 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("cannot locate non-empty slot for a peer: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Request blocks starting from the first non-empty slot.
|
|
|
|
req := &p2ppb.BeaconBlocksByRangeRequest{
|
|
|
|
StartSlot: nonSkippedSlot,
|
2023-07-25 21:57:01 +00:00
|
|
|
Count: reqCount,
|
2020-11-05 07:27:46 +00:00
|
|
|
Step: 1,
|
|
|
|
}
|
|
|
|
blocks, err := f.requestBlocks(ctx, req, pid)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("cannot fetch blocks: %w", err)
|
|
|
|
}
|
2023-07-25 21:57:01 +00:00
|
|
|
if len(blocks) == 0 {
|
|
|
|
return nil, errNoAlternateBlocks
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the first block is not connected to the current canonical chain, we'll stop processing this batch.
|
|
|
|
// Instead, we'll work backwards from the first block until we find a common ancestor,
|
|
|
|
// and then begin processing from there.
|
|
|
|
first := blocks[0]
|
|
|
|
if !f.chain.HasBlock(ctx, first.Block().ParentRoot()) {
|
|
|
|
// Backtrack on a root, to find a common ancestor from which we can resume syncing.
|
|
|
|
fork, err := f.findAncestor(ctx, pid, first)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to find common ancestor: %w", err)
|
|
|
|
}
|
|
|
|
return fork, nil
|
|
|
|
}
|
2020-11-05 07:27:46 +00:00
|
|
|
|
|
|
|
// Traverse blocks, and if we've got one that doesn't have parent in DB, backtrack on it.
|
2023-07-25 21:57:01 +00:00
|
|
|
// Note that we start from the second element in the array, because we know that the first element is in the db,
|
|
|
|
// otherwise we would have gone into the findAncestor early return path above.
|
|
|
|
for i := 1; i < len(blocks); i++ {
|
|
|
|
block := blocks[i]
|
2022-09-06 14:30:16 +00:00
|
|
|
parentRoot := block.Block().ParentRoot()
|
2023-07-25 21:57:01 +00:00
|
|
|
// Step through blocks until we find one that is not in the chain. The goal is to find the point where the
|
|
|
|
// chain observed in the peer diverges from the locally known chain, and then collect up the remainder of the
|
|
|
|
// observed chain chunk to start initial-sync processing from the fork point.
|
|
|
|
if f.chain.HasBlock(ctx, parentRoot) {
|
|
|
|
continue
|
2020-11-05 07:27:46 +00:00
|
|
|
}
|
2023-07-25 21:57:01 +00:00
|
|
|
log.WithFields(logrus.Fields{
|
|
|
|
"peer": pid,
|
|
|
|
"slot": block.Block().Slot(),
|
|
|
|
"root": fmt.Sprintf("%#x", parentRoot),
|
|
|
|
}).Debug("Block with unknown parent root has been found")
|
|
|
|
altBlocks, err := sortedBlockWithVerifiedBlobSlice(blocks[i-1:])
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrap(err, "invalid blocks received in findForkWithPeer")
|
|
|
|
}
|
|
|
|
// We need to fetch the blobs for the given alt-chain if any exist, so that we can try to verify and import
|
|
|
|
// the blocks.
|
2024-03-30 14:54:11 +00:00
|
|
|
bwb, err := f.fetchBlobsFromPeer(ctx, altBlocks, pid, []peer.ID{pid})
|
2023-07-25 21:57:01 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrap(err, "unable to retrieve blobs for blocks found in findForkWithPeer")
|
|
|
|
}
|
|
|
|
// The caller will use the BlocksWith VerifiedBlobs in bwb as the starting point for
|
|
|
|
// round-robin syncing the alternate chain.
|
|
|
|
return &forkData{peer: pid, bwb: bwb}, nil
|
2020-11-05 07:27:46 +00:00
|
|
|
}
|
2023-07-25 21:57:01 +00:00
|
|
|
return nil, errNoAlternateBlocks
|
2020-11-05 07:27:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// findAncestor tries to figure out common ancestor slot that connects a given root to known block.
|
2023-02-09 09:23:32 +00:00
|
|
|
func (f *blocksFetcher) findAncestor(ctx context.Context, pid peer.ID, b interfaces.ReadOnlySignedBeaconBlock) (*forkData, error) {
|
|
|
|
outBlocks := []interfaces.ReadOnlySignedBeaconBlock{b}
|
2020-11-05 07:27:46 +00:00
|
|
|
for i := uint64(0); i < backtrackingMaxHops; i++ {
|
2022-09-06 14:30:16 +00:00
|
|
|
parentRoot := outBlocks[len(outBlocks)-1].Block().ParentRoot()
|
2022-05-04 00:17:40 +00:00
|
|
|
if f.chain.HasBlock(ctx, parentRoot) {
|
2020-11-05 07:27:46 +00:00
|
|
|
// Common ancestor found, forward blocks back to processor.
|
2023-07-25 21:57:01 +00:00
|
|
|
bwb, err := sortedBlockWithVerifiedBlobSlice(outBlocks)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrap(err, "received invalid blocks in findAncestor")
|
|
|
|
}
|
2024-03-30 14:54:11 +00:00
|
|
|
bwb, err = f.fetchBlobsFromPeer(ctx, bwb, pid, []peer.ID{pid})
|
2023-07-25 21:57:01 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrap(err, "unable to retrieve blobs for blocks found in findAncestor")
|
|
|
|
}
|
2020-11-05 07:27:46 +00:00
|
|
|
return &forkData{
|
2023-07-25 21:57:01 +00:00
|
|
|
peer: pid,
|
|
|
|
bwb: bwb,
|
2020-11-05 07:27:46 +00:00
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
// Request block's parent.
|
|
|
|
req := &p2pTypes.BeaconBlockByRootsReq{parentRoot}
|
|
|
|
blocks, err := f.requestBlocksByRoot(ctx, req, pid)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if len(blocks) == 0 {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
outBlocks = append(outBlocks, blocks[0])
|
|
|
|
}
|
|
|
|
return nil, errors.New("no common ancestor found")
|
|
|
|
}
|
|
|
|
|
2020-08-11 15:15:18 +00:00
|
|
|
// bestFinalizedSlot returns the highest finalized slot of the majority of connected peers.
|
2023-01-26 14:40:12 +00:00
|
|
|
func (f *blocksFetcher) bestFinalizedSlot() primitives.Slot {
|
2022-06-25 03:57:52 +00:00
|
|
|
cp := f.chain.FinalizedCheckpt()
|
2020-10-31 21:33:57 +00:00
|
|
|
finalizedEpoch, _ := f.p2p.Peers().BestFinalized(
|
2022-05-20 18:41:33 +00:00
|
|
|
params.BeaconConfig().MaxPeersToSync, cp.Epoch)
|
2021-02-16 07:45:34 +00:00
|
|
|
return params.BeaconConfig().SlotsPerEpoch.Mul(uint64(finalizedEpoch))
|
2020-08-11 15:15:18 +00:00
|
|
|
}
|
2020-08-18 02:10:45 +00:00
|
|
|
|
2020-08-24 14:07:21 +00:00
|
|
|
// bestNonFinalizedSlot returns the highest non-finalized slot of enough number of connected peers.
|
2023-01-26 14:40:12 +00:00
|
|
|
func (f *blocksFetcher) bestNonFinalizedSlot() primitives.Slot {
|
2021-10-01 20:17:57 +00:00
|
|
|
headEpoch := slots.ToEpoch(f.chain.HeadSlot())
|
2020-08-24 14:07:21 +00:00
|
|
|
targetEpoch, _ := f.p2p.Peers().BestNonFinalized(flags.Get().MinimumSyncPeers*2, headEpoch)
|
2021-02-16 07:45:34 +00:00
|
|
|
return params.BeaconConfig().SlotsPerEpoch.Mul(uint64(targetEpoch))
|
2020-08-18 02:10:45 +00:00
|
|
|
}
|
2020-10-31 21:33:57 +00:00
|
|
|
|
|
|
|
// calculateHeadAndTargetEpochs return node's current head epoch, along with the best known target
|
|
|
|
// epoch. For the latter peers supporting that target epoch are returned as well.
|
2023-01-26 14:40:12 +00:00
|
|
|
func (f *blocksFetcher) calculateHeadAndTargetEpochs() (headEpoch, targetEpoch primitives.Epoch, peers []peer.ID) {
|
2020-10-31 21:33:57 +00:00
|
|
|
if f.mode == modeStopOnFinalizedEpoch {
|
2022-06-25 03:57:52 +00:00
|
|
|
cp := f.chain.FinalizedCheckpt()
|
2022-05-20 18:41:33 +00:00
|
|
|
headEpoch = cp.Epoch
|
2020-10-31 21:33:57 +00:00
|
|
|
targetEpoch, peers = f.p2p.Peers().BestFinalized(params.BeaconConfig().MaxPeersToSync, headEpoch)
|
|
|
|
} else {
|
2021-10-01 20:17:57 +00:00
|
|
|
headEpoch = slots.ToEpoch(f.chain.HeadSlot())
|
2020-10-31 21:33:57 +00:00
|
|
|
targetEpoch, peers = f.p2p.Peers().BestNonFinalized(flags.Get().MinimumSyncPeers, headEpoch)
|
|
|
|
}
|
|
|
|
return headEpoch, targetEpoch, peers
|
|
|
|
}
|