diff --git a/packages/beacon-node/src/chain/blocks/verifyBlocksExecutionPayloads.ts b/packages/beacon-node/src/chain/blocks/verifyBlocksExecutionPayloads.ts index e115d5782c40..1f6fc125a44a 100644 --- a/packages/beacon-node/src/chain/blocks/verifyBlocksExecutionPayloads.ts +++ b/packages/beacon-node/src/chain/blocks/verifyBlocksExecutionPayloads.ts @@ -4,8 +4,9 @@ import { isExecutionBlockBodyType, isMergeTransitionBlock as isMergeTransitionBlockFn, isExecutionEnabled, + kzgCommitmentToVersionedHash, } from "@lodestar/state-transition"; -import {bellatrix, allForks, Slot} from "@lodestar/types"; +import {bellatrix, allForks, Slot, deneb} from "@lodestar/types"; import {toHexString} from "@chainsafe/ssz"; import { IForkChoice, @@ -18,6 +19,8 @@ import { } from "@lodestar/fork-choice"; import {ChainForkConfig} from "@lodestar/config"; import {ErrorAborted, Logger} from "@lodestar/utils"; +import {ForkSeq} from "@lodestar/params"; + import {IExecutionEngine} from "../../execution/engine/index.js"; import {BlockError, BlockErrorCode} from "../errors/index.js"; import {IClock} from "../../util/clock.js"; @@ -285,10 +288,12 @@ export async function verifyBlockExecutionPayload( } // TODO: Handle better notifyNewPayload() returning error is syncing - const execResult = await chain.executionEngine.notifyNewPayload( - chain.config.getForkName(block.message.slot), - executionPayloadEnabled - ); + const fork = chain.config.getForkName(block.message.slot); + const versionedHashes = + ForkSeq[fork] >= ForkSeq.deneb + ? (block.message.body as deneb.BeaconBlockBody).blobKzgCommitments.map(kzgCommitmentToVersionedHash) + : undefined; + const execResult = await chain.executionEngine.notifyNewPayload(fork, executionPayloadEnabled, versionedHashes); chain.metrics?.engineNotifyNewPayloadResult.inc({result: execResult.status}); diff --git a/packages/beacon-node/src/chain/produceBlock/validateBlobsAndKzgCommitments.ts b/packages/beacon-node/src/chain/produceBlock/validateBlobsAndKzgCommitments.ts index 07fa987e71d9..54e90672d189 100644 --- a/packages/beacon-node/src/chain/produceBlock/validateBlobsAndKzgCommitments.ts +++ b/packages/beacon-node/src/chain/produceBlock/validateBlobsAndKzgCommitments.ts @@ -1,28 +1,15 @@ -import {verifyKzgCommitmentsAgainstTransactions} from "@lodestar/state-transition"; -import {allForks, deneb} from "@lodestar/types"; -import {toHex} from "@lodestar/utils"; +import {allForks} from "@lodestar/types"; import {BlobsBundle} from "../../execution/index.js"; -import {byteArrayEquals} from "../../util/bytes.js"; -import {ckzg} from "../../util/kzg.js"; /** * Optionally sanity-check that the KZG commitments match the versioned hashes in the transactions * https://github.com/ethereum/consensus-specs/blob/11a037fd9227e29ee809c9397b09f8cc3383a8c0/specs/eip4844/validator.md#blob-kzg-commitments */ export function validateBlobsAndKzgCommitments(payload: allForks.ExecutionPayload, blobsBundle: BlobsBundle): void { - verifyKzgCommitmentsAgainstTransactions(payload.transactions, blobsBundle.commitments); - - // Optionally sanity-check that the KZG commitments match the blobs (as produced by the execution engine) + // sanity-check that the KZG commitments match the blobs (as produced by the execution engine) if (blobsBundle.blobs.length !== blobsBundle.commitments.length) { throw Error( `Blobs bundle blobs len ${blobsBundle.blobs.length} != commitments len ${blobsBundle.commitments.length}` ); } - - for (let i = 0; i < blobsBundle.blobs.length; i++) { - const kzg = ckzg.blobToKzgCommitment(blobsBundle.blobs[i]) as deneb.KZGCommitment; - if (!byteArrayEquals(kzg, blobsBundle.commitments[i])) { - throw Error(`Wrong KZG[${i}] ${toHex(blobsBundle.commitments[i])} expected ${toHex(kzg)}`); - } - } } diff --git a/packages/beacon-node/src/chain/validation/blobsSidecar.ts b/packages/beacon-node/src/chain/validation/blobsSidecar.ts index 952c7840cffa..ad5eddb8c9a6 100644 --- a/packages/beacon-node/src/chain/validation/blobsSidecar.ts +++ b/packages/beacon-node/src/chain/validation/blobsSidecar.ts @@ -3,7 +3,6 @@ import {CoordType} from "@chainsafe/bls/types"; import {deneb, Root, ssz} from "@lodestar/types"; import {bytesToBigInt, toHex} from "@lodestar/utils"; import {BYTES_PER_FIELD_ELEMENT, FIELD_ELEMENTS_PER_BLOB} from "@lodestar/params"; -import {verifyKzgCommitmentsAgainstTransactions} from "@lodestar/state-transition"; import {BlobsSidecarError, BlobsSidecarErrorCode} from "../errors/blobsSidecarError.js"; import {GossipAction} from "../errors/gossipValidation.js"; import {byteArrayEquals} from "../../util/bytes.js"; @@ -27,14 +26,6 @@ export function validateGossipBlobsSidecar( } } - // [REJECT] The KZG commitments correspond to the versioned hashes in the transactions list. - // -- i.e. verify_kzg_commitments_against_transactions(block.body.execution_payload.transactions, block.body.blob_kzg_commitments) - if ( - !verifyKzgCommitmentsAgainstTransactions(block.body.executionPayload.transactions, block.body.blobKzgCommitments) - ) { - throw new BlobsSidecarError(GossipAction.REJECT, {code: BlobsSidecarErrorCode.INVALID_KZG_TXS}); - } - // [IGNORE] the sidecar.beacon_block_slot is for the current slot (with a MAXIMUM_GOSSIP_CLOCK_DISPARITY allowance) // -- i.e. sidecar.beacon_block_slot == block.slot. if (blobsSidecar.beaconBlockSlot !== block.slot) { diff --git a/packages/beacon-node/src/execution/engine/http.ts b/packages/beacon-node/src/execution/engine/http.ts index 1fcd0514e1c5..11ab396e6cd4 100644 --- a/packages/beacon-node/src/execution/engine/http.ts +++ b/packages/beacon-node/src/execution/engine/http.ts @@ -15,6 +15,7 @@ import { PayloadAttributes, TransitionConfigurationV1, BlobsBundle, + VersionedHashes, } from "./interface.js"; import {PayloadIdCache} from "./payloadIdCache.js"; import { @@ -22,6 +23,7 @@ import { EngineApiRpcReturnTypes, parseExecutionPayload, serializeExecutionPayload, + serializeVersionedHashes, serializePayloadAttributes, ExecutionPayloadBody, assertReqSizeLimit, @@ -133,20 +135,44 @@ export class ExecutionEngineHttp implements IExecutionEngine { * * If any of the above fails due to errors unrelated to the normal processing flow of the method, client software MUST respond with an error object. */ - async notifyNewPayload(fork: ForkName, executionPayload: allForks.ExecutionPayload): Promise { + async notifyNewPayload( + fork: ForkName, + executionPayload: allForks.ExecutionPayload, + versionedHashes?: VersionedHashes + ): Promise { const method = ForkSeq[fork] >= ForkSeq.deneb ? "engine_newPayloadV3" : ForkSeq[fork] >= ForkSeq.capella ? "engine_newPayloadV2" : "engine_newPayloadV1"; + const serializedExecutionPayload = serializeExecutionPayload(fork, executionPayload); - const {status, latestValidHash, validationError} = await ( - this.rpcFetchQueue.push({ + const serializedVersionedHashes = + versionedHashes !== undefined ? serializeVersionedHashes(versionedHashes) : undefined; + + let engingRequest: EngineRequest; + if (ForkSeq[fork] >= ForkSeq.deneb) { + if (serializedVersionedHashes === undefined) { + throw Error(`versionedHashes required in notifyNewPayload for fork=${fork}`); + } + const method = "engine_newPayloadV3"; + engingRequest = { + method, + params: [serializedExecutionPayload, serializedVersionedHashes], + methodOpts: notifyNewPayloadOpts, + }; + } else { + const method = ForkSeq[fork] >= ForkSeq.capella ? "engine_newPayloadV2" : "engine_newPayloadV1"; + engingRequest = { method, params: [serializedExecutionPayload], methodOpts: notifyNewPayloadOpts, - }) as Promise + }; + } + + const {status, latestValidHash, validationError} = await ( + this.rpcFetchQueue.push(engingRequest) as Promise ) // If there are errors by EL like connection refused, internal error, they need to be // treated separate from being INVALID. For now, just pass the error upstream. diff --git a/packages/beacon-node/src/execution/engine/interface.ts b/packages/beacon-node/src/execution/engine/interface.ts index e3f17c4fc9d6..d1bea600f5f3 100644 --- a/packages/beacon-node/src/execution/engine/interface.ts +++ b/packages/beacon-node/src/execution/engine/interface.ts @@ -70,6 +70,8 @@ export type BlobsBundle = { proofs: KZGProof[]; }; +export type VersionedHashes = Uint8Array[]; + /** * Execution engine represents an abstract protocol to interact with execution clients. Potential transports include: * - JSON RPC over network @@ -87,7 +89,11 @@ export interface IExecutionEngine { * * Should be called in advance before, after or in parallel to block processing */ - notifyNewPayload(fork: ForkName, executionPayload: allForks.ExecutionPayload): Promise; + notifyNewPayload( + fork: ForkName, + executionPayload: allForks.ExecutionPayload, + versionedHashes?: VersionedHashes + ): Promise; /** * Signal fork choice updates diff --git a/packages/beacon-node/src/execution/engine/mock.ts b/packages/beacon-node/src/execution/engine/mock.ts index 15fb8b33945b..040cfa80798d 100644 --- a/packages/beacon-node/src/execution/engine/mock.ts +++ b/packages/beacon-node/src/execution/engine/mock.ts @@ -1,18 +1,14 @@ import crypto from "node:crypto"; -import { - kzgCommitmentToVersionedHash, - OPAQUE_TX_BLOB_VERSIONED_HASHES_OFFSET, - OPAQUE_TX_MESSAGE_OFFSET, -} from "@lodestar/state-transition"; +import {kzgCommitmentToVersionedHash} from "@lodestar/state-transition"; import {bellatrix, deneb, RootHex, ssz} from "@lodestar/types"; import {fromHex, toHex} from "@lodestar/utils"; import { BYTES_PER_FIELD_ELEMENT, FIELD_ELEMENTS_PER_BLOB, - BLOB_TX_TYPE, ForkSeq, ForkExecution, ForkName, + BLOB_TX_TYPE, } from "@lodestar/params"; import {ZERO_HASH_HEX} from "../../constants/index.js"; import {ckzg} from "../../util/kzg.js"; @@ -139,7 +135,9 @@ export class ExecutionEngineMockBackend implements JsonRpcBackend { * `engine_newPayloadV1` */ private notifyNewPayload( - executionPayloadRpc: EngineApiRpcParamTypes["engine_newPayloadV1"][0] + executionPayloadRpc: EngineApiRpcParamTypes["engine_newPayloadV1"][0], + // TODO deneb: add versionedHashes validation + _versionedHashes?: EngineApiRpcParamTypes["engine_newPayloadV3"][1] ): EngineApiRpcReturnTypes["engine_newPayloadV1"] { const blockHash = executionPayloadRpc.blockHash; const parentHash = executionPayloadRpc.parentHash; @@ -405,26 +403,13 @@ export class ExecutionEngineMockBackend implements JsonRpcBackend { } function transactionForKzgCommitment(kzgCommitment: deneb.KZGCommitment): bellatrix.Transaction { - // Some random value that after the offset's position - const blobVersionedHashesOffset = OPAQUE_TX_BLOB_VERSIONED_HASHES_OFFSET + 64; - - // +32 for the size of versionedHash - const ab = new ArrayBuffer(blobVersionedHashesOffset + 32); - const dv = new DataView(ab); - const ua = new Uint8Array(ab); - - // Set tx type - dv.setUint8(0, BLOB_TX_TYPE); - - // Set offset to hashes array - // const blobVersionedHashesOffset = - // OPAQUE_TX_MESSAGE_OFFSET + opaqueTxDv.getUint32(OPAQUE_TX_BLOB_VERSIONED_HASHES_OFFSET, true); - dv.setUint32(OPAQUE_TX_BLOB_VERSIONED_HASHES_OFFSET, blobVersionedHashesOffset - OPAQUE_TX_MESSAGE_OFFSET, true); - + // Just use versionedHash as the transaction encoding to mock newPayloadV3 verification + // prefixed with BLOB_TX_TYPE + const transaction = new Uint8Array(33); const versionedHash = kzgCommitmentToVersionedHash(kzgCommitment); - ua.set(versionedHash, blobVersionedHashesOffset); - - return ua; + transaction[0] = BLOB_TX_TYPE; + transaction.set(versionedHash, 1); + return transaction; } /** diff --git a/packages/beacon-node/src/execution/engine/types.ts b/packages/beacon-node/src/execution/engine/types.ts index fdb876ce1d44..f1d6e4a1a673 100644 --- a/packages/beacon-node/src/execution/engine/types.ts +++ b/packages/beacon-node/src/execution/engine/types.ts @@ -16,7 +16,13 @@ import { QUANTITY, quantityToBigint, } from "../../eth1/provider/utils.js"; -import {ExecutePayloadStatus, TransitionConfigurationV1, BlobsBundle, PayloadAttributes} from "./interface.js"; +import { + ExecutePayloadStatus, + TransitionConfigurationV1, + BlobsBundle, + PayloadAttributes, + VersionedHashes, +} from "./interface.js"; import {WithdrawalV1} from "./payloadIdCache.js"; /* eslint-disable @typescript-eslint/naming-convention */ @@ -27,7 +33,7 @@ export type EngineApiRpcParamTypes = { */ engine_newPayloadV1: [ExecutionPayloadRpc]; engine_newPayloadV2: [ExecutionPayloadRpc]; - engine_newPayloadV3: [ExecutionPayloadRpc]; + engine_newPayloadV3: [ExecutionPayloadRpc, VersionedHashesRpc]; /** * 1. Object - Payload validity status with respect to the consensus rules: * - blockHash: DATA, 32 Bytes - block hash value of the payload @@ -139,6 +145,8 @@ export type WithdrawalRpc = { amount: QUANTITY; }; +export type VersionedHashesRpc = DATA[]; + export type PayloadAttributesRpc = { /** QUANTITY, 64 Bits - value for the timestamp field of the new payload */ timestamp: QUANTITY; @@ -187,6 +195,10 @@ export function serializeExecutionPayload(fork: ForkName, data: allForks.Executi return payload; } +export function serializeVersionedHashes(vHashes: VersionedHashes): VersionedHashesRpc { + return vHashes.map(bytesToData); +} + export function hasBlockValue(response: ExecutionPayloadResponse): response is ExecutionPayloadRpcWithBlockValue { return (response as ExecutionPayloadRpcWithBlockValue).blockValue !== undefined; } diff --git a/packages/beacon-node/test/unit/util/kzg.test.ts b/packages/beacon-node/test/unit/util/kzg.test.ts index 6bb8c17d41c5..c35b5098863c 100644 --- a/packages/beacon-node/test/unit/util/kzg.test.ts +++ b/packages/beacon-node/test/unit/util/kzg.test.ts @@ -1,11 +1,7 @@ import {expect} from "chai"; import {bellatrix, deneb, ssz} from "@lodestar/types"; -import {BLOB_TX_TYPE, BYTES_PER_FIELD_ELEMENT} from "@lodestar/params"; -import { - kzgCommitmentToVersionedHash, - OPAQUE_TX_BLOB_VERSIONED_HASHES_OFFSET, - OPAQUE_TX_MESSAGE_OFFSET, -} from "@lodestar/state-transition"; +import {BYTES_PER_FIELD_ELEMENT, BLOB_TX_TYPE} from "@lodestar/params"; +import {kzgCommitmentToVersionedHash} from "@lodestar/state-transition"; import {loadEthereumTrustedSetup, initCKZG, ckzg, FIELD_ELEMENTS_PER_BLOB_MAINNET} from "../../../src/util/kzg.js"; import {validateBlobsSidecar, validateGossipBlobsSidecar} from "../../../src/chain/validation/blobsSidecar.js"; @@ -54,26 +50,13 @@ describe("C-KZG", () => { }); function transactionForKzgCommitment(kzgCommitment: deneb.KZGCommitment): bellatrix.Transaction { - // Some random value that after the offset's position - const blobVersionedHashesOffset = OPAQUE_TX_BLOB_VERSIONED_HASHES_OFFSET + 64; - - // +32 for the size of versionedHash - const ab = new ArrayBuffer(blobVersionedHashesOffset + 32); - const dv = new DataView(ab); - const ua = new Uint8Array(ab); - - // Set tx type - dv.setUint8(0, BLOB_TX_TYPE); - - // Set offset to hashes array - // const blobVersionedHashesOffset = - // OPAQUE_TX_MESSAGE_OFFSET + opaqueTxDv.getUint32(OPAQUE_TX_BLOB_VERSIONED_HASHES_OFFSET, true); - dv.setUint32(OPAQUE_TX_BLOB_VERSIONED_HASHES_OFFSET, blobVersionedHashesOffset - OPAQUE_TX_MESSAGE_OFFSET, true); - + // Just use versionedHash as the transaction encoding to mock newPayloadV3 verification + // prefixed with BLOB_TX_TYPE + const transaction = new Uint8Array(33); const versionedHash = kzgCommitmentToVersionedHash(kzgCommitment); - ua.set(versionedHash, blobVersionedHashesOffset); - - return ua; + transaction[0] = BLOB_TX_TYPE; + transaction.set(versionedHash, 1); + return transaction; } /** diff --git a/packages/state-transition/src/block/index.ts b/packages/state-transition/src/block/index.ts index f85ce33644da..bebec0a41ddf 100644 --- a/packages/state-transition/src/block/index.ts +++ b/packages/state-transition/src/block/index.ts @@ -1,5 +1,5 @@ import {ForkSeq} from "@lodestar/params"; -import {allForks, altair, capella, deneb} from "@lodestar/types"; +import {allForks, altair, capella} from "@lodestar/types"; import {getFullOrBlindedPayload, isExecutionEnabled} from "../util/execution.js"; import {CachedBeaconStateAllForks, CachedBeaconStateCapella, CachedBeaconStateBellatrix} from "../types.js"; import {processExecutionPayload} from "./processExecutionPayload.js"; @@ -62,7 +62,7 @@ export function processBlock( } if (fork >= ForkSeq.deneb) { - processBlobKzgCommitments(block.body as deneb.BeaconBlockBody); + processBlobKzgCommitments(externalData); // Only throw preDeneb so beacon can also sync/process blocks optimistically // and let forkChoice handle it if (externalData.dataAvailableStatus === DataAvailableStatus.preDeneb) { diff --git a/packages/state-transition/src/block/processBlobKzgCommitments.ts b/packages/state-transition/src/block/processBlobKzgCommitments.ts index 9ad74a0c74aa..7f50cdd41aa7 100644 --- a/packages/state-transition/src/block/processBlobKzgCommitments.ts +++ b/packages/state-transition/src/block/processBlobKzgCommitments.ts @@ -1,5 +1,4 @@ -import {deneb} from "@lodestar/types"; -import {verifyKzgCommitmentsAgainstTransactions} from "../util/index.js"; +import {BlockExternalData, ExecutionPayloadStatus} from "./externalData.js"; /** * https://github.com/ethereum/consensus-specs/blob/11a037fd9227e29ee809c9397b09f8cc3383a8c0/specs/eip4844/beacon-chain.md#blob-kzg-commitments @@ -10,8 +9,13 @@ import {verifyKzgCommitmentsAgainstTransactions} from "../util/index.js"; * body.blob_kzg_commitments * ) */ -export function processBlobKzgCommitments(body: deneb.BeaconBlockBody): void { - if (!verifyKzgCommitmentsAgainstTransactions(body.executionPayload.transactions, body.blobKzgCommitments)) { - throw Error("Invalid KZG commitments against transactions"); +export function processBlobKzgCommitments(externalData: BlockExternalData): void { + switch (externalData.executionPayloadStatus) { + case ExecutionPayloadStatus.preMerge: + throw Error("executionPayloadStatus preMerge"); + case ExecutionPayloadStatus.invalid: + throw Error("Invalid execution payload"); + case ExecutionPayloadStatus.valid: + break; // ok } } diff --git a/packages/state-transition/src/util/blobs.ts b/packages/state-transition/src/util/blobs.ts index e9f0acd558e2..8b6ea84362c4 100644 --- a/packages/state-transition/src/util/blobs.ts +++ b/packages/state-transition/src/util/blobs.ts @@ -1,91 +1,9 @@ import SHA256 from "@chainsafe/as-sha256"; -import {byteArrayEquals} from "@chainsafe/ssz"; -import {BLOB_TX_TYPE, VERSIONED_HASH_VERSION_KZG} from "@lodestar/params"; -import {bellatrix, deneb} from "@lodestar/types"; -import {toHex} from "@lodestar/utils"; - -// TODO DENEB: Move to params -const BYTES_PER_HASH = 32; - -/** - * Blob transaction: - * - 1 byte prefix - * - class SignedBlobTransaction(Container): message starts at offset 69 - * - class BlobTransaction(Container): blob_versioned_hashes offset value in offset 188, last property in container - * So to read blob_versioned_hashes: - * - Read offset value at [70+188, 70+188+4] - * - Read chunks between offset value and EOF - * Reference: https://gist.github.com/protolambda/23bd106b66f6d4bb854ce46044aa3ca3 - */ -export const OPAQUE_TX_MESSAGE_OFFSET = 70; -export const OPAQUE_TX_BLOB_VERSIONED_HASHES_OFFSET = OPAQUE_TX_MESSAGE_OFFSET + 188; +import {VERSIONED_HASH_VERSION_KZG} from "@lodestar/params"; +import {deneb} from "@lodestar/types"; type VersionHash = Uint8Array; -/** - * https://github.com/ethereum/consensus-specs/blob/11a037fd9227e29ee809c9397b09f8cc3383a8c0/specs/eip4844/beacon-chain.md#verify_kzg_commitments_against_transactions - * No expensive verification, just checks that the version hashes are consistent with the kzg commitments - */ -export function verifyKzgCommitmentsAgainstTransactions( - transactions: bellatrix.Transaction[], - blobKzgCommitments: deneb.KZGCommitment[] -): boolean { - const allVersionedHashes: VersionHash[] = []; - for (const tx of transactions) { - if (tx[0] === BLOB_TX_TYPE) { - // TODO DENEB: Optimize array manipulation - allVersionedHashes.push(...txPeekBlobVersionedHashes(tx)); - } - } - - if (allVersionedHashes.length !== blobKzgCommitments.length) { - throw Error( - `allVersionedHashes len ${allVersionedHashes.length} != blobKzgCommitments len ${blobKzgCommitments.length}` - ); - } - - for (let i = 0; i < blobKzgCommitments.length; i++) { - const versionedHash = kzgCommitmentToVersionedHash(blobKzgCommitments[i]); - if (!byteArrayEquals(allVersionedHashes[i], versionedHash)) { - throw Error(`Wrong versionedHash ${i} ${toHex(allVersionedHashes[i])} != ${toHex(versionedHash)}`); - } - } - - // TODO DENEB: Use proper API, either throw error or return boolean - return true; -} - -function txPeekBlobVersionedHashes(opaqueTx: bellatrix.Transaction): VersionHash[] { - if (opaqueTx[0] !== BLOB_TX_TYPE) { - throw Error(`tx type ${opaqueTx[0]} != BLOB_TX_TYPE`); - } - - const opaqueTxDv = new DataView(opaqueTx.buffer, opaqueTx.byteOffset, opaqueTx.byteLength); - - const blobVersionedHashesOffset = - OPAQUE_TX_MESSAGE_OFFSET + opaqueTxDv.getUint32(OPAQUE_TX_BLOB_VERSIONED_HASHES_OFFSET, true); - - // Guard against offsets that go beyond end of bytes - if (blobVersionedHashesOffset > opaqueTx.length) { - throw Error(`blobVersionedHashesOffset ${blobVersionedHashesOffset} > EOF ${opaqueTx.length}`); - } - - // Guard against not multiple of BYTES_PER_HASH - const blobVersionedHashesByteLen = opaqueTx.length - blobVersionedHashesOffset; - if ((opaqueTx.length - blobVersionedHashesOffset) % BYTES_PER_HASH !== 0) { - throw Error(`Uneven blobVersionedHashesByteLen ${blobVersionedHashesByteLen}`); - } - - const versionedHashes: VersionHash[] = []; - - // iterate from x to end of data, in steps of 32, to get all hashes - for (let i = blobVersionedHashesOffset; i < opaqueTx.length; i += BYTES_PER_HASH) { - versionedHashes.push(opaqueTx.subarray(i, i + BYTES_PER_HASH)); - } - - return versionedHashes; -} - export function kzgCommitmentToVersionedHash(kzgCommitment: deneb.KZGCommitment): VersionHash { const hash = SHA256.digest(kzgCommitment); // Equivalent to `VERSIONED_HASH_VERSION_KZG + hash(kzg_commitment)[1:]` diff --git a/packages/state-transition/test/unit/util/blobs.test.ts b/packages/state-transition/test/unit/util/blobs.test.ts deleted file mode 100644 index 3548963dcad6..000000000000 --- a/packages/state-transition/test/unit/util/blobs.test.ts +++ /dev/null @@ -1,53 +0,0 @@ -import {fromHex} from "@lodestar/utils"; -import {verifyKzgCommitmentsAgainstTransactions} from "../../../src/index.js"; - -// Data from fee-merket test at Inphi/eip4844-interop -// Ref https://github.com/Inphi/eip4844-interop/blob/cad0dab50901cc1371a683388136fd56654d3bba/tests/fee-market/main.go#L67 -const tx = - "03450000000058aca83353114c8385ece4c18461dbb8d1ef7479c3640e812acd2ec488c56fcf43e305a508bc30bec9bff27ab367a1ed0b397b0299bb7f48b72884f5178e7c0a0100000000000000000000000000000000000000000000000000000000000000000000000000000000f2052a0100000000000000000000000000000000000000000000000000000000f2052a010000000000000000000000000000000000000000000000000000005034030000000000c00000004e61bc0000000000000000000000000000000000000000000000000000000000d5000000d5000000005ed0b200000000000000000000000000000000000000000000000000000000d500000001ffb38a7a99e3e2335be83fc74b7faa19d553124301134066927e042d676d93e523ef251e0b82bdcb72d2ca85e99c804f60ffa989"; -const blobKzgCommitment = - "a1a6202ee4a387c16ba23379b9c471a0cc48cb53793678faa92b2920c270867eebeac74351946859ffd199c6fe32385e"; - -describe("blobs", () => { - it("verifyKzgCommitmentsAgainstTransactions", () => { - verifyKzgCommitmentsAgainstTransactions([fromHex(tx)], [fromHex(blobKzgCommitment)]); - }); - - // 05 - // 45000000 - // 0058aca83353114c8385ece4c18461dbb8d1ef7479c3640e812acd2ec488c56fcf43e305a508bc30bec9bff27ab367a1ed0b397b0299bb7f48b72884f5178e7c0a - // 000 - 0100000000000000000000000000000000000000000000000000000000000000 // chain_id - // 032 - 0000000000000000 // nonce - // 040 - 00f2052a01000000000000000000000000000000000000000000000000000000 // max_priority_fee_per_gas - // 072 - 00f2052a01000000000000000000000000000000000000000000000000000000 // max_fee_per_gas - // 104 - 5034030000000000 // gas - // 112 - c0000000 // to - offset_value = 192 - // 116 - 4e61bc0000000000000000000000000000000000000000000000000000000000 // value - // 148 - d5000000 // data - // 152 - d5000000 // access_list - // 156 - 005ed0b200000000000000000000000000000000000000000000000000000000 // max_fee_per_data_gas - // 188 - d5000000 // blob_versioned_hashes - offset_value = 213 - // 192 - 01 ffb38a7a99e3e2335be83fc74b7faa19d5531243 // __value_of to - // 213 - 01134066927e042d676d93e523ef251e0b82bdcb72d2ca85e99c804f60ffa989 // __value_of blob_versioned_hashes - // - // field offset: 32 + 8 + 32 + 32 + 8 + 4 + 32 + 4 + 4 + 32 = 188 - - // class SignedBlobTransaction(Container): - // message: BlobTransaction // 4 bytes offset, continues on 69 - // signature: ECDSASignature // 65 bytes - - // class BlobTransaction(Container): - // chain_id: uint256 // 32 bytes - // nonce: uint64 // 8 bytes - // max_priority_fee_per_gas: uint256 // 32 bytes - // max_fee_per_gas: uint256 // 32 bytes - // gas: uint64 // 8 bytes - // to: Union[None, Address] # Address = Bytes20 // 4 bytes offset - // value: uint256 // 32 bytes - // data: ByteList[MAX_CALLDATA_SIZE] // 4 bytes offset - // access_list: List[AccessTuple, MAX_ACCESS_LIST_SIZE] // 4 bytes offset - // max_fee_per_data_gas: uint256 # new in PR 5707, a.k.a. fee market change of Deneb // 32 bytes offset - // blob_versioned_hashes: List[VersionedHash, MAX_VERSIONED_HASHES_LIST_SIZE] // 4 bytes offset - - // field_offset = 32 + 8 + 32 + 32 + 8 + 4 + 32 + 4 + 4 + 32 = 188 -});