validate EL block hash in Electra when no EL is connected (#6407)

When no EL is connected, it is still required to validate the block hash
of `ExecutionPayload` to prevent attacks that trick us into attesting to
a circular chain with invalid in-between block hashes. This is already
done through Deneb but was still missing in Electra to be rectified now.
This commit is contained in:
Etan Kissling 2024-07-05 10:18:50 +02:00 committed by GitHub
parent c59bb71916
commit 7f59e80aaa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 79 additions and 3 deletions

View File

@ -555,8 +555,7 @@ proc storeBlock(
# This should simulate an unsynced EL, which still must perform these
# checks. This means it must be able to do so without context, beyond
# whatever data the block itself contains.
when typeof(signedBlock).kind >= ConsensusFork.Bellatrix and typeof(signedBlock).kind <= ConsensusFork.Deneb:
debugComment "electra can do this in principle"
when typeof(signedBlock).kind >= ConsensusFork.Bellatrix:
template payload(): auto = signedBlock.message.body.execution_payload
if signedBlock.message.is_execution_block and
payload.block_hash !=

View File

@ -39,6 +39,9 @@ type
ExecutionTransaction* = eth_types.Transaction
ExecutionReceipt* = eth_types.Receipt
ExecutionWithdrawal* = eth_types.Withdrawal
ExecutionDepositRequest* = eth_types.DepositRequest
ExecutionWithdrawalRequest* = eth_types.WithdrawalRequest
ExecutionConsolidationRequest* = eth_types.ConsolidationRequest
ExecutionBlockHeader* = eth_types.BlockHeader
FinalityCheckpoints* = object
@ -474,6 +477,74 @@ proc computeWithdrawalsTrieRoot*(
raiseAssert "HexaryTrie.put failed: " & $exc.msg
tr.rootHash()
func toExecutionDepositRequest*(
request: electra.DepositRequest): ExecutionDepositRequest =
ExecutionDepositRequest(
pubkey: request.pubkey.blob,
withdrawalCredentials: request.withdrawal_credentials.data,
amount: distinctBase(request.amount),
signature: request.signature.blob,
index: request.index)
func toExecutionWithdrawalRequest*(
request: electra.WithdrawalRequest): ExecutionWithdrawalRequest =
ExecutionWithdrawalRequest(
sourceAddress: request.source_address.data,
validatorPubkey: request.validator_pubkey.blob,
amount: distinctBase(request.amount))
func toExecutionConsolidationRequest*(
request: electra.ConsolidationRequest): ExecutionConsolidationRequest =
ExecutionConsolidationRequest(
sourceAddress: request.source_address.data,
sourcePubkey: request.source_pubkey.blob,
targetPubkey: request.target_pubkey.blob)
# https://eips.ethereum.org/EIPS/eip-7685
proc computeRequestsTrieRoot*(
payload: electra.ExecutionPayload): ExecutionHash256 =
if payload.deposit_requests.len == 0 and
payload.withdrawal_requests.len == 0 and
payload.consolidation_requests.len == 0:
return EMPTY_ROOT_HASH
var
tr = initHexaryTrie(newMemoryDB())
i = 0'u64
static:
doAssert DEPOSIT_REQUEST_TYPE < WITHDRAWAL_REQUEST_TYPE
doAssert WITHDRAWAL_REQUEST_TYPE < CONSOLIDATION_REQUEST_TYPE
# EIP-6110
for request in payload.deposit_requests:
try:
tr.put(rlp.encode(i.uint), rlp.encode(
toExecutionDepositRequest(request)))
except RlpError as exc:
raiseAssert "HexaryTree.put failed: " & $exc.msg
inc i
# EIP-7002
for request in payload.withdrawal_requests:
try:
tr.put(rlp.encode(i.uint), rlp.encode(
toExecutionWithdrawalRequest(request)))
except RlpError as exc:
raiseAssert "HexaryTree.put failed: " & $exc.msg
inc i
# EIP-7251
for request in payload.consolidation_requests:
try:
tr.put(rlp.encode(i.uint), rlp.encode(
toExecutionConsolidationRequest(request)))
except RlpError as exc:
raiseAssert "HexaryTree.put failed: " & $exc.msg
inc i
tr.rootHash()
proc blockToBlockHeader*(blck: ForkyBeaconBlock): ExecutionBlockHeader =
template payload: auto = blck.body.execution_payload
@ -503,6 +574,11 @@ proc blockToBlockHeader*(blck: ForkyBeaconBlock): ExecutionBlockHeader =
Opt.some ExecutionHash256(data: blck.parent_root.data)
else:
Opt.none(ExecutionHash256)
requestsRoot =
when typeof(payload).kind >= ConsensusFork.Electra:
Opt.some payload.computeRequestsTrieRoot()
else:
Opt.none(ExecutionHash256)
ExecutionBlockHeader(
parentHash : payload.parent_hash,
@ -524,7 +600,8 @@ proc blockToBlockHeader*(blck: ForkyBeaconBlock): ExecutionBlockHeader =
withdrawalsRoot : withdrawalsRoot,
blobGasUsed : blobGasUsed, # EIP-4844
excessBlobGas : excessBlobGas, # EIP-4844
parentBeaconBlockRoot : parentBeaconBlockRoot) # EIP-4788
parentBeaconBlockRoot : parentBeaconBlockRoot, # EIP-4788
requestsRoot : requestsRoot) # EIP-7685
proc compute_execution_block_hash*(blck: ForkyBeaconBlock): Eth2Digest =
rlpHash blockToBlockHeader(blck)