always use fcUV2 in shapella even for non-proposer fcUs (#4817)

* always use fcUV2 in shapella even for non-proposer fcUs

* avoid template/proc naming conflict with libp2p/signed_envelope.nim having a payload proc
This commit is contained in:
tersec 2023-04-17 14:17:52 +00:00 committed by GitHub
parent addb7bda2f
commit 75be7d267d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 136 additions and 67 deletions

View File

@ -47,11 +47,11 @@ proc initLightClient*(
withBlck(signedBlock):
when consensusFork >= ConsensusFork.Bellatrix:
if blck.message.is_execution_block:
template payload(): auto = blck.message.body.execution_payload
template blckPayload(): auto = blck.message.body.execution_payload
if not payload.block_hash.isZero:
if not blckPayload.block_hash.isZero:
# engine_newPayloadV1
discard await node.elManager.newExecutionPayload(payload)
discard await node.elManager.newExecutionPayload(blckPayload)
# Retain optimistic head for other `forkchoiceUpdated` callers.
# May temporarily block `forkchoiceUpdatedV1` calls, e.g., Geth:
@ -60,15 +60,32 @@ proc initLightClient*(
# Once DAG sync catches up or as new optimistic heads are fetched
# the situation recovers
node.consensusManager[].setOptimisticHead(
blck.toBlockId(), payload.block_hash)
blck.toBlockId(), blckPayload.block_hash)
# engine_forkchoiceUpdatedV1
# engine_forkchoiceUpdatedV1 or engine_forkchoiceUpdatedV2,
# depending on pre or post-Shapella
let beaconHead = node.attestationPool[].getBeaconHead(nil)
discard await node.elManager.forkchoiceUpdated(
headBlockHash = payload.block_hash,
safeBlockHash = beaconHead.safeExecutionPayloadHash,
finalizedBlockHash = beaconHead.finalizedExecutionPayloadHash,
payloadAttributes = NoPayloadAttributes)
template callForkchoiceUpdated(attributes: untyped) =
discard await node.elManager.forkchoiceUpdated(
headBlockHash = blckPayload.block_hash,
safeBlockHash = beaconHead.safeExecutionPayloadHash,
finalizedBlockHash = beaconHead.finalizedExecutionPayloadHash,
payloadAttributes = none attributes)
case node.dag.cfg.consensusForkAtEpoch(beaconHead.blck.bid.slot.epoch)
of ConsensusFork.Capella, ConsensusFork.Deneb:
# https://github.com/ethereum/execution-apis/blob/v1.0.0-beta.3/src/engine/shanghai.md#specification-1
# Consensus layer client MUST call this method instead of
# `engine_forkchoiceUpdatedV1` under any of the following
# conditions:
# `headBlockHash` references a block which `timestamp` is
# greater or equal to the Shanghai timestamp
callForkchoiceUpdated(PayloadAttributesV2)
of ConsensusFork.Bellatrix:
callForkchoiceUpdated(PayloadAttributesV1)
of ConsensusFork.Phase0, ConsensusFork.Altair:
discard
else: discard
optimisticProcessor = initOptimisticProcessor(

View File

@ -164,13 +164,25 @@ proc updateExecutionClientHead(self: ref ConsensusManager,
self.dag.markBlockVerified(self.quarantine[], newHead.blck.root)
return Opt[void].ok()
# Can't use dag.head here because it hasn't been updated yet
let (payloadExecutionStatus, latestValidHash) =
template callForkchoiceUpdated(attributes: untyped): auto =
await self.elManager.forkchoiceUpdated(
headBlockHash = headExecutionPayloadHash,
safeBlockHash = newHead.safeExecutionPayloadHash,
finalizedBlockHash = newHead.finalizedExecutionPayloadHash,
payloadAttributes = NoPayloadAttributes)
payloadAttributes = none attributes)
# Can't use dag.head here because it hasn't been updated yet
let (payloadExecutionStatus, latestValidHash) =
case self.dag.cfg.consensusForkAtEpoch(newHead.blck.bid.slot.epoch)
of ConsensusFork.Capella, ConsensusFork.Deneb:
# https://github.com/ethereum/execution-apis/blob/v1.0.0-beta.3/src/engine/shanghai.md#specification-1
# Consensus layer client MUST call this method instead of
# `engine_forkchoiceUpdatedV1` under any of the following conditions:
# `headBlockHash` references a block which `timestamp` is greater or
# equal to the Shanghai timestamp
callForkchoiceUpdated(PayloadAttributesV2)
of ConsensusFork.Phase0, ConsensusFork.Altair, ConsensusFork.Bellatrix:
callForkchoiceUpdated(PayloadAttributesV1)
case payloadExecutionStatus
of PayloadExecutionStatus.valid:
@ -360,7 +372,7 @@ proc runProposalForkchoiceUpdated*(
let (status, _) = await self.elManager.forkchoiceUpdated(
headBlockHash, safeBlockHash,
beaconHead.finalizedExecutionPayloadHash,
payloadAttributes = fcPayloadAttributes)
payloadAttributes = some fcPayloadAttributes)
debug "Fork-choice updated for proposal", status
static: doAssert high(ConsensusFork) == ConsensusFork.Deneb

View File

@ -133,10 +133,6 @@ type
hasConsensusViolation: bool
## The local chain contradicts the observed consensus on the network
NoPayloadAttributesType = object
## A type with exactly one value, and which is not constructed via a `nil`
## value for a ref object, which which Nim 1.6 crashes with an ICE.
NextExpectedPayloadParams* = object
headBlockHash*: Eth2Digest
safeBlockHash*: Eth2Digest
@ -255,9 +251,6 @@ type
GetPayloadV2Response |
CancunExecutionPayloadAndBlobs
const
NoPayloadAttributes* = default(NoPayloadAttributesType)
declareCounter failed_web3_requests,
"Failed web3 requests"
@ -764,16 +757,13 @@ func areSameAs(expectedParams: Option[NextExpectedPayloadParams],
proc forkchoiceUpdated(rpcClient: RpcClient,
state: ForkchoiceStateV1,
payloadAttributes: PayloadAttributesV1 |
PayloadAttributesV2 |
NoPayloadAttributesType):
payloadAttributes: Option[PayloadAttributesV1] |
Option[PayloadAttributesV2]):
Future[ForkchoiceUpdatedResponse] =
when payloadAttributes is NoPayloadAttributesType:
rpcClient.engine_forkchoiceUpdatedV1(state, none PayloadAttributesV1)
elif payloadAttributes is PayloadAttributesV1:
rpcClient.engine_forkchoiceUpdatedV1(state, some payloadAttributes)
elif payloadAttributes is PayloadAttributesV2:
rpcClient.engine_forkchoiceUpdatedV2(state, some payloadAttributes)
when payloadAttributes is Option[PayloadAttributesV1]:
rpcClient.engine_forkchoiceUpdatedV1(state, payloadAttributes)
elif payloadAttributes is Option[PayloadAttributesV2]:
rpcClient.engine_forkchoiceUpdatedV2(state, payloadAttributes)
else:
static: doAssert false
@ -806,7 +796,7 @@ proc getPayloadFromSingleEL(
headBlockHash: headBlock.asBlockHash,
safeBlockHash: safeBlock.asBlockHash,
finalizedBlockHash: finalizedBlock.asBlockHash),
PayloadAttributesV1(
some PayloadAttributesV1(
timestamp: Quantity timestamp,
prevRandao: FixedBytes[32] randomData.data,
suggestedFeeRecipient: suggestedFeeRecipient))
@ -816,7 +806,7 @@ proc getPayloadFromSingleEL(
headBlockHash: headBlock.asBlockHash,
safeBlockHash: safeBlock.asBlockHash,
finalizedBlockHash: finalizedBlock.asBlockHash),
PayloadAttributesV2(
some PayloadAttributesV2(
timestamp: Quantity timestamp,
prevRandao: FixedBytes[32] randomData.data,
suggestedFeeRecipient: suggestedFeeRecipient,
@ -1204,8 +1194,8 @@ proc sendNewPayload*(m: ELManager,
proc forkchoiceUpdatedForSingleEL(
connection: ELConnection,
state: ref ForkchoiceStateV1,
payloadAttributes: PayloadAttributesV1 | PayloadAttributesV2 |
NoPayloadAttributesType):
payloadAttributes: Option[PayloadAttributesV1] |
Option[PayloadAttributesV2]):
Future[PayloadStatusV1] {.async.} =
let
rpcClient = await connection.connectedRpcClient()
@ -1223,9 +1213,10 @@ proc forkchoiceUpdatedForSingleEL(
return response.payloadStatus
proc forkchoiceUpdated*(m: ELManager,
headBlockHash, safeBlockHash, finalizedBlockHash: Eth2Digest,
payloadAttributes: PayloadAttributesV1 | PayloadAttributesV2 |
NoPayloadAttributesType):
headBlockHash, safeBlockHash,
finalizedBlockHash: Eth2Digest,
payloadAttributes: Option[PayloadAttributesV1] |
Option[PayloadAttributesV2]):
Future[(PayloadExecutionStatus, Option[BlockHash])] {.async.} =
doAssert not headBlockHash.isZero
@ -1243,18 +1234,24 @@ proc forkchoiceUpdated*(m: ELManager,
if m.elConnections.len == 0:
return (PayloadExecutionStatus.syncing, none BlockHash)
when payloadAttributes is PayloadAttributesV2:
template payloadAttributesV2(): auto = payloadAttributes
elif payloadAttributes is PayloadAttributesV1:
template payloadAttributesV2(): auto = PayloadAttributesV2(
timestamp: payloadAttributes.timestamp,
prevRandao: payloadAttributes.prevRandao,
suggestedFeeRecipient: payloadAttributes.suggestedFeeRecipient,
withdrawals: @[])
elif payloadAttributes is NoPayloadAttributesType:
when payloadAttributes is Option[PayloadAttributesV2]:
template payloadAttributesV2(): auto =
# Because timestamp and prevRandao are both 0, won't false-positive match
(static(default(PayloadAttributesV2)))
if payloadAttributes.isSome:
payloadAttributes.get
else:
# As timestamp and prevRandao are both 0, won't false-positive match
(static(default(PayloadAttributesV2)))
elif payloadAttributes is Option[PayloadAttributesV1]:
template payloadAttributesV2(): auto =
if payloadAttributes.isSome:
PayloadAttributesV2(
timestamp: payloadAttributes.get.timestamp,
prevRandao: payloadAttributes.get.prevRandao,
suggestedFeeRecipient: payloadAttributes.get.suggestedFeeRecipient,
withdrawals: @[])
else:
# As timestamp and prevRandao are both 0, won't false-positive match
(static(default(PayloadAttributesV2)))
else:
static: doAssert false

View File

@ -214,14 +214,14 @@ proc storeBackfillBlock(
res
from web3/engine_api_types import PayloadExecutionStatus, PayloadStatusV1
from web3/engine_api_types import
PayloadAttributesV1, PayloadAttributesV2, PayloadExecutionStatus,
PayloadStatusV1
from ../eth1/eth1_monitor import
ELManager, NoPayloadAttributes, asEngineExecutionPayload, sendNewPayload,
forkchoiceUpdated
ELManager, asEngineExecutionPayload, sendNewPayload, forkchoiceUpdated
proc expectValidForkchoiceUpdated(
elManager: ELManager,
elManager: ELManager, headBlockPayloadAttributesType: typedesc,
headBlockHash, safeBlockHash, finalizedBlockHash: Eth2Digest,
receivedBlock: ForkySignedBeaconBlock): Future[void] {.async.} =
let
@ -229,7 +229,7 @@ proc expectValidForkchoiceUpdated(
headBlockHash = headBlockHash,
safeBlockHash = safeBlockHash,
finalizedBlockHash = finalizedBlockHash,
payloadAttributes = NoPayloadAttributes)
payloadAttributes = none headBlockPayloadAttributesType)
receivedExecutionBlockHash =
when typeof(receivedBlock).toFork >= ConsensusFork.Bellatrix:
receivedBlock.message.body.execution_payload.block_hash
@ -525,7 +525,7 @@ proc storeBlock*(
# > Client software MAY skip an update of the forkchoice state and MUST
# NOT begin a payload build process if `forkchoiceState.headBlockHash`
# references an ancestor of the head of canonical chain.
# https://github.com/ethereum/execution-apis/blob/v1.0.0-beta.2/src/engine/paris.md#specification-1
# https://github.com/ethereum/execution-apis/blob/v1.0.0-beta.3/src/engine/paris.md#specification-1
#
# However, in practice, an EL client may not have completed importing all
# block headers, so may be unaware of a block's ancestor status.
@ -534,11 +534,27 @@ proc storeBlock*(
# - "Beacon chain gapped" from DAG head to optimistic head,
# - followed by "Beacon chain reorged" from optimistic head back to DAG.
self.consensusManager[].updateHead(newHead.get.blck)
discard await elManager.forkchoiceUpdated(
headBlockHash = self.consensusManager[].optimisticExecutionPayloadHash,
safeBlockHash = newHead.get.safeExecutionPayloadHash,
finalizedBlockHash = newHead.get.finalizedExecutionPayloadHash,
payloadAttributes = NoPayloadAttributes)
template callForkchoiceUpdated(attributes: untyped) =
discard await elManager.forkchoiceUpdated(
headBlockHash = self.consensusManager[].optimisticExecutionPayloadHash,
safeBlockHash = newHead.get.safeExecutionPayloadHash,
finalizedBlockHash = newHead.get.finalizedExecutionPayloadHash,
payloadAttributes = none attributes)
case self.consensusManager.dag.cfg.consensusForkAtEpoch(
newHead.get.blck.bid.slot.epoch)
of ConsensusFork.Capella, ConsensusFork.Deneb:
# https://github.com/ethereum/execution-apis/blob/v1.0.0-beta.3/src/engine/shanghai.md#specification-1
# Consensus layer client MUST call this method instead of
# `engine_forkchoiceUpdatedV1` under any of the following conditions:
# `headBlockHash` references a block which `timestamp` is greater or
# equal to the Shanghai timestamp
callForkchoiceUpdated(PayloadAttributesV2)
of ConsensusFork.Bellatrix:
callForkchoiceUpdated(PayloadAttributesV1)
of ConsensusFork.Phase0, ConsensusFork.Altair:
discard
else:
let
headExecutionPayloadHash =
@ -557,11 +573,22 @@ proc storeBlock*(
if self.consensusManager.checkNextProposer(wallSlot).isNone:
# No attached validator is next proposer, so use non-proposal fcU
await elManager.expectValidForkchoiceUpdated(
headBlockHash = headExecutionPayloadHash,
safeBlockHash = newHead.get.safeExecutionPayloadHash,
finalizedBlockHash = newHead.get.finalizedExecutionPayloadHash,
receivedBlock = signedBlock)
template callForkchoiceUpdated(payloadAttributeType: untyped): auto =
await elManager.expectValidForkchoiceUpdated(
headBlockPayloadAttributesType = payloadAttributeType,
headBlockHash = headExecutionPayloadHash,
safeBlockHash = newHead.get.safeExecutionPayloadHash,
finalizedBlockHash = newHead.get.finalizedExecutionPayloadHash,
receivedBlock = signedBlock)
case self.consensusManager.dag.cfg.consensusForkAtEpoch(
newHead.get.blck.bid.slot.epoch)
of ConsensusFork.Capella, ConsensusFork.Deneb:
callForkchoiceUpdated(payloadAttributeType = PayloadAttributesV2)
of ConsensusFork.Phase0, ConsensusFork.Altair,
ConsensusFork.Bellatrix:
callForkchoiceUpdated(payloadAttributeType = PayloadAttributesV1)
else:
# Some attached validator is next proposer, so prepare payload. As
# updateHead() updated the DAG head, runProposalForkchoiceUpdated,

View File

@ -105,7 +105,12 @@ programMain:
opt = signedBlock.toBlockId(),
wallSlot = getBeaconTime().slotOrZero
withBlck(signedBlock):
when consensusFork >= ConsensusFork.Bellatrix:
when consensusFork >= ConsensusFork.Capella:
# https://github.com/ethereum/execution-apis/blob/v1.0.0-beta.3/src/engine/shanghai.md#specification-1
# Consensus layer client MUST call this method instead of
# `engine_forkchoiceUpdatedV1` under any of the following conditions:
# `headBlockHash` references a block which `timestamp` is greater or
# equal to the Shanghai timestamp
if blck.message.is_execution_block:
template payload(): auto = blck.message.body.execution_payload
@ -115,7 +120,18 @@ programMain:
headBlockHash = payload.block_hash,
safeBlockHash = payload.block_hash, # stub value
finalizedBlockHash = ZERO_HASH,
payloadAttributes = NoPayloadAttributes)
payloadAttributes = none PayloadAttributesV2)
elif consensusFork >= ConsensusFork.Bellatrix:
if blck.message.is_execution_block:
template payload(): auto = blck.message.body.execution_payload
if elManager != nil and not payload.block_hash.isZero:
discard await elManager.newExecutionPayload(payload)
discard await elManager.forkchoiceUpdated(
headBlockHash = payload.block_hash,
safeBlockHash = payload.block_hash, # stub value
finalizedBlockHash = ZERO_HASH,
payloadAttributes = none PayloadAttributesV1)
else: discard
optimisticProcessor = initOptimisticProcessor(
getBeaconTime, optimisticHandler)