use executionValid bit in BlockRef (#4956)

Update `beacon_node/rpc` usage for the new `executionValid` field.
This commit is contained in:
Etan Kissling 2023-05-25 15:57:24 +02:00 committed by GitHub
parent 10569ff2e9
commit 00728e9bb7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 58 additions and 79 deletions

View File

@ -1961,13 +1961,13 @@ proc pruneBlocksDAG(dag: ChainDAGRef) =
dagPruneDur = Moment.now() - startTick dagPruneDur = Moment.now() - startTick
# https://github.com/ethereum/consensus-specs/blob/v1.3.0/sync/optimistic.md#helpers # https://github.com/ethereum/consensus-specs/blob/v1.3.0/sync/optimistic.md#helpers
template is_optimistic*(dag: ChainDAGRef, root: Eth2Digest): bool = template is_optimistic*(dag: ChainDAGRef, bid: BlockId): bool =
let blck = dag.getBlockRef(root) let blck =
if blck.isSome: if bid.slot <= dag.finalizedHead.slot:
not blck.get.executionValid dag.finalizedHead.blck
else: else:
# Either it doesn't exist at all, or it's finalized dag.getBlockRef(bid.root).expect("Non-finalized block is known")
not dag.finalizedHead.blck.executionValid not blck.executionValid
proc markBlockVerified*(dag: ChainDAGRef, blck: BlockRef) = proc markBlockVerified*(dag: ChainDAGRef, blck: BlockRef) =
var cur = blck var cur = blck

View File

@ -246,7 +246,7 @@ proc initFullNode(
proc onBlockAdded(data: ForkedTrustedSignedBeaconBlock) = proc onBlockAdded(data: ForkedTrustedSignedBeaconBlock) =
let optimistic = let optimistic =
if node.currentSlot().epoch() >= dag.cfg.BELLATRIX_FORK_EPOCH: if node.currentSlot().epoch() >= dag.cfg.BELLATRIX_FORK_EPOCH:
some node.dag.is_optimistic(data.root) some node.dag.is_optimistic(data.toBlockId())
else: else:
none[bool]() none[bool]()
node.eventBus.blocksQueue.emit( node.eventBus.blocksQueue.emit(
@ -255,7 +255,8 @@ proc initFullNode(
let eventData = let eventData =
if node.currentSlot().epoch() >= dag.cfg.BELLATRIX_FORK_EPOCH: if node.currentSlot().epoch() >= dag.cfg.BELLATRIX_FORK_EPOCH:
var res = data var res = data
res.optimistic = some node.dag.is_optimistic(data.block_root) res.optimistic = some node.dag.is_optimistic(
BlockId(slot: data.slot, root: data.block_root))
res res
else: else:
data data
@ -264,7 +265,8 @@ proc initFullNode(
let eventData = let eventData =
if node.currentSlot().epoch() >= dag.cfg.BELLATRIX_FORK_EPOCH: if node.currentSlot().epoch() >= dag.cfg.BELLATRIX_FORK_EPOCH:
var res = data var res = data
res.optimistic = some node.dag.is_optimistic(data.new_head_block) res.optimistic = some node.dag.is_optimistic(
BlockId(slot: data.slot, root: data.new_head_block))
res res
else: else:
data data
@ -285,7 +287,10 @@ proc initFullNode(
let eventData = let eventData =
if node.currentSlot().epoch() >= dag.cfg.BELLATRIX_FORK_EPOCH: if node.currentSlot().epoch() >= dag.cfg.BELLATRIX_FORK_EPOCH:
var res = data var res = data
res.optimistic = some node.dag.is_optimistic(data.block_root) # `slot` in this `BlockId` may be higher than block's actual slot,
# this is alright for the purpose of calling `is_optimistic`.
res.optimistic = some node.dag.is_optimistic(
BlockId(slot: data.epoch.start_slot, root: data.block_root))
res res
else: else:
data data
@ -1186,7 +1191,7 @@ proc onSlotEnd(node: BeaconNode, slot: Slot) {.async.} =
# Update upcoming actions - we do this every slot in case a reorg happens # Update upcoming actions - we do this every slot in case a reorg happens
let head = node.dag.head let head = node.dag.head
if node.isSynced(head) == SyncStatus.synced: if node.isSynced(head) and head.executionValid:
withState(node.dag.headState): withState(node.dag.headState):
if node.consensusManager[].actionTracker.needsUpdate( if node.consensusManager[].actionTracker.needsUpdate(
forkyState, slot.epoch + 1): forkyState, slot.epoch + 1):

View File

@ -114,7 +114,7 @@ proc installDebugApiHandlers*(router: var RestRouter, node: BeaconNode) =
validity: validity:
if item.invalid: if item.invalid:
RestNodeValidity.invalid RestNodeValidity.invalid
elif node.dag.is_optimistic(item.bid.root): elif node.dag.is_optimistic(item.bid):
RestNodeValidity.optimistic RestNodeValidity.optimistic
else: else:
RestNodeValidity.valid, RestNodeValidity.valid,

View File

@ -1,3 +1,4 @@
# beacon_chain
# Copyright (c) 2018-2023 Status Research & Development GmbH # Copyright (c) 2018-2023 Status Research & Development GmbH
# Licensed and distributed under either of # Licensed and distributed under either of
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT). # * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
@ -238,9 +239,9 @@ proc installNimbusApiHandlers*(router: var RestRouter, node: BeaconNode) =
return RestApiResponse.jsonError(Http503, BeaconNodeInSyncError, return RestApiResponse.jsonError(Http503, BeaconNodeInSyncError,
$res.error()) $res.error())
let tres = res.get() let tres = res.get()
if tres.optimistic: if not tres.executionValid:
return RestApiResponse.jsonError(Http503, BeaconNodeInSyncError) return RestApiResponse.jsonError(Http503, BeaconNodeInSyncError)
tres.head tres
let proposalState = assignClone(node.dag.headState) let proposalState = assignClone(node.dag.headState)
node.dag.withUpdatedState( node.dag.withUpdatedState(
proposalState[], proposalState[],

View File

@ -33,23 +33,17 @@ func match(data: openArray[char], charset: set[char]): int =
proc getSyncedHead*( proc getSyncedHead*(
node: BeaconNode, node: BeaconNode,
slot: Slot slot: Slot
): Result[tuple[head: BlockRef, optimistic: bool], cstring] = ): Result[BlockRef, cstring] =
let let head = node.dag.head
head = node.dag.head
optimistic = if not node.isSynced(head):
case node.isSynced(head)
of SyncStatus.unsynced:
return err("Beacon node not fully and non-optimistically synced") return err("Beacon node not fully and non-optimistically synced")
of SyncStatus.synced:
false
of SyncStatus.optimistic:
true
# Enough ahead not to know the shuffling # Enough ahead not to know the shuffling
if slot > head.slot + SLOTS_PER_EPOCH * 2: if slot > head.slot + SLOTS_PER_EPOCH * 2:
return err("Requesting far ahead of the current head") return err("Requesting far ahead of the current head")
ok((head, optimistic)) ok(head)
func getCurrentSlot*(node: BeaconNode, slot: Slot): func getCurrentSlot*(node: BeaconNode, slot: Slot):
Result[Slot, cstring] = Result[Slot, cstring] =
@ -61,7 +55,7 @@ func getCurrentSlot*(node: BeaconNode, slot: Slot):
proc getSyncedHead*( proc getSyncedHead*(
node: BeaconNode, node: BeaconNode,
epoch: Epoch, epoch: Epoch,
): Result[tuple[head: BlockRef, optimistic: bool], cstring] = ): Result[BlockRef, cstring] =
if epoch > MaxEpoch: if epoch > MaxEpoch:
return err("Requesting epoch for which slot would overflow") return err("Requesting epoch for which slot would overflow")
node.getSyncedHead(epoch.start_slot()) node.getSyncedHead(epoch.start_slot())
@ -276,30 +270,22 @@ proc getShufflingOptimistic*(node: BeaconNode,
dependentSlot: Slot, dependentSlot: Slot,
dependentRoot: Eth2Digest): Option[bool] = dependentRoot: Eth2Digest): Option[bool] =
if node.currentSlot().epoch() >= node.dag.cfg.BELLATRIX_FORK_EPOCH: if node.currentSlot().epoch() >= node.dag.cfg.BELLATRIX_FORK_EPOCH:
if dependentSlot <= node.dag.finalizedHead.slot: # `slot` in this `BlockId` may be higher than block's actual slot,
some[bool](false) # this is alright for the purpose of calling `is_optimistic`.
else: let bid = BlockId(slot: dependentSlot, root: dependentRoot)
some[bool](node.dag.is_optimistic(dependentRoot)) some[bool](node.dag.is_optimistic(bid))
else: else:
none[bool]() none[bool]()
proc getStateOptimistic*(node: BeaconNode, proc getStateOptimistic*(node: BeaconNode,
state: ForkedHashedBeaconState): Option[bool] = state: ForkedHashedBeaconState): Option[bool] =
if node.currentSlot().epoch() >= node.dag.cfg.BELLATRIX_FORK_EPOCH: if node.currentSlot().epoch() >= node.dag.cfg.BELLATRIX_FORK_EPOCH:
case state.kind if state.kind >= ConsensusFork.Bellatrix:
of ConsensusFork.Phase0, ConsensusFork.Altair:
some[bool](false)
of ConsensusFork.Bellatrix, ConsensusFork.Capella,
ConsensusFork.Deneb:
# A state is optimistic iff the block which created it is # A state is optimistic iff the block which created it is
withState(state): let stateBid = withState(state): forkyState.latest_block_id
# The block root which created the state at slot `n` is at slot `n-1` some[bool](node.dag.is_optimistic(stateBid))
if forkyState.data.slot == GENESIS_SLOT:
some[bool](false)
else: else:
doAssert forkyState.data.slot > 0 some[bool](false)
some[bool](node.dag.is_optimistic(
get_block_root_at_slot(forkyState.data, forkyState.data.slot - 1)))
else: else:
none[bool]() none[bool]()
@ -307,11 +293,10 @@ proc getBlockOptimistic*(node: BeaconNode,
blck: ForkedTrustedSignedBeaconBlock | blck: ForkedTrustedSignedBeaconBlock |
ForkedSignedBeaconBlock): Option[bool] = ForkedSignedBeaconBlock): Option[bool] =
if node.currentSlot().epoch() >= node.dag.cfg.BELLATRIX_FORK_EPOCH: if node.currentSlot().epoch() >= node.dag.cfg.BELLATRIX_FORK_EPOCH:
case blck.kind if blck.kind >= ConsensusFork.Bellatrix:
of ConsensusFork.Phase0, ConsensusFork.Altair: some[bool](node.dag.is_optimistic(blck.toBlockId()))
else:
some[bool](false) some[bool](false)
of ConsensusFork.Bellatrix, ConsensusFork.Capella, ConsensusFork.Deneb:
some[bool](node.dag.is_optimistic(blck.root))
else: else:
none[bool]() none[bool]()

View File

@ -65,7 +65,7 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) =
return RestApiResponse.jsonError(Http400, InvalidEpochValueError, return RestApiResponse.jsonError(Http400, InvalidEpochValueError,
"Cannot request duties past next epoch") "Cannot request duties past next epoch")
res res
let (qhead, _) = let qhead =
block: block:
let res = node.getSyncedHead(qepoch) let res = node.getSyncedHead(qepoch)
if res.isErr(): if res.isErr():
@ -126,7 +126,7 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) =
return RestApiResponse.jsonError(Http400, InvalidEpochValueError, return RestApiResponse.jsonError(Http400, InvalidEpochValueError,
"Cannot request duties past next epoch") "Cannot request duties past next epoch")
res res
let (qhead, _) = let qhead =
block: block:
let res = node.getSyncedHead(qepoch) let res = node.getSyncedHead(qepoch)
if res.isErr(): if res.isErr():
@ -278,7 +278,7 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) =
return RestApiResponse.jsonResponseWOpt(res, optimistic) return RestApiResponse.jsonResponseWOpt(res, optimistic)
elif qSyncPeriod > headSyncPeriod: elif qSyncPeriod > headSyncPeriod:
# The requested epoch may still be too far in the future. # The requested epoch may still be too far in the future.
if node.isSynced(node.dag.head) != SyncStatus.synced: if not node.isSynced(node.dag.head) or not node.dag.head.executionValid:
return RestApiResponse.jsonError(Http503, BeaconNodeInSyncError) return RestApiResponse.jsonError(Http503, BeaconNodeInSyncError)
else: else:
return RestApiResponse.jsonError(Http400, EpochFromFutureError) return RestApiResponse.jsonError(Http400, EpochFromFutureError)
@ -378,9 +378,9 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) =
return RestApiResponse.jsonError(Http503, BeaconNodeInSyncError, return RestApiResponse.jsonError(Http503, BeaconNodeInSyncError,
$res.error()) $res.error())
let tres = res.get() let tres = res.get()
if tres.optimistic: if not tres.executionValid:
return RestApiResponse.jsonError(Http503, BeaconNodeInSyncError) return RestApiResponse.jsonError(Http503, BeaconNodeInSyncError)
tres.head tres
let let
proposer = node.dag.getProposer(qhead, qslot).valueOr: proposer = node.dag.getProposer(qhead, qslot).valueOr:
return RestApiResponse.jsonError(Http400, ProposerNotFoundError) return RestApiResponse.jsonError(Http400, ProposerNotFoundError)
@ -488,9 +488,9 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) =
return RestApiResponse.jsonError(Http503, BeaconNodeInSyncError, return RestApiResponse.jsonError(Http503, BeaconNodeInSyncError,
$res.error()) $res.error())
let tres = res.get() let tres = res.get()
if tres.optimistic: if not tres.executionValid:
return RestApiResponse.jsonError(Http503, BeaconNodeInSyncError) return RestApiResponse.jsonError(Http503, BeaconNodeInSyncError)
tres.head tres
let proposer = node.dag.getProposer(qhead, qslot).valueOr: let proposer = node.dag.getProposer(qhead, qslot).valueOr:
return RestApiResponse.jsonError(Http400, ProposerNotFoundError) return RestApiResponse.jsonError(Http400, ProposerNotFoundError)
@ -586,9 +586,9 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) =
return RestApiResponse.jsonError(Http503, BeaconNodeInSyncError, return RestApiResponse.jsonError(Http503, BeaconNodeInSyncError,
$res.error()) $res.error())
let tres = res.get() let tres = res.get()
if tres.optimistic: if not tres.executionValid:
return RestApiResponse.jsonError(Http503, BeaconNodeInSyncError) return RestApiResponse.jsonError(Http503, BeaconNodeInSyncError)
tres.head tres
let epochRef = node.dag.getEpochRef(qhead, qslot.epoch, true).valueOr: let epochRef = node.dag.getEpochRef(qhead, qslot.epoch, true).valueOr:
return RestApiResponse.jsonError(Http400, PrunedStateError, $error) return RestApiResponse.jsonError(Http400, PrunedStateError, $error)
makeAttestationData(epochRef, qhead.atSlot(qslot), qindex) makeAttestationData(epochRef, qhead.atSlot(qslot), qindex)
@ -677,7 +677,7 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) =
$dres.error()) $dres.error())
dres.get() dres.get()
if node.isSynced(node.dag.head) == SyncStatus.unsynced: if not node.isSynced(node.dag.head):
return RestApiResponse.jsonError(Http503, BeaconNodeInSyncError) return RestApiResponse.jsonError(Http503, BeaconNodeInSyncError)
let let
@ -833,7 +833,7 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) =
return RestApiResponse.jsonError(Http503, BeaconNodeInSyncError, return RestApiResponse.jsonError(Http503, BeaconNodeInSyncError,
$res.error()) $res.error())
let tres = res.get() let tres = res.get()
if tres.optimistic: if not tres.executionValid:
return RestApiResponse.jsonError(Http503, BeaconNodeInSyncError) return RestApiResponse.jsonError(Http503, BeaconNodeInSyncError)
var contribution = SyncCommitteeContribution() var contribution = SyncCommitteeContribution()

