diff --git a/beacon_chain/gossip_processing/block_processor.nim b/beacon_chain/gossip_processing/block_processor.nim index 39871f9bf..cc3e69b3d 100644 --- a/beacon_chain/gossip_processing/block_processor.nim +++ b/beacon_chain/gossip_processing/block_processor.nim @@ -9,7 +9,7 @@ import chronicles, chronos, metrics, - ../spec/[forks, signatures, signatures_batch], + ../spec/[forks, helpers_el, signatures, signatures_batch], ../sszdump from std/deques import Deque, addLast, contains, initDeque, items, len, shrink @@ -548,8 +548,11 @@ proc storeBlock( if signedBlock.message.is_execution_block: template payload(): auto = signedBlock.message.body.execution_payload - template returnWithError(msg: string): untyped = - debug msg, executionPayload = shortLog(payload) + template returnWithError(msg: string, extraMsg = ""): untyped = + if extraMsg != "": + debug msg, reason = extraMsg, executionPayload = shortLog(payload) + else: + debug msg, executionPayload = shortLog(payload) self[].dumpInvalidBlock(signedBlock) doAssert strictVerification notin dag.updateFlags self.consensusManager.quarantine[].addUnviable(signedBlock.root) @@ -563,10 +566,16 @@ proc storeBlock( returnWithError "Execution block hash validation failed" # [New in Deneb:EIP4844] - # TODO run https://github.com/ethereum/consensus-specs/blob/v1.3.0/specs/deneb/beacon-chain.md#blob-kzg-commitments - # https://github.com/ethereum/execution-apis/blob/main/src/engine/experimental/blob-extension.md#specification - # "This validation MUST be instantly run in all cases even during active - # sync process." + when typeof(signedBlock).kind >= ConsensusFork.Deneb: + let blobsRes = signedBlock.message.is_valid_versioned_hashes + if blobsRes.isErr: + 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() diff --git a/beacon_chain/spec/helpers_el.nim b/beacon_chain/spec/helpers_el.nim new file mode 100644 index 000000000..d33d1b39f --- /dev/null +++ b/beacon_chain/spec/helpers_el.nim @@ -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()