check blob versioned hashes when no EL is connected (#6501)
* check blob versioned hashes when no EL is connected When no EL is conencted, we have to at the very least ensure that the data in the beacon block is consistent with the execution payload. We already do this for the block hash, but also have to do it for the `blob_kzg_commitments`. To validate that they are linked with the execution payload, we have to RLP decode all EIP-4844 blob transactions and compare their blob versioned hashes with the hashed commitments. * simplify loop in case where `blob_versioned_hashes` doesn't exist * skip blob transaction parsing pre Deneb
This commit is contained in:
parent
21aeeaf561
commit
a597fe95fa
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
import
|
import
|
||||||
chronicles, chronos, metrics,
|
chronicles, chronos, metrics,
|
||||||
../spec/[forks, signatures, signatures_batch],
|
../spec/[forks, helpers_el, signatures, signatures_batch],
|
||||||
../sszdump
|
../sszdump
|
||||||
|
|
||||||
from std/deques import Deque, addLast, contains, initDeque, items, len, shrink
|
from std/deques import Deque, addLast, contains, initDeque, items, len, shrink
|
||||||
|
@ -548,7 +548,10 @@ proc storeBlock(
|
||||||
if signedBlock.message.is_execution_block:
|
if signedBlock.message.is_execution_block:
|
||||||
template payload(): auto = signedBlock.message.body.execution_payload
|
template payload(): auto = signedBlock.message.body.execution_payload
|
||||||
|
|
||||||
template returnWithError(msg: string): untyped =
|
template returnWithError(msg: string, extraMsg = ""): untyped =
|
||||||
|
if extraMsg != "":
|
||||||
|
debug msg, reason = extraMsg, executionPayload = shortLog(payload)
|
||||||
|
else:
|
||||||
debug msg, executionPayload = shortLog(payload)
|
debug msg, executionPayload = shortLog(payload)
|
||||||
self[].dumpInvalidBlock(signedBlock)
|
self[].dumpInvalidBlock(signedBlock)
|
||||||
doAssert strictVerification notin dag.updateFlags
|
doAssert strictVerification notin dag.updateFlags
|
||||||
|
@ -563,10 +566,16 @@ proc storeBlock(
|
||||||
returnWithError "Execution block hash validation failed"
|
returnWithError "Execution block hash validation failed"
|
||||||
|
|
||||||
# [New in Deneb:EIP4844]
|
# [New in Deneb:EIP4844]
|
||||||
# TODO run https://github.com/ethereum/consensus-specs/blob/v1.3.0/specs/deneb/beacon-chain.md#blob-kzg-commitments
|
when typeof(signedBlock).kind >= ConsensusFork.Deneb:
|
||||||
# https://github.com/ethereum/execution-apis/blob/main/src/engine/experimental/blob-extension.md#specification
|
let blobsRes = signedBlock.message.is_valid_versioned_hashes
|
||||||
# "This validation MUST be instantly run in all cases even during active
|
if blobsRes.isErr:
|
||||||
# sync process."
|
returnWithError "Blob versioned hashes invalid", blobsRes.error
|
||||||
|
else:
|
||||||
|
# If there are EIP-4844 (type 3) transactions in the payload with
|
||||||
|
# versioned hashes, the transactions would be rejected by the EL
|
||||||
|
# based on payload timestamp (only allowed post Deneb);
|
||||||
|
# There are no `blob_kzg_commitments` before Deneb to compare against
|
||||||
|
discard
|
||||||
|
|
||||||
let newPayloadTick = Moment.now()
|
let newPayloadTick = Moment.now()
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
# beacon_chain
|
||||||
|
# Copyright (c) 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/typetraits,
|
||||||
|
eth/common/eth_types_rlp,
|
||||||
|
"."/[helpers, state_transition_block]
|
||||||
|
|
||||||
|
func readExecutionTransaction(
|
||||||
|
txBytes: bellatrix.Transaction): Result[ExecutionTransaction, string] =
|
||||||
|
# Nim 2.0.8: `rlp.decode(distinctBase(txBytes), ExecutionTransaction)`
|
||||||
|
# uses the generic `read` from `rlp.nim` instead of the specific `read`
|
||||||
|
# from `eth_types_rlp.nim`, leading to compilation error.
|
||||||
|
# Doing this in two steps works around this resolution order issue.
|
||||||
|
var rlp = rlpFromBytes(distinctBase(txBytes))
|
||||||
|
try:
|
||||||
|
ok rlp.read(ExecutionTransaction)
|
||||||
|
except RlpError as exc:
|
||||||
|
err("Invalid transaction: " & exc.msg)
|
||||||
|
|
||||||
|
# https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.4/specs/deneb/beacon-chain.md#is_valid_versioned_hashes
|
||||||
|
func is_valid_versioned_hashes*(blck: ForkyBeaconBlock): Result[void, string] =
|
||||||
|
static: doAssert typeof(blck).kind >= ConsensusFork.Deneb
|
||||||
|
template transactions: untyped = blck.body.execution_payload.transactions
|
||||||
|
template commitments: untyped = blck.body.blob_kzg_commitments
|
||||||
|
|
||||||
|
var i = 0
|
||||||
|
for txBytes in transactions:
|
||||||
|
if txBytes.len == 0 or txBytes[0] != TxEip4844.byte:
|
||||||
|
continue # Only blob transactions may have blobs
|
||||||
|
let tx = ? txBytes.readExecutionTransaction()
|
||||||
|
for vHash in tx.versionedHashes:
|
||||||
|
if commitments.len <= i:
|
||||||
|
return err("Extra blobs without matching `blob_kzg_commitments`")
|
||||||
|
if vHash.data != kzg_commitment_to_versioned_hash(commitments[i]):
|
||||||
|
return err("Invalid `blob_versioned_hash` at index " & $i)
|
||||||
|
inc i
|
||||||
|
if i != commitments.len:
|
||||||
|
return err("Extra `blob_kzg_commitments` without matching blobs")
|
||||||
|
ok()
|
Loading…
Reference in New Issue