View File

@ -90,11 +90,6 @@ type
BlindedBlockResult[SBBB] = BlindedBlockResult[SBBB] =
Result[tuple[blindedBlckPart: SBBB, blockValue: UInt256], string] Result[tuple[blindedBlckPart: SBBB, blockValue: UInt256], string]
SyncStatus* {.pure.} = enum
synced
unsynced
optimistic
proc getValidator*(validators: auto, proc getValidator*(validators: auto,
pubkey: ValidatorPubKey): Opt[ValidatorAndIndex] = pubkey: ValidatorPubKey): Opt[ValidatorAndIndex] =
let idx = validators.findIt(it.pubkey == pubkey) let idx = validators.findIt(it.pubkey == pubkey)
@ -139,7 +134,7 @@ proc getValidatorForDuties*(
node.attachedValidators[].getValidatorForDuties( node.attachedValidators[].getValidatorForDuties(
key.toPubKey(), slot, slashingSafe) key.toPubKey(), slot, slashingSafe)
proc isSynced*(node: BeaconNode, head: BlockRef): SyncStatus = proc isSynced*(node: BeaconNode, head: BlockRef): bool =
## TODO This function is here as a placeholder for some better heurestics to ## TODO This function is here as a placeholder for some better heurestics to
## determine if we're in sync and should be producing blocks and ## determine if we're in sync and should be producing blocks and
## attestations. Generally, the problem is that slot time keeps advancing ## attestations. Generally, the problem is that slot time keeps advancing
@ -160,14 +155,8 @@ proc isSynced*(node: BeaconNode, head: BlockRef): SyncStatus =
# TODO if everyone follows this logic, the network will not recover from a # TODO if everyone follows this logic, the network will not recover from a
# halt: nobody will be producing blocks because everone expects someone # halt: nobody will be producing blocks because everone expects someone
# else to do it # else to do it
if wallSlot.afterGenesis and not wallSlot.afterGenesis or
head.slot + node.config.syncHorizon < wallSlot.slot: head.slot + node.config.syncHorizon >= wallSlot.slot
SyncStatus.unsynced
else:
if not head.executionValid:
SyncStatus.optimistic
else:
SyncStatus.synced
proc handleLightClientUpdates*(node: BeaconNode, slot: Slot) {.async.} = proc handleLightClientUpdates*(node: BeaconNode, slot: Slot) {.async.} =
static: doAssert lightClientFinalityUpdateSlotOffset == static: doAssert lightClientFinalityUpdateSlotOffset ==
@ -1550,8 +1539,7 @@ proc handleValidatorDuties*(node: BeaconNode, lastSlot, slot: Slot) {.async.} =
# The dag head might be updated by sync while we're working due to the # The dag head might be updated by sync while we're working due to the
# await calls, thus we use a local variable to keep the logic straight here # await calls, thus we use a local variable to keep the logic straight here
var head = node.dag.head var head = node.dag.head
case node.isSynced(head) if not node.isSynced(head):
of SyncStatus.unsynced:
info "Beacon node not in sync; skipping validator duties for now", info "Beacon node not in sync; skipping validator duties for now",
slot, headSlot = head.slot slot, headSlot = head.slot
@ -1560,7 +1548,7 @@ proc handleValidatorDuties*(node: BeaconNode, lastSlot, slot: Slot) {.async.} =
return return
of SyncStatus.optimistic: elif not head.executionValid:
info "Execution client not in sync; skipping validator duties for now", info "Execution client not in sync; skipping validator duties for now",
slot, headSlot = head.slot slot, headSlot = head.slot
@ -1568,7 +1556,7 @@ proc handleValidatorDuties*(node: BeaconNode, lastSlot, slot: Slot) {.async.} =
updateValidatorMetrics(node) updateValidatorMetrics(node)
return return
of SyncStatus.synced: else:
discard # keep going discard # keep going
withState(node.dag.headState): withState(node.dag.headState):
@ -1681,7 +1669,7 @@ proc registerDuties*(node: BeaconNode, wallSlot: Slot) {.async.} =
## Register upcoming duties of attached validators with the duty tracker ## Register upcoming duties of attached validators with the duty tracker
if node.attachedValidators[].count() == 0 or if node.attachedValidators[].count() == 0 or
node.isSynced(node.dag.head) != SyncStatus.synced: not node.isSynced(node.dag.head) or not node.dag.head.executionValid:
# Nothing to do because we have no validator attached # Nothing to do because we have no validator attached
return return