Skip to content

Commit

Permalink
support electra block proposals for internal BN validators (#6187)
Browse files Browse the repository at this point in the history
  • Loading branch information
tersec committed Apr 9, 2024
1 parent b8eb518 commit 6ce5d58
Show file tree
Hide file tree
Showing 10 changed files with 116 additions and 42 deletions.
1 change: 0 additions & 1 deletion beacon_chain/beacon_node.nim
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,6 @@ type
processingDelay*: Opt[Duration]
lastValidAttestedBlock*: Opt[BlockSlot]

# TODO stew/sequtils2
template findIt*(s: openArray, predicate: untyped): int =
var res = -1
for i, it {.inject.} in s:
Expand Down
1 change: 0 additions & 1 deletion beacon_chain/consensus_object_pools/spec_cache.nim
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,6 @@ func get_attesting_indices*(shufflingRef: ShufflingRef,
committee_index: CommitteeIndex,
bits: CommitteeValidatorsBits):
seq[ValidatorIndex] =
# TODO sequtils2 mapIt
for idx in get_attesting_indices(shufflingRef, slot, committee_index, bits):
result.add(idx)

Expand Down
3 changes: 3 additions & 0 deletions beacon_chain/spec/eth2_apis/eth2_rest_serialization.nim
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ RestJson.useDefaultSerializationFor(
GetForkScheduleResponse,
GetGenesisResponse,
GetHeaderResponseDeneb,
GetHeaderResponseElectra,
GetKeystoresResponse,
GetNextWithdrawalsResponse,
GetPoolAttesterSlashingsResponse,
Expand Down Expand Up @@ -253,8 +254,10 @@ RestJson.useDefaultSerializationFor(
electra.SignedBeaconBlock,
electra_mev.BlindedBeaconBlock,
electra_mev.BlindedBeaconBlockBody,
electra_mev.BuilderBid,
electra_mev.ExecutionPayloadAndBlobsBundle,
electra_mev.SignedBlindedBeaconBlock,
electra_mev.SignedBuilderBid,
phase0.BeaconBlock,
phase0.BeaconBlockBody,
phase0.BeaconState,
Expand Down
1 change: 1 addition & 0 deletions beacon_chain/spec/eth2_apis/rest_types.nim
Original file line number Diff line number Diff line change
Expand Up @@ -548,6 +548,7 @@ type
GetForkScheduleResponse* = DataEnclosedObject[seq[Fork]]
GetGenesisResponse* = DataEnclosedObject[RestGenesis]
GetHeaderResponseDeneb* = DataVersionEnclosedObject[deneb_mev.SignedBuilderBid]
GetHeaderResponseElectra* = DataVersionEnclosedObject[electra_mev.SignedBuilderBid]
GetNetworkIdentityResponse* = DataEnclosedObject[RestNetworkIdentity]
GetPeerCountResponse* = DataMetaEnclosedObject[RestPeerCount]
GetPeerResponse* = DataMetaEnclosedObject[RestNodePeer]
Expand Down
8 changes: 8 additions & 0 deletions beacon_chain/spec/mev/rest_electra_mev_calls.nim
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ import

export chronos, client, rest_types, eth2_rest_serialization

proc getHeaderElectra*(slot: Slot,
parent_hash: Eth2Digest,
pubkey: ValidatorPubKey
): RestPlainResponse {.
rest, endpoint: "/eth/v1/builder/header/{slot}/{parent_hash}/{pubkey}",
meth: MethodGet, connection: {Dedicated, Close}.}
## https://github.com/ethereum/builder-specs/blob/v0.4.0/apis/builder/header.yaml

proc submitBlindedBlock*(body: electra_mev.SignedBlindedBeaconBlock
): RestPlainResponse {.
rest, endpoint: "/eth/v1/builder/blinded_blocks",
Expand Down
5 changes: 3 additions & 2 deletions beacon_chain/spec/signatures.nim
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,8 @@ proc verify_contribution_and_proof_signature*(
# https://github.com/ethereum/builder-specs/blob/v0.4.0/specs/bellatrix/builder.md#signing
func compute_builder_signing_root(
fork: Fork,
msg: deneb_mev.BuilderBid | ValidatorRegistrationV1): Eth2Digest =
msg: deneb_mev.BuilderBid | electra_mev.BuilderBid |
ValidatorRegistrationV1): Eth2Digest =
# Uses genesis fork version regardless
doAssert fork.current_version == fork.previous_version

Expand All @@ -386,7 +387,7 @@ proc get_builder_signature*(
blsSign(privkey, signing_root.data)

proc verify_builder_signature*(
fork: Fork, msg: deneb_mev.BuilderBid,
fork: Fork, msg: deneb_mev.BuilderBid | electra_mev.BuilderBid,
pubkey: ValidatorPubKey | CookedPubKey, signature: SomeSig): bool =
let signing_root = compute_builder_signing_root(fork, msg)
blsVerify(pubkey, signing_root.data, signature)
Expand Down
1 change: 0 additions & 1 deletion beacon_chain/spec/state_transition_block.nim
Original file line number Diff line number Diff line change
Expand Up @@ -573,7 +573,6 @@ proc process_sync_aggregate*(
# Apply participant and proposer rewards
let indices = get_sync_committee_cache(state, cache).current_sync_committee

# TODO could use a sequtils2 zipIt
for i in 0 ..< min(
state.current_sync_committee.pubkeys.len,
sync_aggregate.sync_committee_bits.len):
Expand Down
118 changes: 83 additions & 35 deletions beacon_chain/validators/beacon_validators.nim
Original file line number Diff line number Diff line change
Expand Up @@ -565,7 +565,8 @@ proc makeBeaconBlockForHeadAndSlot*(
kzg_commitments = Opt.none(KzgCommitments))

proc getBlindedExecutionPayload[
EPH: deneb_mev.BlindedExecutionPayloadAndBlobsBundle](
EPH: deneb_mev.BlindedExecutionPayloadAndBlobsBundle |
electra_mev.BlindedExecutionPayloadAndBlobsBundle](
node: BeaconNode, payloadBuilderClient: RestClientRef, slot: Slot,
executionBlockHash: Eth2Digest, pubkey: ValidatorPubKey):
Future[BlindedBlockResult[EPH]] {.async: (raises: [CancelledError, RestError]).} =
Expand All @@ -587,6 +588,22 @@ proc getBlindedExecutionPayload[
"Unable to decode Deneb blinded header: " & $res.error &
" with HTTP status " & $response.status & ", Content-Type " &
$response.contentType & " and content " & $response.data)
elif EPH is electra_mev.BlindedExecutionPayloadAndBlobsBundle:
let
response = awaitWithTimeout(
payloadBuilderClient.getHeaderElectra(
slot, executionBlockHash, pubkey),
BUILDER_PROPOSAL_DELAY_TOLERANCE):
return err "Timeout obtaining Electra blinded header from builder"

res = decodeBytes(
GetHeaderResponseElectra, response.data, response.contentType)

blindedHeader = res.valueOr:
return err(
"Unable to decode Electra blinded header: " & $res.error &
" with HTTP status " & $response.status & ", Content-Type " &
$response.contentType & " and content " & $response.data)
else:
static: doAssert false

Expand All @@ -599,15 +616,12 @@ proc getBlindedExecutionPayload[
blindedHeader.data.message.pubkey, blindedHeader.data.signature):
return err "getBlindedExecutionPayload: signature verification failed"

when EPH is deneb_mev.BlindedExecutionPayloadAndBlobsBundle:
template builderBid: untyped = blindedHeader.data.message
return ok(BuilderBid[EPH](
blindedBlckPart: EPH(
execution_payload_header: builderBid.header,
blob_kzg_commitments: builderBid.blob_kzg_commitments),
executionPayloadValue: builderBid.value))
else:
static: doAssert false
template builderBid: untyped = blindedHeader.data.message
return ok(BuilderBid[EPH](
blindedBlckPart: EPH(
execution_payload_header: builderBid.header,
blob_kzg_commitments: builderBid.blob_kzg_commitments),
executionPayloadValue: builderBid.value))

from ./message_router_mev import
copyFields, getFieldNames, unblindAndRouteBlockMEV
Expand All @@ -634,6 +648,28 @@ proc constructSignableBlindedBlock[T: deneb_mev.SignedBlindedBeaconBlock](

blindedBlock

proc constructSignableBlindedBlock[T: electra_mev.SignedBlindedBeaconBlock](
blck: electra.BeaconBlock,
blindedBundle: electra_mev.BlindedExecutionPayloadAndBlobsBundle): T =
# Leaves signature field default, to be filled in by caller
const
blckFields = getFieldNames(typeof(blck))
blckBodyFields = getFieldNames(typeof(blck.body))

var blindedBlock: T

# https://github.com/ethereum/builder-specs/blob/v0.4.0/specs/bellatrix/validator.md#block-proposal
copyFields(blindedBlock.message, blck, blckFields)
copyFields(blindedBlock.message.body, blck.body, blckBodyFields)
assign(
blindedBlock.message.body.execution_payload_header,
blindedBundle.execution_payload_header)
assign(
blindedBlock.message.body.blob_kzg_commitments,
blindedBundle.blob_kzg_commitments)

blindedBlock

func constructPlainBlindedBlock[T: deneb_mev.BlindedBeaconBlock](
blck: ForkyBeaconBlock,
blindedBundle: deneb_mev.BlindedExecutionPayloadAndBlobsBundle): T =
Expand All @@ -658,7 +694,9 @@ func constructPlainBlindedBlock[T: deneb_mev.BlindedBeaconBlock](

blindedBlock

proc blindedBlockCheckSlashingAndSign[T: deneb_mev.SignedBlindedBeaconBlock](
proc blindedBlockCheckSlashingAndSign[
T: deneb_mev.SignedBlindedBeaconBlock |
electra_mev.SignedBlindedBeaconBlock](
node: BeaconNode, slot: Slot, validator: AttachedValidator,
validator_index: ValidatorIndex, nonsignedBlindedBlock: T):
Future[Result[T, string]] {.async: (raises: [CancelledError]).} =
Expand Down Expand Up @@ -690,17 +728,21 @@ proc blindedBlockCheckSlashingAndSign[T: deneb_mev.SignedBlindedBeaconBlock](

return ok blindedBlock

proc getUnsignedBlindedBeaconBlock[T: deneb_mev.SignedBlindedBeaconBlock](
proc getUnsignedBlindedBeaconBlock[
T: deneb_mev.SignedBlindedBeaconBlock |
electra_mev.SignedBlindedBeaconBlock](
node: BeaconNode, slot: Slot,
validator_index: ValidatorIndex, forkedBlock: ForkedBeaconBlock,
executionPayloadHeader: capella.ExecutionPayloadHeader |
deneb_mev.BlindedExecutionPayloadAndBlobsBundle):
executionPayloadHeader: deneb_mev.BlindedExecutionPayloadAndBlobsBundle |
electra_mev.BlindedExecutionPayloadAndBlobsBundle):
Result[T, string] =
withBlck(forkedBlock):
when consensusFork >= ConsensusFork.Deneb:
when not (
(T is deneb_mev.SignedBlindedBeaconBlock and
consensusFork == ConsensusFork.Deneb)):
consensusFork == ConsensusFork.Deneb) or
(T is electra_mev.SignedBlindedBeaconBlock and
consensusFork == ConsensusFork.Electra)):
return err("getUnsignedBlindedBeaconBlock: mismatched block/payload types")
else:
return ok constructSignableBlindedBlock[T](
Expand All @@ -709,8 +751,8 @@ proc getUnsignedBlindedBeaconBlock[T: deneb_mev.SignedBlindedBeaconBlock](
return err("getUnsignedBlindedBeaconBlock: attempt to construct pre-Deneb blinded block")

proc getBlindedBlockParts[
EPH: capella.ExecutionPayloadHeader |
deneb_mev.BlindedExecutionPayloadAndBlobsBundle](
EPH: deneb_mev.BlindedExecutionPayloadAndBlobsBundle |
electra_mev.BlindedExecutionPayloadAndBlobsBundle](
node: BeaconNode, payloadBuilderClient: RestClientRef, head: BlockRef,
pubkey: ValidatorPubKey, slot: Slot, randao: ValidatorSig,
validator_index: ValidatorIndex, graffiti: GraffitiBytes):
Expand Down Expand Up @@ -755,18 +797,7 @@ proc getBlindedBlockParts[
#
# This doesn't have withdrawals, which each node has regardless of engine or
# builder API. makeBeaconBlockForHeadAndSlot fills it in later.
when EPH is capella.ExecutionPayloadHeader:
type PayloadType = capella.ExecutionPayloadForSigning
template actualEPH: untyped = executionPayloadHeader.get.blindedBlckPart
let withdrawals_root =
Opt.some executionPayloadHeader.get.blindedBlckPart.withdrawals_root
const kzg_commitments = Opt.none KzgCommitments

var shimExecutionPayload: PayloadType
copyFields(
shimExecutionPayload.executionPayload,
executionPayloadHeader.get.blindedBlckPart, getFieldNames(EPH))
elif EPH is deneb_mev.BlindedExecutionPayloadAndBlobsBundle:
when EPH is deneb_mev.BlindedExecutionPayloadAndBlobsBundle:
type PayloadType = deneb.ExecutionPayloadForSigning
template actualEPH: untyped =
executionPayloadHeader.get.blindedBlckPart.execution_payload_header
Expand All @@ -780,6 +811,20 @@ proc getBlindedBlockParts[
deneb_mev.BlindedExecutionPayloadAndBlobsBundle.execution_payload_header
copyFields(
shimExecutionPayload.executionPayload, actualEPH, getFieldNames(DenebEPH))
elif EPH is electra_mev.BlindedExecutionPayloadAndBlobsBundle:
type PayloadType = electra.ExecutionPayloadForSigning
template actualEPH: untyped =
executionPayloadHeader.get.blindedBlckPart.execution_payload_header
let
withdrawals_root = Opt.some actualEPH.withdrawals_root
kzg_commitments = Opt.some(
executionPayloadHeader.get.blindedBlckPart.blob_kzg_commitments)

var shimExecutionPayload: PayloadType
type ElectraEPH =
electra_mev.BlindedExecutionPayloadAndBlobsBundle.execution_payload_header
copyFields(
shimExecutionPayload.executionPayload, actualEPH, getFieldNames(ElectraEPH))
else:
static: doAssert false

Expand All @@ -803,7 +848,9 @@ proc getBlindedBlockParts[
forkedBlck.consensusBlockValue,
forkedBlck.blck))

proc getBuilderBid[SBBB: deneb_mev.SignedBlindedBeaconBlock](
proc getBuilderBid[
SBBB: deneb_mev.SignedBlindedBeaconBlock |
electra_mev.SignedBlindedBeaconBlock](
node: BeaconNode, payloadBuilderClient: RestClientRef, head: BlockRef,
validator_pubkey: ValidatorPubKey, slot: Slot, randao: ValidatorSig,
graffitiBytes: GraffitiBytes, validator_index: ValidatorIndex):
Expand All @@ -812,6 +859,8 @@ proc getBuilderBid[SBBB: deneb_mev.SignedBlindedBeaconBlock](
## Used by the BN's own validators, but not the REST server
when SBBB is deneb_mev.SignedBlindedBeaconBlock:
type EPH = deneb_mev.BlindedExecutionPayloadAndBlobsBundle
elif SBBB is electra_mev.SignedBlindedBeaconBlock:
type EPH = electra_mev.BlindedExecutionPayloadAndBlobsBundle
else:
static: doAssert false

Expand Down Expand Up @@ -842,7 +891,9 @@ proc getBuilderBid[SBBB: deneb_mev.SignedBlindedBeaconBlock](

proc proposeBlockMEV(
node: BeaconNode, payloadBuilderClient: RestClientRef,
blindedBlock: deneb_mev.SignedBlindedBeaconBlock):
blindedBlock:
deneb_mev.SignedBlindedBeaconBlock |
electra_mev.SignedBlindedBeaconBlock):
Future[Result[BlockRef, string]] {.async: (raises: [CancelledError]).} =
let unblindedBlockRef = await node.unblindAndRouteBlockMEV(
payloadBuilderClient, blindedBlock)
Expand Down Expand Up @@ -1198,10 +1249,7 @@ proc proposeBlock(node: BeaconNode,
genesis_validators_root, node.config.localBlockValueBoost)

return withConsensusFork(node.dag.cfg.consensusForkAtEpoch(slot.epoch)):
when consensusFork >= ConsensusFork.Electra:
debugRaiseAssert "can't propose electra block"
return default(BlockRef)
elif consensusFork >= ConsensusFork.Deneb:
when consensusFork >= ConsensusFork.Deneb:
proposeBlockContinuation(
consensusFork.SignedBlindedBeaconBlock,
consensusFork.ExecutionPayloadForSigning)
Expand Down
4 changes: 3 additions & 1 deletion beacon_chain/validators/message_router_mev.nim
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,13 @@ macro copyFields*(
dst: untyped, src: untyped, fieldNames: static[seq[string]]): untyped =
result = newStmtList()
for name in fieldNames:
debugRaiseAssert "deposit_receipts_root and exits_root are not currently filled in anywhere properly, so blinded electra proposals will fail"
if name notin [
# These fields are the ones which vary between the blinded and
# unblinded objects, and can't simply be copied.
"transactions_root", "execution_payload",
"execution_payload_header", "body", "withdrawals_root"]:
"execution_payload_header", "body", "withdrawals_root",
"deposit_receipts_root", "exits_root"]:
# TODO use stew/assign2
result.add newAssignment(
newDotExpr(dst, ident(name)), newDotExpr(src, ident(name)))
Expand Down
16 changes: 15 additions & 1 deletion beacon_chain/validators/validator_pool.nim
Original file line number Diff line number Diff line change
Expand Up @@ -502,7 +502,8 @@ proc getBlockSignature*(v: AttachedValidator, fork: Fork,
block_root: Eth2Digest,
blck: ForkedBeaconBlock | ForkedBlindedBeaconBlock |
ForkedMaybeBlindedBeaconBlock |
deneb_mev.BlindedBeaconBlock
deneb_mev.BlindedBeaconBlock |
electra_mev.BlindedBeaconBlock
): Future[SignatureResult]
{.async: (raises: [CancelledError]).} =
type SomeBlockBody =
Expand Down Expand Up @@ -578,6 +579,19 @@ proc getBlockSignature*(v: AttachedValidator, fork: Fork,
Web3SignerForkedBeaconBlock(kind: ConsensusFork.Deneb,
data: blck.toBeaconBlockHeader),
proofs)
elif blck is electra_mev.BlindedBeaconBlock:
case v.data.remoteType
of RemoteSignerType.Web3Signer:
Web3SignerRequest.init(fork, genesis_validators_root,
Web3SignerForkedBeaconBlock(kind: ConsensusFork.Electra,
data: blck.toBeaconBlockHeader))
of RemoteSignerType.VerifyingWeb3Signer:
let proofs = blockPropertiesProofs(
blck.body, electraIndex)
Web3SignerRequest.init(fork, genesis_validators_root,
Web3SignerForkedBeaconBlock(kind: ConsensusFork.Electra,
data: blck.toBeaconBlockHeader),
proofs)
elif blck is ForkedMaybeBlindedBeaconBlock:
withForkyMaybeBlindedBlck(blck):
# TODO why isn't this a case statement
Expand Down

0 comments on commit 6ce5d58

Please sign in to comment.