mirror of
https://github.com/status-im/nimbus-eth2.git
synced 2025-01-12 15:24:14 +00:00
b32205de7c
* reworked some of the das core specs, pr'd to check whether whether the conflicting type issue is centric to my machine or not * bumped nim-blscurve to 9c6e80c6109133c0af3025654f5a8820282cff05, same as unstable * bumped nim-eth2-scenarios, nim-nat-traversal at par with unstable, added more pathches, made peerdas devnet branch backward compatible, peerdas passing new ssz tests as per alpha3, disabled electra fixture tests, as branch hasn't been rebased for a while * refactor test fixture files * rm: serializeDataColumn * refactor: took data columns extracted from blobs during block proposal to the heap * disable blob broadcast in pd devnet * fix addBlock in message router * fix: data column iterator * added debug checkpoints to check CI * refactor if else conditions * add: updated das core specs to alpha 3, and unit tests pass
157 lines
6.5 KiB
Nim
157 lines
6.5 KiB
Nim
# beacon_chain
|
|
# Copyright (c) 2022-2024 Status Research & Development GmbH
|
|
# Licensed and distributed under either of
|
|
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
|
|
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
|
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
|
|
|
{.push raises: [].}
|
|
|
|
import std/macros
|
|
import metrics
|
|
import stew/assign2
|
|
import ../beacon_node
|
|
|
|
from ../spec/datatypes/bellatrix import SignedBeaconBlock
|
|
from ../spec/mev/rest_deneb_mev_calls import submitBlindedBlock
|
|
from ../spec/mev/rest_electra_mev_calls import submitBlindedBlock
|
|
|
|
const
|
|
BUILDER_BLOCK_SUBMISSION_DELAY_TOLERANCE = 5.seconds
|
|
|
|
declareCounter beacon_block_builder_proposed,
|
|
"Number of beacon chain blocks produced using an external block builder"
|
|
|
|
func getFieldNames*(x: typedesc[auto]): seq[string] {.compileTime.} =
|
|
var res: seq[string]
|
|
for name, _ in fieldPairs(default(x)):
|
|
res.add name
|
|
res
|
|
|
|
macro copyFields*(
|
|
dst: untyped, src: untyped, fieldNames: static[seq[string]]): untyped =
|
|
result = newStmtList()
|
|
for name in fieldNames:
|
|
debugComment "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",
|
|
"deposit_receipts_root", "withdrawal_requests_root"]:
|
|
# TODO use stew/assign2
|
|
result.add newAssignment(
|
|
newDotExpr(dst, ident(name)), newDotExpr(src, ident(name)))
|
|
|
|
proc unblindAndRouteBlockMEV*(
|
|
node: BeaconNode, payloadBuilderRestClient: RestClientRef,
|
|
blindedBlock:
|
|
deneb_mev.SignedBlindedBeaconBlock |
|
|
electra_mev.SignedBlindedBeaconBlock):
|
|
Future[Result[Opt[BlockRef], string]] {.async: (raises: [CancelledError]).} =
|
|
const consensusFork = typeof(blindedBlock).kind
|
|
|
|
info "Proposing blinded Builder API block",
|
|
blindedBlock = shortLog(blindedBlock)
|
|
|
|
# By time submitBlindedBlock is called, must already have done slashing
|
|
# protection check
|
|
let response =
|
|
try:
|
|
await payloadBuilderRestClient.submitBlindedBlock(blindedBlock).
|
|
wait(BUILDER_BLOCK_SUBMISSION_DELAY_TOLERANCE)
|
|
# From here on, including error paths, disallow local EL production by
|
|
# returning Opt.some, regardless of whether on head or newBlock.
|
|
except AsyncTimeoutError:
|
|
return err("Submitting blinded block timed out")
|
|
except RestEncodingError as exc:
|
|
return err(
|
|
"REST encoding error submitting blinded block, reason " & exc.msg)
|
|
except RestDnsResolveError as exc:
|
|
return err(
|
|
"REST unable to resolve remote host, reason " & exc.msg)
|
|
except RestCommunicationError as exc:
|
|
return err(
|
|
"REST unable to communicate with remote host, reason " & exc.msg)
|
|
|
|
const httpOk = 200
|
|
if response.status != httpOk:
|
|
# https://github.com/ethereum/builder-specs/blob/v0.4.0/specs/bellatrix/validator.md#proposer-slashing
|
|
# This means if a validator publishes a signature for a
|
|
# `BlindedBeaconBlock` (via a dissemination of a
|
|
# `SignedBlindedBeaconBlock`) then the validator **MUST** not use the
|
|
# local build process as a fallback, even in the event of some failure
|
|
# with the external builder network.
|
|
return err("submitBlindedBlock failed with HTTP error code " &
|
|
$response.status & ": " & $shortLog(blindedBlock))
|
|
|
|
when blindedBlock is deneb_mev.SignedBlindedBeaconBlock:
|
|
let res = decodeBytes(
|
|
SubmitBlindedBlockResponseDeneb, response.data, response.contentType)
|
|
elif blindedBlock is electra_mev.SignedBlindedBeaconBlock:
|
|
let res = decodeBytes(
|
|
SubmitBlindedBlockResponseElectra, response.data, response.contentType)
|
|
else:
|
|
static: doAssert false
|
|
|
|
let bundle = res.valueOr:
|
|
return err("Could not decode " & $consensusFork & " blinded block: " & $res.error &
|
|
" with HTTP status " & $response.status & ", Content-Type " &
|
|
$response.contentType & " and content " & $response.data)
|
|
|
|
template execution_payload: untyped = bundle.data.execution_payload
|
|
|
|
if hash_tree_root(blindedBlock.message.body.execution_payload_header) !=
|
|
hash_tree_root(execution_payload):
|
|
return err("unblinded payload doesn't match blinded payload header: " &
|
|
$blindedBlock.message.body.execution_payload_header)
|
|
|
|
# Signature provided is consistent with unblinded execution payload,
|
|
# so construct full beacon block
|
|
# https://github.com/ethereum/builder-specs/blob/v0.4.0/specs/bellatrix/validator.md#block-proposal
|
|
var signedBlock = consensusFork.SignedBeaconBlock(
|
|
signature: blindedBlock.signature)
|
|
copyFields(
|
|
signedBlock.message, blindedBlock.message,
|
|
getFieldNames(typeof(signedBlock.message)))
|
|
copyFields(
|
|
signedBlock.message.body, blindedBlock.message.body,
|
|
getFieldNames(typeof(signedBlock.message.body)))
|
|
assign(signedBlock.message.body.execution_payload, execution_payload)
|
|
signedBlock.root = hash_tree_root(signedBlock.message)
|
|
doAssert signedBlock.root == hash_tree_root(blindedBlock.message)
|
|
|
|
let blobsOpt =
|
|
when consensusFork >= ConsensusFork.Deneb:
|
|
template blobs_bundle: untyped = bundle.data.blobs_bundle
|
|
if blindedBlock.message.body.blob_kzg_commitments !=
|
|
bundle.data.blobs_bundle.commitments:
|
|
return err("unblinded blobs bundle has unexpected commitments")
|
|
let ok = verifyProofs(
|
|
blobs_bundle.blobs.mapIt(KzgBlob(bytes: it)),
|
|
asSeq blobs_bundle.commitments,
|
|
asSeq blobs_bundle.proofs).valueOr:
|
|
return err("unblinded blobs bundle fails verification")
|
|
if not ok:
|
|
return err("unblinded blobs bundle is invalid")
|
|
Opt.some(signedBlock.create_blob_sidecars(
|
|
blobs_bundle.proofs, blobs_bundle.blobs))
|
|
else:
|
|
Opt.none(seq[BlobSidecar])
|
|
|
|
debug "unblindAndRouteBlockMEV: proposing unblinded block",
|
|
blck = shortLog(signedBlock)
|
|
|
|
let newBlockRef =
|
|
(await node.router.routeSignedBeaconBlock(signedBlock, blobsOpt)).valueOr:
|
|
# submitBlindedBlock has run, so don't allow fallback to run
|
|
return err("routeSignedBeaconBlock error") # Errors logged in router
|
|
|
|
if newBlockRef.isSome:
|
|
beacon_block_builder_proposed.inc()
|
|
notice "Block proposed (MEV)",
|
|
blockRoot = shortLog(signedBlock.root), blck = shortLog(signedBlock),
|
|
signature = shortLog(signedBlock.signature)
|
|
|
|
ok newBlockRef
|