set `safe_block_hash` to fork choice justified (#4010)
Implements the fork choice safe block spec, where `safe_block_hash` in `forkChoiceUpdated` is set to justified (used to be `ZERO_HASH`). https://github.com/ethereum/consensus-specs/blob/v1.2.0-rc.3/fork_choice/safe-block.md#get_safe_execution_payload_hash
This commit is contained in:
parent
ebfb624557
commit
64972e3c8a
|
@ -733,26 +733,55 @@ func getAggregatedAttestation*(pool: var AttestationPool,
|
||||||
|
|
||||||
res
|
res
|
||||||
|
|
||||||
|
type BeaconHead* = object
|
||||||
|
blck*: BlockRef
|
||||||
|
safeExecutionPayloadHash*, finalizedExecutionPayloadHash*: Eth2Digest
|
||||||
|
|
||||||
|
proc getBeaconHead*(
|
||||||
|
pool: var AttestationPool, headBlock: BlockRef): BeaconHead =
|
||||||
|
let
|
||||||
|
finalizedExecutionPayloadHash =
|
||||||
|
pool.dag.loadExecutionBlockRoot(pool.dag.finalizedHead.blck)
|
||||||
|
|
||||||
|
# https://github.com/ethereum/consensus-specs/blob/v1.2.0-rc.3/fork_choice/safe-block.md#get_safe_execution_payload_hash
|
||||||
|
safeBlockRoot = pool.forkChoice.get_safe_beacon_block_root()
|
||||||
|
safeBlock = pool.dag.getBlockRef(safeBlockRoot)
|
||||||
|
safeExecutionPayloadHash =
|
||||||
|
if safeBlock.isErr:
|
||||||
|
# Safe block is currently the justified block determined by fork choice.
|
||||||
|
# If finality already advanced beyond the current justified checkpoint,
|
||||||
|
# e.g., because we have selected a head that did not yet realize the cp,
|
||||||
|
# the justified block may end up not having a `BlockRef` anymore.
|
||||||
|
# Because we know that a different fork already finalized a later point,
|
||||||
|
# let's just report the finalized execution payload hash instead.
|
||||||
|
finalizedExecutionPayloadHash
|
||||||
|
else:
|
||||||
|
pool.dag.loadExecutionBlockRoot(safeBlock.get)
|
||||||
|
|
||||||
|
BeaconHead(
|
||||||
|
blck: headBlock,
|
||||||
|
safeExecutionPayloadHash: safeExecutionPayloadHash,
|
||||||
|
finalizedExecutionPayloadHash: finalizedExecutionPayloadHash)
|
||||||
|
|
||||||
proc selectOptimisticHead*(
|
proc selectOptimisticHead*(
|
||||||
pool: var AttestationPool, wallTime: BeaconTime): Opt[BlockRef] =
|
pool: var AttestationPool, wallTime: BeaconTime): Opt[BeaconHead] =
|
||||||
## Trigger fork choice and returns the new head block.
|
## Trigger fork choice and returns the new head block.
|
||||||
## Can return `nil`
|
|
||||||
# TODO rename this to get_optimistic_head
|
# TODO rename this to get_optimistic_head
|
||||||
let newHead = pool.forkChoice.get_head(pool.dag, wallTime)
|
let newHeadRoot = pool.forkChoice.get_head(pool.dag, wallTime)
|
||||||
|
if newHeadRoot.isErr:
|
||||||
|
error "Couldn't select head", err = newHeadRoot.error
|
||||||
|
return err()
|
||||||
|
|
||||||
if newHead.isErr:
|
let headBlock = pool.dag.getBlockRef(newHeadRoot.get()).valueOr:
|
||||||
error "Couldn't select head", err = newHead.error
|
# This should normally not happen, but if the chain dag and fork choice
|
||||||
err()
|
# get out of sync, we'll need to try to download the selected head - in
|
||||||
else:
|
# the meantime, return nil to indicate that no new head was chosen
|
||||||
let ret = pool.dag.getBlockRef(newHead.get())
|
warn "Fork choice selected unknown head, trying to sync",
|
||||||
if ret.isErr():
|
root = newHeadRoot.get()
|
||||||
# This should normally not happen, but if the chain dag and fork choice
|
pool.quarantine[].addMissing(newHeadRoot.get())
|
||||||
# get out of sync, we'll need to try to download the selected head - in
|
return err()
|
||||||
# the meantime, return nil to indicate that no new head was chosen
|
|
||||||
warn "Fork choice selected unknown head, trying to sync", root = newHead.get()
|
|
||||||
pool.quarantine[].addMissing(newHead.get())
|
|
||||||
|
|
||||||
ret
|
ok pool.getBeaconHead(headBlock)
|
||||||
|
|
||||||
proc prune*(pool: var AttestationPool) =
|
proc prune*(pool: var AttestationPool) =
|
||||||
if (let v = pool.forkChoice.prune(); v.isErr):
|
if (let v = pool.forkChoice.prune(); v.isErr):
|
||||||
|
|
|
@ -118,7 +118,8 @@ from web3/engine_api_types import
|
||||||
func `$`(h: BlockHash): string = $h.asEth2Digest
|
func `$`(h: BlockHash): string = $h.asEth2Digest
|
||||||
|
|
||||||
proc runForkchoiceUpdated*(
|
proc runForkchoiceUpdated*(
|
||||||
eth1Monitor: Eth1Monitor, headBlockRoot, finalizedBlockRoot: Eth2Digest):
|
eth1Monitor: Eth1Monitor,
|
||||||
|
headBlockRoot, safeBlockRoot, finalizedBlockRoot: Eth2Digest):
|
||||||
Future[PayloadExecutionStatus] {.async.} =
|
Future[PayloadExecutionStatus] {.async.} =
|
||||||
# Allow finalizedBlockRoot to be 0 to avoid sync deadlocks.
|
# Allow finalizedBlockRoot to be 0 to avoid sync deadlocks.
|
||||||
#
|
#
|
||||||
|
@ -138,15 +139,15 @@ proc runForkchoiceUpdated*(
|
||||||
|
|
||||||
let fcuR = awaitWithTimeout(
|
let fcuR = awaitWithTimeout(
|
||||||
forkchoiceUpdated(
|
forkchoiceUpdated(
|
||||||
eth1Monitor, headBlockRoot, finalizedBlockRoot),
|
eth1Monitor, headBlockRoot, safeBlockRoot, finalizedBlockRoot),
|
||||||
FORKCHOICEUPDATED_TIMEOUT):
|
FORKCHOICEUPDATED_TIMEOUT):
|
||||||
debug "runForkchoiceUpdated: forkchoiceUpdated timed out"
|
debug "runForkchoiceUpdated: forkchoiceUpdated timed out"
|
||||||
ForkchoiceUpdatedResponse(
|
ForkchoiceUpdatedResponse(
|
||||||
payloadStatus: PayloadStatusV1(status: PayloadExecutionStatus.syncing))
|
payloadStatus: PayloadStatusV1(
|
||||||
|
status: PayloadExecutionStatus.syncing))
|
||||||
|
|
||||||
debug "runForkchoiceUpdated: ran forkchoiceUpdated",
|
debug "runForkchoiceUpdated: ran forkchoiceUpdated",
|
||||||
headBlockRoot,
|
headBlockRoot, safeBlockRoot, finalizedBlockRoot,
|
||||||
finalizedBlockRoot,
|
|
||||||
payloadStatus = $fcuR.payloadStatus.status,
|
payloadStatus = $fcuR.payloadStatus.status,
|
||||||
latestValidHash = $fcuR.payloadStatus.latestValidHash,
|
latestValidHash = $fcuR.payloadStatus.latestValidHash,
|
||||||
validationError = $fcuR.payloadStatus.validationError
|
validationError = $fcuR.payloadStatus.validationError
|
||||||
|
@ -157,31 +158,32 @@ proc runForkchoiceUpdated*(
|
||||||
err = err.msg
|
err = err.msg
|
||||||
return PayloadExecutionStatus.syncing
|
return PayloadExecutionStatus.syncing
|
||||||
|
|
||||||
proc updateExecutionClientHead(self: ref ConsensusManager, newHead: BlockRef)
|
proc updateExecutionClientHead(self: ref ConsensusManager, newHead: BeaconHead)
|
||||||
{.async.} =
|
{.async.} =
|
||||||
if self.eth1Monitor.isNil:
|
if self.eth1Monitor.isNil:
|
||||||
return
|
return
|
||||||
|
|
||||||
let executionHeadRoot = self.dag.loadExecutionBlockRoot(newHead)
|
let headExecutionPayloadHash = self.dag.loadExecutionBlockRoot(newHead.blck)
|
||||||
|
|
||||||
if executionHeadRoot.isZero:
|
if headExecutionPayloadHash.isZero:
|
||||||
# Blocks without execution payloads can't be optimistic.
|
# Blocks without execution payloads can't be optimistic.
|
||||||
self.dag.markBlockVerified(self.quarantine[], newHead.root)
|
self.dag.markBlockVerified(self.quarantine[], newHead.blck.root)
|
||||||
return
|
return
|
||||||
|
|
||||||
# Can't use dag.head here because it hasn't been updated yet
|
# Can't use dag.head here because it hasn't been updated yet
|
||||||
let payloadExecutionStatus = await self.eth1Monitor.runForkchoiceUpdated(
|
let payloadExecutionStatus = await self.eth1Monitor.runForkchoiceUpdated(
|
||||||
executionHeadRoot,
|
headExecutionPayloadHash,
|
||||||
self.dag.loadExecutionBlockRoot(self.dag.finalizedHead.blck))
|
newHead.safeExecutionPayloadHash,
|
||||||
|
newHead.finalizedExecutionPayloadHash)
|
||||||
|
|
||||||
case payloadExecutionStatus
|
case payloadExecutionStatus
|
||||||
of PayloadExecutionStatus.valid:
|
of PayloadExecutionStatus.valid:
|
||||||
self.dag.markBlockVerified(self.quarantine[], newHead.root)
|
self.dag.markBlockVerified(self.quarantine[], newHead.blck.root)
|
||||||
of PayloadExecutionStatus.invalid, PayloadExecutionStatus.invalid_block_hash:
|
of PayloadExecutionStatus.invalid, PayloadExecutionStatus.invalid_block_hash:
|
||||||
self.dag.markBlockInvalid(newHead.root)
|
self.dag.markBlockInvalid(newHead.blck.root)
|
||||||
self.quarantine[].addUnviable(newHead.root)
|
self.quarantine[].addUnviable(newHead.blck.root)
|
||||||
of PayloadExecutionStatus.accepted, PayloadExecutionStatus.syncing:
|
of PayloadExecutionStatus.accepted, PayloadExecutionStatus.syncing:
|
||||||
self.dag.optimisticRoots.incl newHead.root
|
self.dag.optimisticRoots.incl newHead.blck.root
|
||||||
|
|
||||||
proc updateHead*(self: var ConsensusManager, newHead: BlockRef) =
|
proc updateHead*(self: var ConsensusManager, newHead: BlockRef) =
|
||||||
## Trigger fork choice and update the DAG with the new head block
|
## Trigger fork choice and update the DAG with the new head block
|
||||||
|
@ -206,11 +208,11 @@ proc updateHead*(self: var ConsensusManager, wallSlot: Slot) =
|
||||||
head = shortLog(self.dag.head), wallSlot
|
head = shortLog(self.dag.head), wallSlot
|
||||||
return
|
return
|
||||||
|
|
||||||
if self.dag.loadExecutionBlockRoot(newHead).isZero:
|
if self.dag.loadExecutionBlockRoot(newHead.blck).isZero:
|
||||||
# Blocks without execution payloads can't be optimistic.
|
# Blocks without execution payloads can't be optimistic.
|
||||||
self.dag.markBlockVerified(self.quarantine[], newHead.root)
|
self.dag.markBlockVerified(self.quarantine[], newHead.blck.root)
|
||||||
|
|
||||||
self.updateHead(newHead)
|
self.updateHead(newHead.blck)
|
||||||
|
|
||||||
proc checkNextProposer(dag: ChainDAGRef, slot: Slot):
|
proc checkNextProposer(dag: ChainDAGRef, slot: Slot):
|
||||||
Opt[(ValidatorIndex, ValidatorPubKey)] =
|
Opt[(ValidatorIndex, ValidatorPubKey)] =
|
||||||
|
@ -247,9 +249,8 @@ proc runProposalForkchoiceUpdated*(self: ref ConsensusManager) {.async.} =
|
||||||
get_randao_mix(state.data, get_current_epoch(state.data)).data
|
get_randao_mix(state.data, get_current_epoch(state.data)).data
|
||||||
feeRecipient = self.getFeeRecipient(
|
feeRecipient = self.getFeeRecipient(
|
||||||
nextProposer, validatorIndex, nextSlot.epoch)
|
nextProposer, validatorIndex, nextSlot.epoch)
|
||||||
headBlockRoot = self.dag.loadExecutionBlockRoot(self.dag.head)
|
beaconHead = self.attestationPool[].getBeaconHead(self.dag.head)
|
||||||
finalizedBlockRoot =
|
headBlockRoot = self.dag.loadExecutionBlockRoot(beaconHead.blck)
|
||||||
self.dag.loadExecutionBlockRoot(self.dag.finalizedHead.blck)
|
|
||||||
|
|
||||||
if headBlockRoot.isZero:
|
if headBlockRoot.isZero:
|
||||||
return
|
return
|
||||||
|
@ -257,8 +258,11 @@ proc runProposalForkchoiceUpdated*(self: ref ConsensusManager) {.async.} =
|
||||||
try:
|
try:
|
||||||
let fcResult = awaitWithTimeout(
|
let fcResult = awaitWithTimeout(
|
||||||
forkchoiceUpdated(
|
forkchoiceUpdated(
|
||||||
self.eth1Monitor, headBlockRoot, finalizedBlockRoot, timestamp,
|
self.eth1Monitor,
|
||||||
randomData, feeRecipient),
|
headBlockRoot,
|
||||||
|
beaconHead.safeExecutionPayloadHash,
|
||||||
|
beaconHead.finalizedExecutionPayloadHash,
|
||||||
|
timestamp, randomData, feeRecipient),
|
||||||
FORKCHOICEUPDATED_TIMEOUT):
|
FORKCHOICEUPDATED_TIMEOUT):
|
||||||
debug "runProposalForkchoiceUpdated: forkchoiceUpdated timed out"
|
debug "runProposalForkchoiceUpdated: forkchoiceUpdated timed out"
|
||||||
ForkchoiceUpdatedResponse(
|
ForkchoiceUpdatedResponse(
|
||||||
|
@ -271,13 +275,14 @@ proc runProposalForkchoiceUpdated*(self: ref ConsensusManager) {.async.} =
|
||||||
self.forkchoiceUpdatedInfo = Opt.some ForkchoiceUpdatedInformation(
|
self.forkchoiceUpdatedInfo = Opt.some ForkchoiceUpdatedInformation(
|
||||||
payloadId: bellatrix.PayloadID(fcResult.payloadId.get),
|
payloadId: bellatrix.PayloadID(fcResult.payloadId.get),
|
||||||
headBlockRoot: headBlockRoot,
|
headBlockRoot: headBlockRoot,
|
||||||
finalizedBlockRoot: finalizedBlockRoot,
|
safeBlockRoot: beaconHead.safeExecutionPayloadHash,
|
||||||
|
finalizedBlockRoot: beaconHead.finalizedExecutionPayloadHash,
|
||||||
timestamp: timestamp,
|
timestamp: timestamp,
|
||||||
feeRecipient: feeRecipient)
|
feeRecipient: feeRecipient)
|
||||||
except CatchableError as err:
|
except CatchableError as err:
|
||||||
error "Engine API fork-choice update failed", err = err.msg
|
error "Engine API fork-choice update failed", err = err.msg
|
||||||
|
|
||||||
proc updateHeadWithExecution*(self: ref ConsensusManager, newHead: BlockRef)
|
proc updateHeadWithExecution*(self: ref ConsensusManager, newHead: BeaconHead)
|
||||||
{.async.} =
|
{.async.} =
|
||||||
## Trigger fork choice and update the DAG with the new head block
|
## Trigger fork choice and update the DAG with the new head block
|
||||||
## This does not automatically prune the DAG after finalization
|
## This does not automatically prune the DAG after finalization
|
||||||
|
@ -290,7 +295,7 @@ proc updateHeadWithExecution*(self: ref ConsensusManager, newHead: BlockRef)
|
||||||
|
|
||||||
# Store the new head in the chain DAG - this may cause epochs to be
|
# Store the new head in the chain DAG - this may cause epochs to be
|
||||||
# justified and finalized
|
# justified and finalized
|
||||||
self.dag.updateHead(newHead, self.quarantine[])
|
self.dag.updateHead(newHead.blck, self.quarantine[])
|
||||||
|
|
||||||
# TODO after things stabilize with this, check for upcoming proposal and
|
# TODO after things stabilize with this, check for upcoming proposal and
|
||||||
# don't bother sending first fcU, but initially, keep both in place
|
# don't bother sending first fcU, but initially, keep both in place
|
||||||
|
|
|
@ -496,7 +496,7 @@ proc newPayload*(p: Eth1Monitor, payload: engine_api.ExecutionPayloadV1):
|
||||||
p.dataProvider.web3.provider.engine_newPayloadV1(payload)
|
p.dataProvider.web3.provider.engine_newPayloadV1(payload)
|
||||||
|
|
||||||
proc forkchoiceUpdated*(p: Eth1Monitor,
|
proc forkchoiceUpdated*(p: Eth1Monitor,
|
||||||
headBlock, finalizedBlock: Eth2Digest):
|
headBlock, safeBlock, finalizedBlock: Eth2Digest):
|
||||||
Future[engine_api.ForkchoiceUpdatedResponse] =
|
Future[engine_api.ForkchoiceUpdatedResponse] =
|
||||||
# Eth1 monitor can recycle connections without (external) warning; at least,
|
# Eth1 monitor can recycle connections without (external) warning; at least,
|
||||||
# don't crash.
|
# don't crash.
|
||||||
|
@ -510,17 +510,12 @@ proc forkchoiceUpdated*(p: Eth1Monitor,
|
||||||
p.dataProvider.web3.provider.engine_forkchoiceUpdatedV1(
|
p.dataProvider.web3.provider.engine_forkchoiceUpdatedV1(
|
||||||
ForkchoiceStateV1(
|
ForkchoiceStateV1(
|
||||||
headBlockHash: headBlock.asBlockHash,
|
headBlockHash: headBlock.asBlockHash,
|
||||||
|
safeBlockHash: safeBlock.asBlockHash,
|
||||||
# https://hackmd.io/@n0ble/kintsugi-spec#Engine-API
|
|
||||||
# "CL client software MUST use headBlockHash value as a stub for the
|
|
||||||
# safeBlockHash parameter"
|
|
||||||
safeBlockHash: headBlock.asBlockHash,
|
|
||||||
|
|
||||||
finalizedBlockHash: finalizedBlock.asBlockHash),
|
finalizedBlockHash: finalizedBlock.asBlockHash),
|
||||||
none(engine_api.PayloadAttributesV1))
|
none(engine_api.PayloadAttributesV1))
|
||||||
|
|
||||||
proc forkchoiceUpdated*(p: Eth1Monitor,
|
proc forkchoiceUpdated*(p: Eth1Monitor,
|
||||||
headBlock, finalizedBlock: Eth2Digest,
|
headBlock, safeBlock, finalizedBlock: Eth2Digest,
|
||||||
timestamp: uint64,
|
timestamp: uint64,
|
||||||
randomData: array[32, byte],
|
randomData: array[32, byte],
|
||||||
suggestedFeeRecipient: Eth1Address):
|
suggestedFeeRecipient: Eth1Address):
|
||||||
|
@ -537,12 +532,7 @@ proc forkchoiceUpdated*(p: Eth1Monitor,
|
||||||
p.dataProvider.web3.provider.engine_forkchoiceUpdatedV1(
|
p.dataProvider.web3.provider.engine_forkchoiceUpdatedV1(
|
||||||
ForkchoiceStateV1(
|
ForkchoiceStateV1(
|
||||||
headBlockHash: headBlock.asBlockHash,
|
headBlockHash: headBlock.asBlockHash,
|
||||||
|
safeBlockHash: safeBlock.asBlockHash,
|
||||||
# https://hackmd.io/@n0ble/kintsugi-spec#Engine-API
|
|
||||||
# "CL client software MUST use headBlockHash value as a stub for the
|
|
||||||
# safeBlockHash parameter"
|
|
||||||
safeBlockHash: headBlock.asBlockHash,
|
|
||||||
|
|
||||||
finalizedBlockHash: finalizedBlock.asBlockHash),
|
finalizedBlockHash: finalizedBlock.asBlockHash),
|
||||||
some(engine_api.PayloadAttributesV1(
|
some(engine_api.PayloadAttributesV1(
|
||||||
timestamp: Quantity timestamp,
|
timestamp: Quantity timestamp,
|
||||||
|
|
|
@ -413,6 +413,11 @@ proc get_head*(self: var ForkChoice,
|
||||||
self.checkpoints.justified.balances,
|
self.checkpoints.justified.balances,
|
||||||
self.checkpoints.proposer_boost_root)
|
self.checkpoints.proposer_boost_root)
|
||||||
|
|
||||||
|
# https://github.com/ethereum/consensus-specs/blob/v1.2.0-rc.3/fork_choice/safe-block.md#get_safe_beacon_block_root
|
||||||
|
func get_safe_beacon_block_root*(self: var ForkChoice): Eth2Digest =
|
||||||
|
# Use most recent justified block as a stopgap
|
||||||
|
self.checkpoints.justified.checkpoint.root
|
||||||
|
|
||||||
func prune*(
|
func prune*(
|
||||||
self: var ForkChoiceBackend, finalized_root: Eth2Digest
|
self: var ForkChoiceBackend, finalized_root: Eth2Digest
|
||||||
): FcResult[void] =
|
): FcResult[void] =
|
||||||
|
|
|
@ -173,20 +173,20 @@ from ../eth1/eth1_monitor import
|
||||||
Eth1Monitor, asEngineExecutionPayload, ensureDataProvider, newPayload
|
Eth1Monitor, asEngineExecutionPayload, ensureDataProvider, newPayload
|
||||||
|
|
||||||
proc expectValidForkchoiceUpdated(
|
proc expectValidForkchoiceUpdated(
|
||||||
eth1Monitor: Eth1Monitor, headBlockRoot, finalizedBlockRoot: Eth2Digest):
|
eth1Monitor: Eth1Monitor,
|
||||||
Future[void] {.async.} =
|
headBlockRoot, safeBlockRoot, finalizedBlockRoot: Eth2Digest
|
||||||
|
): Future[void] {.async.} =
|
||||||
let payloadExecutionStatus =
|
let payloadExecutionStatus =
|
||||||
await eth1Monitor.runForkchoiceUpdated(headBlockRoot, finalizedBlockRoot)
|
await eth1Monitor.runForkchoiceUpdated(
|
||||||
|
headBlockRoot, safeBlockRoot, finalizedBlockRoot)
|
||||||
if payloadExecutionStatus != PayloadExecutionStatus.valid:
|
if payloadExecutionStatus != PayloadExecutionStatus.valid:
|
||||||
# Only called when expecting this to be valid because `newPayload` or some
|
# Only called when expecting this to be valid because `newPayload` or some
|
||||||
# previous `forkchoiceUpdated` had already marked it as valid.
|
# previous `forkchoiceUpdated` had already marked it as valid.
|
||||||
warn "expectValidForkchoiceUpdate: forkChoiceUpdated not `VALID`",
|
warn "expectValidForkchoiceUpdate: forkChoiceUpdated not `VALID`",
|
||||||
payloadExecutionStatus,
|
payloadExecutionStatus, headBlockRoot, safeBlockRoot, finalizedBlockRoot
|
||||||
headBlockRoot,
|
|
||||||
finalizedBlockRoot
|
|
||||||
|
|
||||||
from ../consensus_object_pools/attestation_pool import
|
from ../consensus_object_pools/attestation_pool import
|
||||||
addForkChoice, selectOptimisticHead
|
addForkChoice, selectOptimisticHead, BeaconHead
|
||||||
from ../consensus_object_pools/blockchain_dag import
|
from ../consensus_object_pools/blockchain_dag import
|
||||||
is_optimistic, loadExecutionBlockRoot, markBlockVerified
|
is_optimistic, loadExecutionBlockRoot, markBlockVerified
|
||||||
from ../consensus_object_pools/block_dag import shortLog
|
from ../consensus_object_pools/block_dag import shortLog
|
||||||
|
@ -294,20 +294,20 @@ proc storeBlock*(
|
||||||
wallSlot.start_beacon_time)
|
wallSlot.start_beacon_time)
|
||||||
|
|
||||||
if newHead.isOk:
|
if newHead.isOk:
|
||||||
let executionHeadRoot =
|
let headExecutionPayloadHash =
|
||||||
self.consensusManager.dag.loadExecutionBlockRoot(newHead.get)
|
self.consensusManager.dag.loadExecutionBlockRoot(newHead.get.blck)
|
||||||
if executionHeadRoot.isZero:
|
if headExecutionPayloadHash.isZero:
|
||||||
# Blocks without execution payloads can't be optimistic.
|
# Blocks without execution payloads can't be optimistic.
|
||||||
self.consensusManager[].updateHead(newHead.get)
|
self.consensusManager[].updateHead(newHead.get.blck)
|
||||||
elif not self.consensusManager.dag.is_optimistic newHead.get.root:
|
elif not self.consensusManager.dag.is_optimistic newHead.get.blck.root:
|
||||||
# Not `NOT_VALID`; either `VALID` or `INVALIDATED`, but latter wouldn't
|
# Not `NOT_VALID`; either `VALID` or `INVALIDATED`, but latter wouldn't
|
||||||
# be selected as head, so `VALID`. `forkchoiceUpdated` necessary for EL
|
# be selected as head, so `VALID`. `forkchoiceUpdated` necessary for EL
|
||||||
# client only.
|
# client only.
|
||||||
self.consensusManager[].updateHead(newHead.get)
|
self.consensusManager[].updateHead(newHead.get.blck)
|
||||||
asyncSpawn self.consensusManager.eth1Monitor.expectValidForkchoiceUpdated(
|
asyncSpawn self.consensusManager.eth1Monitor.expectValidForkchoiceUpdated(
|
||||||
executionHeadRoot,
|
headExecutionPayloadHash,
|
||||||
self.consensusManager.dag.loadExecutionBlockRoot(
|
newHead.get.safeExecutionPayloadHash,
|
||||||
self.consensusManager.dag.finalizedHead.blck))
|
newHead.get.finalizedExecutionPayloadHash)
|
||||||
|
|
||||||
# TODO remove redundant fcU in case of proposal
|
# TODO remove redundant fcU in case of proposal
|
||||||
asyncSpawn self.consensusManager.runProposalForkchoiceUpdated()
|
asyncSpawn self.consensusManager.runProposalForkchoiceUpdated()
|
||||||
|
|
|
@ -90,6 +90,7 @@ programMain:
|
||||||
# engine_forkchoiceUpdatedV1
|
# engine_forkchoiceUpdatedV1
|
||||||
discard await eth1Monitor.runForkchoiceUpdated(
|
discard await eth1Monitor.runForkchoiceUpdated(
|
||||||
headBlockRoot = payload.block_hash,
|
headBlockRoot = payload.block_hash,
|
||||||
|
safeBlockRoot = payload.block_hash, # stub value
|
||||||
finalizedBlockRoot = ZERO_HASH)
|
finalizedBlockRoot = ZERO_HASH)
|
||||||
else: discard
|
else: discard
|
||||||
return
|
return
|
||||||
|
|
|
@ -313,6 +313,7 @@ from web3/engine_api import ForkchoiceUpdatedResponse
|
||||||
# TODO: This copies the entire BeaconState on each call
|
# TODO: This copies the entire BeaconState on each call
|
||||||
proc forkchoice_updated(state: bellatrix.BeaconState,
|
proc forkchoice_updated(state: bellatrix.BeaconState,
|
||||||
head_block_hash: Eth2Digest,
|
head_block_hash: Eth2Digest,
|
||||||
|
safe_block_hash: Eth2Digest,
|
||||||
finalized_block_hash: Eth2Digest,
|
finalized_block_hash: Eth2Digest,
|
||||||
fee_recipient: ethtypes.Address,
|
fee_recipient: ethtypes.Address,
|
||||||
execution_engine: Eth1Monitor):
|
execution_engine: Eth1Monitor):
|
||||||
|
@ -328,8 +329,8 @@ proc forkchoice_updated(state: bellatrix.BeaconState,
|
||||||
try:
|
try:
|
||||||
awaitWithTimeout(
|
awaitWithTimeout(
|
||||||
execution_engine.forkchoiceUpdated(
|
execution_engine.forkchoiceUpdated(
|
||||||
head_block_hash, finalized_block_hash, timestamp, random.data,
|
head_block_hash, safe_block_hash, finalized_block_hash,
|
||||||
fee_recipient),
|
timestamp, random.data, fee_recipient),
|
||||||
FORKCHOICEUPDATED_TIMEOUT):
|
FORKCHOICEUPDATED_TIMEOUT):
|
||||||
error "Engine API fork-choice update timed out"
|
error "Engine API fork-choice update timed out"
|
||||||
default(ForkchoiceUpdatedResponse)
|
default(ForkchoiceUpdatedResponse)
|
||||||
|
@ -398,14 +399,15 @@ proc getExecutionPayload(
|
||||||
node.eth1Monitor.terminalBlockHash.get.asEth2Digest
|
node.eth1Monitor.terminalBlockHash.get.asEth2Digest
|
||||||
else:
|
else:
|
||||||
default(Eth2Digest)
|
default(Eth2Digest)
|
||||||
executionBlockRoot = node.dag.loadExecutionBlockRoot(node.dag.head)
|
beaconHead = node.attestationPool[].getBeaconHead(node.dag.head)
|
||||||
|
executionBlockRoot = node.dag.loadExecutionBlockRoot(beaconHead.blck)
|
||||||
latestHead =
|
latestHead =
|
||||||
if not executionBlockRoot.isZero:
|
if not executionBlockRoot.isZero:
|
||||||
executionBlockRoot
|
executionBlockRoot
|
||||||
else:
|
else:
|
||||||
terminalBlockHash
|
terminalBlockHash
|
||||||
latestFinalized =
|
latestSafe = beaconHead.safeExecutionPayloadHash
|
||||||
node.dag.loadExecutionBlockRoot(node.dag.finalizedHead.blck)
|
latestFinalized = beaconHead.finalizedExecutionPayloadHash
|
||||||
feeRecipient = node.getFeeRecipient(pubkey, validator_index, epoch)
|
feeRecipient = node.getFeeRecipient(pubkey, validator_index, epoch)
|
||||||
lastFcU = node.consensusManager.forkchoiceUpdatedInfo
|
lastFcU = node.consensusManager.forkchoiceUpdatedInfo
|
||||||
timestamp = compute_timestamp_at_slot(
|
timestamp = compute_timestamp_at_slot(
|
||||||
|
@ -414,14 +416,14 @@ proc getExecutionPayload(
|
||||||
payload_id =
|
payload_id =
|
||||||
if lastFcU.isSome and
|
if lastFcU.isSome and
|
||||||
lastFcU.get.headBlockRoot == latestHead and
|
lastFcU.get.headBlockRoot == latestHead and
|
||||||
|
lastFcU.get.safeBlockRoot == latestSafe and
|
||||||
lastFcU.get.finalizedBlockRoot == latestFinalized and
|
lastFcU.get.finalizedBlockRoot == latestFinalized and
|
||||||
lastFcU.get.timestamp == timestamp and
|
lastFcU.get.timestamp == timestamp and
|
||||||
lastFcU.get.feeRecipient == feeRecipient:
|
lastFcU.get.feeRecipient == feeRecipient:
|
||||||
some bellatrix.PayloadID(lastFcU.get.payloadId)
|
some bellatrix.PayloadID(lastFcU.get.payloadId)
|
||||||
else:
|
else:
|
||||||
debug "getExecutionPayload: didn't find payloadId, re-querying",
|
debug "getExecutionPayload: didn't find payloadId, re-querying",
|
||||||
latestHead,
|
latestHead, latestSafe, latestFinalized,
|
||||||
latestFinalized,
|
|
||||||
timestamp,
|
timestamp,
|
||||||
feeRecipient,
|
feeRecipient,
|
||||||
cachedHeadBlockRoot = lastFcU.get.headBlockRoot,
|
cachedHeadBlockRoot = lastFcU.get.headBlockRoot,
|
||||||
|
@ -430,7 +432,8 @@ proc getExecutionPayload(
|
||||||
cachedFeeRecipient = lastFcU.get.feeRecipient
|
cachedFeeRecipient = lastFcU.get.feeRecipient
|
||||||
|
|
||||||
(await forkchoice_updated(
|
(await forkchoice_updated(
|
||||||
proposalState.bellatrixData.data, latestHead, latestFinalized,
|
proposalState.bellatrixData.data,
|
||||||
|
latestHead, latestSafe, latestFinalized,
|
||||||
feeRecipient, node.consensusManager.eth1Monitor))
|
feeRecipient, node.consensusManager.eth1Monitor))
|
||||||
payload = try:
|
payload = try:
|
||||||
awaitWithTimeout(
|
awaitWithTimeout(
|
||||||
|
|
|
@ -416,7 +416,8 @@ suite "Attestation pool processing" & preset():
|
||||||
epochRef, blckRef, unrealized, signedBlock.message,
|
epochRef, blckRef, unrealized, signedBlock.message,
|
||||||
blckRef.slot.start_beacon_time)
|
blckRef.slot.start_beacon_time)
|
||||||
|
|
||||||
let head = pool[].selectOptimisticHead(b1Add[].slot.start_beacon_time).get()
|
let head =
|
||||||
|
pool[].selectOptimisticHead(b1Add[].slot.start_beacon_time).get().blck
|
||||||
check:
|
check:
|
||||||
head == b1Add[]
|
head == b1Add[]
|
||||||
|
|
||||||
|
@ -430,7 +431,8 @@ suite "Attestation pool processing" & preset():
|
||||||
epochRef, blckRef, unrealized, signedBlock.message,
|
epochRef, blckRef, unrealized, signedBlock.message,
|
||||||
blckRef.slot.start_beacon_time)
|
blckRef.slot.start_beacon_time)
|
||||||
|
|
||||||
let head2 = pool[].selectOptimisticHead(b2Add[].slot.start_beacon_time).get()
|
let head2 =
|
||||||
|
pool[].selectOptimisticHead(b2Add[].slot.start_beacon_time).get().blck
|
||||||
|
|
||||||
check:
|
check:
|
||||||
head2 == b2Add[]
|
head2 == b2Add[]
|
||||||
|
@ -447,7 +449,8 @@ suite "Attestation pool processing" & preset():
|
||||||
epochRef, blckRef, unrealized, signedBlock.message,
|
epochRef, blckRef, unrealized, signedBlock.message,
|
||||||
blckRef.slot.start_beacon_time)
|
blckRef.slot.start_beacon_time)
|
||||||
|
|
||||||
let head = pool[].selectOptimisticHead(b10Add[].slot.start_beacon_time).get()
|
let head =
|
||||||
|
pool[].selectOptimisticHead(b10Add[].slot.start_beacon_time).get().blck
|
||||||
|
|
||||||
check:
|
check:
|
||||||
head == b10Add[]
|
head == b10Add[]
|
||||||
|
@ -475,7 +478,8 @@ suite "Attestation pool processing" & preset():
|
||||||
attestation0, @[bc1[0]], attestation0.loadSig,
|
attestation0, @[bc1[0]], attestation0.loadSig,
|
||||||
attestation0.data.slot.start_beacon_time)
|
attestation0.data.slot.start_beacon_time)
|
||||||
|
|
||||||
let head2 = pool[].selectOptimisticHead(b10Add[].slot.start_beacon_time).get()
|
let head2 =
|
||||||
|
pool[].selectOptimisticHead(b10Add[].slot.start_beacon_time).get().blck
|
||||||
|
|
||||||
check:
|
check:
|
||||||
# Single vote for b10 and no votes for b11
|
# Single vote for b10 and no votes for b11
|
||||||
|
@ -488,7 +492,8 @@ suite "Attestation pool processing" & preset():
|
||||||
attestation1, @[bc1[1]], attestation1.loadSig,
|
attestation1, @[bc1[1]], attestation1.loadSig,
|
||||||
attestation1.data.slot.start_beacon_time)
|
attestation1.data.slot.start_beacon_time)
|
||||||
|
|
||||||
let head3 = pool[].selectOptimisticHead(b10Add[].slot.start_beacon_time).get()
|
let head3 =
|
||||||
|
pool[].selectOptimisticHead(b10Add[].slot.start_beacon_time).get().blck
|
||||||
let bigger = if b11.root.data < b10.root.data: b10Add else: b11Add
|
let bigger = if b11.root.data < b10.root.data: b10Add else: b11Add
|
||||||
|
|
||||||
check:
|
check:
|
||||||
|
@ -499,7 +504,8 @@ suite "Attestation pool processing" & preset():
|
||||||
attestation2, @[bc1[2]], attestation2.loadSig,
|
attestation2, @[bc1[2]], attestation2.loadSig,
|
||||||
attestation2.data.slot.start_beacon_time)
|
attestation2.data.slot.start_beacon_time)
|
||||||
|
|
||||||
let head4 = pool[].selectOptimisticHead(b11Add[].slot.start_beacon_time).get()
|
let head4 =
|
||||||
|
pool[].selectOptimisticHead(b11Add[].slot.start_beacon_time).get().blck
|
||||||
|
|
||||||
check:
|
check:
|
||||||
# Two votes for b11
|
# Two votes for b11
|
||||||
|
@ -517,7 +523,8 @@ suite "Attestation pool processing" & preset():
|
||||||
epochRef, blckRef, unrealized, signedBlock.message,
|
epochRef, blckRef, unrealized, signedBlock.message,
|
||||||
blckRef.slot.start_beacon_time)
|
blckRef.slot.start_beacon_time)
|
||||||
|
|
||||||
let head = pool[].selectOptimisticHead(b10Add[].slot.start_beacon_time).get()
|
let head =
|
||||||
|
pool[].selectOptimisticHead(b10Add[].slot.start_beacon_time).get().blck
|
||||||
|
|
||||||
check:
|
check:
|
||||||
head == b10Add[]
|
head == b10Add[]
|
||||||
|
@ -550,7 +557,8 @@ suite "Attestation pool processing" & preset():
|
||||||
epochRef, blckRef, unrealized, signedBlock.message,
|
epochRef, blckRef, unrealized, signedBlock.message,
|
||||||
blckRef.slot.start_beacon_time)
|
blckRef.slot.start_beacon_time)
|
||||||
|
|
||||||
let head = pool[].selectOptimisticHead(b10Add[].slot.start_beacon_time).get()
|
let head =
|
||||||
|
pool[].selectOptimisticHead(b10Add[].slot.start_beacon_time).get().blck
|
||||||
|
|
||||||
doAssert: head == b10Add[]
|
doAssert: head == b10Add[]
|
||||||
|
|
||||||
|
@ -577,7 +585,9 @@ suite "Attestation pool processing" & preset():
|
||||||
epochRef, blckRef, unrealized, signedBlock.message,
|
epochRef, blckRef, unrealized, signedBlock.message,
|
||||||
blckRef.slot.start_beacon_time)
|
blckRef.slot.start_beacon_time)
|
||||||
|
|
||||||
let head = pool[].selectOptimisticHead(blockRef[].slot.start_beacon_time).get()
|
let head =
|
||||||
|
pool[].selectOptimisticHead(
|
||||||
|
blockRef[].slot.start_beacon_time).get().blck
|
||||||
doAssert: head == blockRef[]
|
doAssert: head == blockRef[]
|
||||||
dag.updateHead(head, quarantine[])
|
dag.updateHead(head, quarantine[])
|
||||||
pruneAtFinalization(dag, pool[])
|
pruneAtFinalization(dag, pool[])
|
||||||
|
|
Loading…
Reference in New Issue