post-merge Bellatrix block proposals (#3570)
* post-merge Bellatrix block proposals * tolerate running without an Eth1Monitor better * remove obsolete comment * use correct empty receipts root * handle invalid CLI parameters in parseCmdArg overloads
This commit is contained in:
parent
bacc1ff8ed
commit
ab1fac7236
|
@ -513,6 +513,13 @@ type
|
|||
defaultValue: 128
|
||||
name: "safe-slots-to-import-optimistically" }: uint64
|
||||
|
||||
# Same option as appears in Lighthouse and Prysm
|
||||
# https://lighthouse-book.sigmaprime.io/suggested-fee-recipient.html
|
||||
# https://github.com/prysmaticlabs/prysm/pull/10312
|
||||
suggestedFeeRecipient* {.
|
||||
desc: "Suggested fee recipient"
|
||||
name: "suggested-fee-recipient" .}: Option[Address]
|
||||
|
||||
of BNStartUpCmd.createTestnet:
|
||||
testnetDepositsFile* {.
|
||||
desc: "A LaunchPad deposits file for the genesis state validators"
|
||||
|
@ -1099,6 +1106,13 @@ proc readValue*(r: var TomlReader, a: var WalletName)
|
|||
except CatchableError:
|
||||
r.raiseUnexpectedValue("string expected")
|
||||
|
||||
proc readValue*(r: var TomlReader, a: var Address)
|
||||
{.raises: [Defect, IOError, SerializationError].} =
|
||||
try:
|
||||
a = parseCmdArg(Address, r.readValue(string))
|
||||
except CatchableError:
|
||||
r.raiseUnexpectedValue("string expected")
|
||||
|
||||
proc loadEth2Network*(config: BeaconNodeConf): Eth2NetworkMetadata {.raises: [Defect, IOError].} =
|
||||
network_name.set(2, labelValues = [config.eth2Network.get(otherwise = "mainnet")])
|
||||
when not defined(gnosisChainBinary):
|
||||
|
|
|
@ -1022,7 +1022,7 @@ proc doStop(m: Eth1Monitor) {.async.} =
|
|||
m.dataProvider = nil
|
||||
|
||||
proc ensureDataProvider*(m: Eth1Monitor) {.async.} =
|
||||
if not m.dataProvider.isNil:
|
||||
if m.isNil or not m.dataProvider.isNil:
|
||||
return
|
||||
|
||||
let web3Url = m.web3Urls[m.startIdx mod m.web3Urls.len]
|
||||
|
|
|
@ -376,6 +376,10 @@ proc newExecutionPayload*(
|
|||
UInt256.fromBytesLE(executionPayload.base_fee_per_gas.data),
|
||||
numTransactions = executionPayload.transactions.len
|
||||
|
||||
if eth1Monitor.isNil:
|
||||
info "newPayload: attempting to process execution payload without an Eth1Monitor. Ensure --web3-url setting is correct."
|
||||
return PayloadExecutionStatus.syncing
|
||||
|
||||
try:
|
||||
let
|
||||
payloadResponse =
|
||||
|
@ -383,7 +387,7 @@ proc newExecutionPayload*(
|
|||
eth1Monitor.newPayload(
|
||||
executionPayload.asEngineExecutionPayload),
|
||||
web3Timeout):
|
||||
debug "newPayload: newPayload timed out"
|
||||
info "newPayload: newPayload timed out"
|
||||
PayloadStatusV1(status: PayloadExecutionStatus.syncing)
|
||||
payloadStatus = payloadResponse.status
|
||||
|
||||
|
@ -432,7 +436,7 @@ proc runQueueProcessingLoop*(self: ref BlockProcessor) {.async.} =
|
|||
self.consensusManager.eth1Monitor,
|
||||
blck.blck.bellatrixData.message.body.execution_payload)
|
||||
except CatchableError as err:
|
||||
debug "runQueueProcessingLoop: newExecutionPayload failed",
|
||||
debug "runQueueProcessingLoop: newPayload failed",
|
||||
err = err.msg
|
||||
PayloadExecutionStatus.syncing
|
||||
else:
|
||||
|
@ -502,14 +506,6 @@ proc runQueueProcessingLoop*(self: ref BlockProcessor) {.async.} =
|
|||
|
||||
if executionPayloadStatus == PayloadExecutionStatus.valid and
|
||||
hasExecutionPayload:
|
||||
let
|
||||
headBlockRoot = self.consensusManager.dag.head.executionBlockRoot
|
||||
|
||||
finalizedBlockRoot =
|
||||
if not isZero(
|
||||
self.consensusManager.dag.finalizedHead.blck.executionBlockRoot):
|
||||
self.consensusManager.dag.finalizedHead.blck.executionBlockRoot
|
||||
else:
|
||||
default(Eth2Digest)
|
||||
|
||||
await self.runForkchoiceUpdated(headBlockRoot, finalizedBlockRoot)
|
||||
await self.runForkchoiceUpdated(
|
||||
self.consensusManager.dag.head.executionBlockRoot,
|
||||
self.consensusManager.dag.finalizedHead.blck.executionBlockRoot)
|
||||
|
|
|
@ -169,7 +169,9 @@ proc loadEth2NetworkMetadata*(path: string, eth1Network = none(Eth1Network)): Et
|
|||
let data = (genesisData[0 ..< 40].toHex())
|
||||
data in [
|
||||
# Prater
|
||||
"60F4596000000000043DB0D9A83813551EE2F33450D23797757D430911A9320530AD8A0EABC43EFB"
|
||||
"60F4596000000000043DB0D9A83813551EE2F33450D23797757D430911A9320530AD8A0EABC43EFB",
|
||||
# Kiln
|
||||
"0C572B620000000099B09FCD43E5905236C370F184056BEC6E6638CFC31A323B304FC4AA789CB4AD"
|
||||
]
|
||||
else:
|
||||
false
|
||||
|
|
|
@ -506,3 +506,30 @@ func compute_timestamp_at_slot*(state: ForkyBeaconState, slot: Slot): uint64 =
|
|||
# Note: This function is unsafe with respect to overflows and underflows.
|
||||
let slots_since_genesis = slot - GENESIS_SLOT
|
||||
state.genesis_time + slots_since_genesis * SECONDS_PER_SLOT
|
||||
|
||||
# https://github.com/ethereum/consensus-specs/blob/v1.1.10/tests/core/pyspec/eth2spec/test/helpers/execution_payload.py#L1-L31
|
||||
func build_empty_execution_payload*(state: bellatrix.BeaconState): ExecutionPayload =
|
||||
## Assuming a pre-state of the same slot, build a valid ExecutionPayload
|
||||
## without any transactions.
|
||||
let
|
||||
latest = state.latest_execution_payload_header
|
||||
timestamp = compute_timestamp_at_slot(state, state.slot)
|
||||
randao_mix = get_randao_mix(state, get_current_epoch(state))
|
||||
|
||||
var payload = ExecutionPayload(
|
||||
parent_hash: latest.block_hash,
|
||||
state_root: latest.state_root, # no changes to the state
|
||||
receipts_root: static(Eth2Digest.fromHex(
|
||||
"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")),
|
||||
block_number: latest.block_number + 1,
|
||||
prev_randao: randao_mix,
|
||||
gas_limit: latest.gas_limit, # retain same limit
|
||||
gas_used: 0, # empty block, 0 gas
|
||||
timestamp: timestamp,
|
||||
base_fee_per_gas: latest.base_fee_per_gas) # retain same base_fee
|
||||
|
||||
payload.block_hash = withEth2Hash:
|
||||
h.update payload.hash_tree_root().data
|
||||
h.update cast[array[13, uint8]]("FAKE RLP HASH")
|
||||
|
||||
payload
|
||||
|
|
|
@ -41,6 +41,7 @@ import
|
|||
|
||||
from eth/async_utils import awaitWithTimeout
|
||||
from web3/engine_api import ForkchoiceUpdatedResponse
|
||||
from web3/engine_api_types import PayloadExecutionStatus
|
||||
|
||||
# Metrics for tracking attestation and beacon block loss
|
||||
const delayBuckets = [-Inf, -4.0, -2.0, -1.0, -0.5, -0.1, -0.05,
|
||||
|
@ -434,7 +435,7 @@ proc forkchoice_updated(state: bellatrix.BeaconState,
|
|||
head_block_hash, finalized_block_hash, timestamp, random.data,
|
||||
fee_recipient),
|
||||
web3Timeout):
|
||||
debug "forkchoice_updated: forkchoiceUpdated timed out"
|
||||
info "forkchoice_updated: forkchoiceUpdated timed out"
|
||||
default(ForkchoiceUpdatedResponse)
|
||||
payloadId = forkchoiceResponse.payloadId
|
||||
|
||||
|
@ -443,6 +444,65 @@ proc forkchoice_updated(state: bellatrix.BeaconState,
|
|||
else:
|
||||
none(bellatrix.PayloadID)
|
||||
|
||||
proc get_execution_payload(
|
||||
payload_id: Option[bellatrix.PayloadId], execution_engine: Eth1Monitor):
|
||||
Future[bellatrix.ExecutionPayload] {.async.} =
|
||||
return if payload_id.isNone():
|
||||
# Pre-merge, empty payload
|
||||
default(bellatrix.ExecutionPayload)
|
||||
else:
|
||||
asConsensusExecutionPayload(
|
||||
await execution_engine.getPayload(payload_id.get))
|
||||
|
||||
proc getExecutionPayload(node: BeaconNode, proposalState: auto):
|
||||
Future[ExecutionPayload] {.async.} =
|
||||
# https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/bellatrix/validator.md#executionpayload
|
||||
|
||||
# Only current hardfork with execution payloads is Bellatrix
|
||||
static: doAssert high(BeaconStateFork) == BeaconStateFork.Bellatrix
|
||||
template empty_execution_payload(): auto =
|
||||
build_empty_execution_payload(proposalState.bellatrixData.data)
|
||||
|
||||
if node.eth1Monitor.isNil:
|
||||
warn "getExecutionPayload: eth1Monitor not initialized; using empty execution payload"
|
||||
return empty_execution_payload
|
||||
|
||||
try:
|
||||
# Minimize window for Eth1 monitor to shut down connection
|
||||
await node.consensusManager.eth1Monitor.ensureDataProvider()
|
||||
|
||||
let
|
||||
feeRecipient =
|
||||
if node.config.suggestedFeeRecipient.isSome:
|
||||
node.config.suggestedFeeRecipient.get
|
||||
else:
|
||||
default(Eth1Address)
|
||||
latestHead =
|
||||
if not node.dag.head.executionBlockRoot.isZero:
|
||||
node.dag.head.executionBlockRoot
|
||||
else:
|
||||
default(Eth2Digest)
|
||||
latestFinalized = node.dag.finalizedHead.blck.executionBlockRoot
|
||||
payload_id = (await forkchoice_updated(
|
||||
proposalState.bellatrixData.data, latestHead, latestFinalized,
|
||||
feeRecipient, node.consensusManager.eth1Monitor))
|
||||
payload = await get_execution_payload(
|
||||
payload_id, node.consensusManager.eth1Monitor)
|
||||
executionPayloadStatus =
|
||||
await node.consensusManager.eth1Monitor.newExecutionPayload(
|
||||
payload)
|
||||
|
||||
if executionPayloadStatus != PayloadExecutionStatus.valid:
|
||||
info "getExecutionPayload: newExecutionPayload not valid; using empty execution payload",
|
||||
executionPayloadStatus
|
||||
return empty_execution_payload
|
||||
|
||||
return payload
|
||||
except CatchableError as err:
|
||||
error "Error creating non-empty execution payload; using empty execution payload",
|
||||
msg = err.msg
|
||||
return empty_execution_payload
|
||||
|
||||
proc makeBeaconBlockForHeadAndSlot*(node: BeaconNode,
|
||||
randao_reveal: ValidatorSig,
|
||||
validator_index: ValidatorIndex,
|
||||
|
@ -472,6 +532,9 @@ proc makeBeaconBlockForHeadAndSlot*(node: BeaconNode,
|
|||
warn "Eth1 deposits not available. Skipping block proposal", slot
|
||||
return ForkedBlockResult.err("Eth1 deposits not available")
|
||||
|
||||
# Only current hardfork with execution payloads is Bellatrix
|
||||
static: doAssert high(BeaconStateFork) == BeaconStateFork.Bellatrix
|
||||
|
||||
let exits = withState(state):
|
||||
node.exitPool[].getBeaconBlockExits(state.data)
|
||||
let res = makeBeaconBlock(
|
||||
|
@ -488,7 +551,13 @@ proc makeBeaconBlockForHeadAndSlot*(node: BeaconNode,
|
|||
SyncAggregate.init()
|
||||
else:
|
||||
node.syncCommitteeMsgPool[].produceSyncAggregate(head.root),
|
||||
default(bellatrix.ExecutionPayload),
|
||||
if slot.epoch < node.dag.cfg.BELLATRIX_FORK_EPOCH or
|
||||
# TODO when Eth1Monitor TTD following comes in, actually detect
|
||||
# transition block directly
|
||||
not is_merge_transition_complete(proposalState.bellatrixData.data):
|
||||
default(bellatrix.ExecutionPayload)
|
||||
else:
|
||||
(await getExecutionPayload(node, proposalState)),
|
||||
noRollback, # Temporary state - no need for rollback
|
||||
cache)
|
||||
if res.isErr():
|
||||
|
|
|
@ -43,33 +43,6 @@ func sign_block(state: ForkyBeaconState, blck: var ForkySignedBeaconBlock) =
|
|||
blck.root,
|
||||
privkey).toValidatorSig()
|
||||
|
||||
# https://github.com/ethereum/consensus-specs/blob/v1.1.10/tests/core/pyspec/eth2spec/test/helpers/execution_payload.py#L1-L31
|
||||
func build_empty_execution_payload(state: bellatrix.BeaconState): ExecutionPayload =
|
||||
## Assuming a pre-state of the same slot, build a valid ExecutionPayload
|
||||
## without any transactions.
|
||||
let
|
||||
latest = state.latest_execution_payload_header
|
||||
timestamp = compute_timestamp_at_slot(state, state.slot)
|
||||
randao_mix = get_randao_mix(state, get_current_epoch(state))
|
||||
|
||||
var payload = ExecutionPayload(
|
||||
parent_hash: latest.block_hash,
|
||||
state_root: latest.state_root, # no changes to the state
|
||||
receipts_root: Eth2Digest(data: cast[array[32, uint8]](
|
||||
"no receipts here\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0")),
|
||||
block_number: latest.block_number + 1,
|
||||
prev_randao: randao_mix,
|
||||
gas_limit: latest.gas_limit, # retain same limit
|
||||
gas_used: 0, # empty block, 0 gas
|
||||
timestamp: timestamp,
|
||||
base_fee_per_gas: latest.base_fee_per_gas) # retain same base_fee
|
||||
|
||||
payload.block_hash = withEth2Hash:
|
||||
h.update payload.hash_tree_root().data
|
||||
h.update cast[array[13, uint8]]("FAKE RLP HASH")
|
||||
|
||||
payload
|
||||
|
||||
# https://github.com/ethereum/consensus-specs/blob/v1.1.10/tests/core/pyspec/eth2spec/test/helpers/block.py#L75-L104
|
||||
proc mockBlock*(
|
||||
state: ForkedHashedBeaconState,
|
||||
|
|
Loading…
Reference in New Issue