Skip to content

Commit c91d9d6

Browse files
authored
validate EL block hash in EL simulation (#4420)
When simulating EL with `--optimistic` flag, perform block hash check.
1 parent 07d4160 commit c91d9d6

File tree

4 files changed

+49
-20
lines changed

4 files changed

+49
-20
lines changed

beacon_chain/gossip_processing/block_processor.nim

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -374,14 +374,28 @@ proc storeBlock*(
374374
self.consensusManager.quarantine[].addUnviable(signedBlock.root)
375375
return err((VerifierError.UnviableFork, ProcessingStatus.completed))
376376

377-
if NewPayloadStatus.noResponse == payloadStatus and not self[].optimistic:
378-
# Disallow the `MissingParent` from leaking to the sync/request managers
379-
# as it will be descored. However sync and request managers interact via
380-
# `processBlock` (indirectly). `validator_duties` does call `storeBlock`
381-
# directly, so is exposed to this, but only cares about whether there is
382-
# an error or not.
383-
return err((
384-
VerifierError.MissingParent, ProcessingStatus.notCompleted))
377+
if NewPayloadStatus.noResponse == payloadStatus:
378+
if not self[].optimistic:
379+
# Disallow the `MissingParent` from leaking to the sync/request managers
380+
# as it will be descored. However sync and request managers interact via
381+
# `processBlock` (indirectly). `validator_duties` does call `storeBlock`
382+
# directly, so is exposed to this, but only cares about whether there is
383+
# an error or not.
384+
return err((
385+
VerifierError.MissingParent, ProcessingStatus.notCompleted))
386+
387+
# Client software MUST validate blockHash value as being equivalent to
388+
# Keccak256(RLP(ExecutionBlockHeader))
389+
# https://github.com/ethereum/execution-apis/blob/v1.0.0-beta.1/src/engine/specification.md#specification
390+
when typeof(signedBlock).toFork() >= BeaconBlockFork.Bellatrix:
391+
template payload(): auto = signedBlock.message.body.execution_payload
392+
if payload.block_hash != payload.compute_execution_block_hash():
393+
debug "EL block hash validation failed", execution_payload = payload
394+
doAssert strictVerification notin dag.updateFlags
395+
self.consensusManager.quarantine[].addUnviable(signedBlock.root)
396+
return err((VerifierError.UnviableFork, ProcessingStatus.completed))
397+
else:
398+
discard
385399

386400
# We'll also remove the block as an orphan: it's unlikely the parent is
387401
# missing if we get this far - should that be the case, the block will

beacon_chain/spec/helpers.nim

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,19 @@ func compute_timestamp_at_slot*(state: ForkyBeaconState, slot: Slot): uint64 =
372372
let slots_since_genesis = slot - GENESIS_SLOT
373373
state.genesis_time + slots_since_genesis * SECONDS_PER_SLOT
374374

375+
proc computeTransactionsTrieRoot*(
376+
payload: bellatrix.ExecutionPayload | capella.ExecutionPayload): Hash256 =
377+
if payload.transactions.len == 0:
378+
return EMPTY_ROOT_HASH
379+
380+
var tr = initHexaryTrie(newMemoryDB())
381+
for i, transaction in payload.transactions:
382+
try:
383+
tr.put(rlp.encode(i), distinctBase(transaction)) # Already RLP encoded
384+
except RlpError as exc:
385+
doAssert false, "HexaryTrie.put failed: " & $exc.msg
386+
tr.rootHash()
387+
375388
func gweiToWei*(gwei: Gwei): UInt256 =
376389
gwei.u256 * 1_000_000_000.u256
377390

@@ -397,18 +410,15 @@ proc computeWithdrawalsTrieRoot*(
397410
doAssert false, "HexaryTrie.put failed: " & $exc.msg
398411
tr.rootHash()
399412

400-
proc emptyPayloadToBlockHeader*(
413+
proc payloadToBlockHeader*(
401414
payload: bellatrix.ExecutionPayload | capella.ExecutionPayload
402415
): ExecutionBlockHeader =
403416
static: # `GasInt` is signed. We only use it for hashing.
404417
doAssert sizeof(GasInt) == sizeof(payload.gas_limit)
405418
doAssert sizeof(GasInt) == sizeof(payload.gas_used)
406419

407-
## This function assumes that the payload is empty!
408-
doAssert payload.transactions.len == 0
409-
410420
let
411-
txRoot = EMPTY_ROOT_HASH
421+
txRoot = payload.computeTransactionsTrieRoot()
412422
withdrawalsRoot =
413423
when payload is bellatrix.ExecutionPayload:
414424
none(Hash256)
@@ -434,7 +444,12 @@ proc emptyPayloadToBlockHeader*(
434444
fee : some payload.base_fee_per_gas,
435445
withdrawalsRoot: withdrawalsRoot)
436446

437-
func build_empty_execution_payload*(
447+
proc compute_execution_block_hash*(
448+
payload: bellatrix.ExecutionPayload | capella.ExecutionPayload
449+
): Eth2Digest =
450+
rlpHash payloadToBlockHeader(payload)
451+
452+
proc build_empty_execution_payload*(
438453
state: bellatrix.BeaconState,
439454
feeRecipient: Eth1Address): bellatrix.ExecutionPayload =
440455
## Assuming a pre-state of the same slot, build a valid ExecutionPayload
@@ -459,7 +474,7 @@ func build_empty_execution_payload*(
459474
timestamp: timestamp,
460475
base_fee_per_gas: base_fee)
461476

462-
payload.block_hash = rlpHash emptyPayloadToBlockHeader(payload)
477+
payload.block_hash = payload.compute_execution_block_hash()
463478

464479
payload
465480

@@ -491,6 +506,6 @@ proc build_empty_execution_payload*(
491506
for withdrawal in expectedWithdrawals:
492507
doAssert payload.withdrawals.add withdrawal
493508

494-
payload.block_hash = rlpHash emptyPayloadToBlockHeader(payload)
509+
payload.block_hash = payload.compute_execution_block_hash()
495510

496511
payload

tests/test_helpers.nim

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ suite "Spec helpers":
9696
for i, withdrawal in withdrawals:
9797
check payload.withdrawals[i] == withdrawal
9898

99-
let elHeader = emptyPayloadToBlockHeader(payload)
99+
let elHeader = payloadToBlockHeader(payload)
100100
check elHeader.withdrawalsRoot.isSome
101101
if withdrawals.len == 0:
102102
check elHeader.withdrawalsRoot.get == EMPTY_ROOT_HASH

tests/testblockutil.nim

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ func signBlock(
8181
from ../beacon_chain/spec/datatypes/capella import
8282
BeaconState, ExecutionPayload, SignedBLSToExecutionChangeList
8383

84-
func build_empty_merge_execution_payload(state: bellatrix.BeaconState):
84+
proc build_empty_merge_execution_payload(state: bellatrix.BeaconState):
8585
bellatrix.ExecutionPayload =
8686
## Assuming a pre-state of the same slot, build a valid ExecutionPayload
8787
## without any transactions from a non-merged block.
@@ -104,7 +104,7 @@ func build_empty_merge_execution_payload(state: bellatrix.BeaconState):
104104
timestamp: timestamp,
105105
base_fee_per_gas: EIP1559_INITIAL_BASE_FEE)
106106

107-
payload.block_hash = rlpHash emptyPayloadToBlockHeader(payload)
107+
payload.block_hash = rlpHash payloadToBlockHeader(payload)
108108

109109
payload
110110

@@ -131,7 +131,7 @@ proc build_empty_merge_execution_payload(state: capella.BeaconState):
131131
timestamp: timestamp,
132132
base_fee_per_gas: EIP1559_INITIAL_BASE_FEE)
133133

134-
payload.block_hash = rlpHash emptyPayloadToBlockHeader(payload)
134+
payload.block_hash = rlpHash payloadToBlockHeader(payload)
135135

136136
payload
137137

0 commit comments

Comments
 (0)