Skip to content

Commit

Permalink
add branch discovery module for supporting chain stall situation (#6125)
Browse files Browse the repository at this point in the history
In split view situation, the canonical chain may only be served by a
tiny amount of peers, and branches may span long durations. Minority
branches may still have a large weight from attestations and should
be discovered. To assist with that, add a branch discovery module that
assists in such a situation by specifically targeting peers with unknown
histories and downloading from them, in addition to sync manager work
which handles popular branches.
  • Loading branch information
etan-status committed Mar 24, 2024
1 parent 66a9304 commit fc9bc1d
Show file tree
Hide file tree
Showing 10 changed files with 346 additions and 9 deletions.
5 changes: 3 additions & 2 deletions beacon_chain/beacon_node.nim
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import
attestation_pool, sync_committee_msg_pool, validator_change_pool],
./spec/datatypes/[base, altair],
./spec/eth2_apis/dynamic_fee_recipients,
./sync/[sync_manager, request_manager],
./sync/[branch_discovery, sync_manager, request_manager],
./validators/[
action_tracker, message_router, validator_monitor, validator_pool,
keystore_management],
Expand All @@ -35,7 +35,7 @@ export
osproc, chronos, presto, action_tracker,
beacon_clock, beacon_chain_db, conf, light_client,
attestation_pool, sync_committee_msg_pool, validator_change_pool,
eth2_network, el_manager, request_manager, sync_manager,
eth2_network, el_manager, branch_discovery, request_manager, sync_manager,
eth2_processor, optimistic_processor, blockchain_dag, block_quarantine,
base, message_router, validator_monitor, validator_pool,
consensus_manager, dynamic_fee_recipients
Expand Down Expand Up @@ -85,6 +85,7 @@ type
requestManager*: RequestManager
syncManager*: SyncManager[Peer, PeerId]
backfiller*: SyncManager[Peer, PeerId]
branchDiscovery*: ref BranchDiscovery
genesisSnapshotContent*: string
processor*: ref Eth2Processor
blockProcessor*: ref BlockProcessor
Expand Down
1 change: 0 additions & 1 deletion beacon_chain/consensus_object_pools/block_clearance.nim
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,6 @@ proc addResolvedHeadBlock(
epochRef = dag.getEpochRef(state, cache)
epochRefTick = Moment.now()

dag.resetChainProgressWatchdog()
debug "Block resolved",
blockRoot = shortLog(blockRoot),
blck = shortLog(trustedBlock.message),
Expand Down
1 change: 1 addition & 0 deletions beacon_chain/consensus_object_pools/blockchain_dag.nim
Original file line number Diff line number Diff line change
Expand Up @@ -2412,6 +2412,7 @@ proc updateHead*(
quit 1

dag.head = newHead
dag.resetChainProgressWatchdog()

if dag.headState.is_merge_transition_complete() and not
lastHeadMergeComplete and
Expand Down
1 change: 0 additions & 1 deletion beacon_chain/gossip_processing/block_processor.nim
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ const
## Number of slots from wall time that we start processing every payload

type
BlobSidecars* = seq[ref BlobSidecar]
BlockEntry = object
blck*: ForkedSignedBeaconBlock
blobs*: Opt[BlobSidecars]
Expand Down
4 changes: 4 additions & 0 deletions beacon_chain/networking/peer_protocol.nim
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,10 @@ proc updateStatus*(peer: Peer): Future[bool] {.async: (raises: [CancelledError])

await peer.handleStatus(nstate, theirStatus)

proc getHeadRoot*(peer: Peer): Eth2Digest =
## Returns head root for specific peer ``peer``.
peer.state(PeerSync).statusMsg.headRoot

proc getHeadSlot*(peer: Peer): Slot =
## Returns head slot for specific peer ``peer``.
peer.state(PeerSync).statusMsg.headSlot
Expand Down
2 changes: 2 additions & 0 deletions beacon_chain/networking/peer_scores.nim
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ const
## Peer's `status` answer is fine.
PeerScoreNoValues* = -100
## Peer did not respond in time to a request.
PeerScoreGoodBatchValue* = 5
## Individual portion of peer's multi-step answer is fine.
PeerScoreGoodValues* = 100
## Peer's answer to our request is fine.
PeerScoreBadValues* = -1000
Expand Down
28 changes: 27 additions & 1 deletion beacon_chain/nimbus_beacon_node.nim
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,9 @@ proc initFullNode(
func getFrontfillSlot(): Slot =
max(dag.frontfill.get(BlockId()).slot, dag.horizon)

func isBlockKnown(blockRoot: Eth2Digest): bool =
dag.getBlockRef(blockRoot).isSome

let
quarantine = newClone(
Quarantine.init())
Expand Down Expand Up @@ -398,6 +401,13 @@ proc initFullNode(
# that should probably be reimagined more holistically in the future.
blockProcessor[].addBlock(
MsgSource.gossip, signedBlock, blobs, maybeFinalized = maybeFinalized)
branchDiscoveryBlockVerifier = proc(
signedBlock: ForkedSignedBeaconBlock,
blobs: Opt[BlobSidecars]
): Future[Result[void, VerifierError]] {.async: (raises: [
CancelledError], raw: true).} =
blockProcessor[].addBlock(
MsgSource.gossip, signedBlock, blobs, maybeFinalized = false)
rmanBlockVerifier = proc(signedBlock: ForkedSignedBeaconBlock,
maybeFinalized: bool):
Future[Result[void, VerifierError]] {.async: (raises: [CancelledError]).} =
Expand Down Expand Up @@ -448,6 +458,9 @@ proc initFullNode(
getLocalWallSlot, getFirstSlotAtFinalizedEpoch, getBackfillSlot,
getFrontfillSlot, dag.backfill.slot, blockVerifier,
maxHeadAge = 0)
branchDiscovery = BranchDiscovery.new(
node.network, getFirstSlotAtFinalizedEpoch, isBlockKnown,
branchDiscoveryBlockVerifier)
router = (ref MessageRouter)(
processor: processor,
network: node.network)
Expand Down Expand Up @@ -490,6 +503,7 @@ proc initFullNode(
node.requestManager = requestManager
node.syncManager = syncManager
node.backfiller = backfiller
node.branchDiscovery = branchDiscovery
node.router = router

await node.addValidators()
Expand Down Expand Up @@ -1596,6 +1610,10 @@ proc onSlotEnd(node: BeaconNode, slot: Slot) {.async.} =

await node.updateGossipStatus(slot + 1)

# Branch discovery module is only used to support ongoing sync manager tasks
if not node.syncManager.inProgress:
await node.branchDiscovery.stop()

func formatNextConsensusFork(
node: BeaconNode, withVanityArt = false): Opt[string] =
let consensusFork =
Expand All @@ -1615,6 +1633,14 @@ func syncStatus(node: BeaconNode, wallSlot: Slot): string =
let optimisticHead = not node.dag.head.executionValid
if node.syncManager.inProgress:
let
degradedSuffix =
case node.branchDiscovery.state
of BranchDiscoveryState.Active:
"/discovering"
of BranchDiscoveryState.Suspended:
"/degraded"
of BranchDiscoveryState.Stopped:
""
optimisticSuffix =
if optimisticHead:
"/opt"
Expand All @@ -1637,7 +1663,7 @@ func syncStatus(node: BeaconNode, wallSlot: Slot): string =
formatFloat(progress, ffDecimal, precision = 2) & "%"
else:
""
node.syncManager.syncStatus & optimisticSuffix &
node.syncManager.syncStatus & degradedSuffix & optimisticSuffix &
lightClientSuffix & catchingUpSuffix
elif node.backfiller.inProgress:
"backfill: " & node.backfiller.syncStatus
Expand Down
1 change: 1 addition & 0 deletions beacon_chain/spec/datatypes/deneb.nim
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ type
signed_block_header*: SignedBeaconBlockHeader
kzg_commitment_inclusion_proof*:
array[KZG_COMMITMENT_INCLUSION_PROOF_DEPTH, Eth2Digest]
BlobSidecars* = seq[ref BlobSidecar]

# https://github.com/ethereum/beacon-APIs/blob/4882aa0803b622b75bab286b285599d70b7a2429/apis/eventstream/index.yaml#L138-L142
# Spec object, not only internal, because it gets serialized out for the
Expand Down

0 comments on commit fc9bc1d

Please sign in to comment.