diff --git a/package-lock.json b/package-lock.json index 01a1d98d67..f2b15da9b0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4814,11 +4814,12 @@ } }, "node_modules/c-kzg": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/c-kzg/-/c-kzg-1.0.9.tgz", - "integrity": "sha512-5shQs7k/f7cN0Ya7g1bTgCX7CO2emh/2mkPKrjxqkC7Y+tM9YN88MWkop9ftMMZXadvVMrxWfZ/RCqBR8jRQOQ==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/c-kzg/-/c-kzg-2.0.4.tgz", + "integrity": "sha512-DsHrUSUIvC/k8TuHDTLddMGYBTYfcleyoIjv9k5iv4kJTI4J6gkntEocjKbKDCmohrwms0N4QYqx1ugp3RY3FQ==", "hasInstallScript": true, "dependencies": { + "bindings": "^1.5.0", "node-addon-api": "^5.0.0" } }, @@ -17806,7 +17807,7 @@ }, "devDependencies": { "@types/lru-cache": "^5.1.0", - "c-kzg": "^1.0.8" + "c-kzg": "^2.0.4" }, "engines": { "node": ">=14" @@ -17861,7 +17862,7 @@ "@ethereumjs/vm": "6.4.1", "abstract-level": "^1.0.3", "body-parser": "^1.19.2", - "c-kzg": "^1.0.8", + "c-kzg": "^2.0.4", "chalk": "^4.1.2", "connect": "^3.7.0", "cors": "^2.8.5", @@ -18099,7 +18100,6 @@ "license": "MPL-2.0", "dependencies": { "@ethereumjs/common": "^3.1.1", - "@ethereumjs/tx": "^4.1.1", "@ethereumjs/util": "^8.0.5", "@ethersproject/providers": "^5.7.1", "debug": "^4.3.3", @@ -18116,7 +18116,7 @@ "@types/minimist": "^1.2.2", "@types/node-dir": "^0.0.34", "benchmark": "^2.1.4", - "c-kzg": "^1.0.8", + "c-kzg": "^2.0.4", "level": "^8.0.0", "memory-level": "^1.0.0", "minimist": "^1.2.5", @@ -18233,7 +18233,7 @@ "node": ">=14" }, "peerDependencies": { - "c-kzg": "^1.0.8" + "c-kzg": "^2.0.4" }, "peerDependenciesMeta": { "c-kzg": { @@ -18256,6 +18256,14 @@ }, "engines": { "node": ">=14" + }, + "peerDependencies": { + "c-kzg": "^2.0.4" + }, + "peerDependenciesMeta": { + "c-kzg": { + "optional": true + } } }, "packages/vm": { @@ -19510,7 +19518,7 @@ "@ethereumjs/tx": "^4.1.1", "@ethereumjs/util": "^8.0.5", "@types/lru-cache": "^5.1.0", - "c-kzg": "^1.0.8", + "c-kzg": "^2.0.4", "ethereum-cryptography": "^1.1.2", "ethers": "^5.7.1" } @@ -19558,7 +19566,7 @@ "@types/jwt-simple": "^0.5.33", "abstract-level": "^1.0.3", "body-parser": "^1.19.2", - "c-kzg": "^1.0.8", + "c-kzg": "^2.0.4", "chalk": "^4.1.2", "connect": "^3.7.0", "constants-browserify": "^1.0.0", @@ -19732,7 +19740,6 @@ "requires": { "@ethereumjs/common": "^3.1.1", "@ethereumjs/statemanager": "^1.0.4", - "@ethereumjs/tx": "^4.1.1", "@ethereumjs/util": "^8.0.5", "@ethersproject/abi": "^5.0.12", "@ethersproject/providers": "^5.7.1", @@ -19742,7 +19749,7 @@ "@types/minimist": "^1.2.2", "@types/node-dir": "^0.0.34", "benchmark": "^2.1.4", - "c-kzg": "^1.0.8", + "c-kzg": "^2.0.4", "debug": "^4.3.3", "ethereum-cryptography": "^1.1.2", "level": "^8.0.0", @@ -21817,10 +21824,11 @@ "version": "3.1.2" }, "c-kzg": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/c-kzg/-/c-kzg-1.0.9.tgz", - "integrity": "sha512-5shQs7k/f7cN0Ya7g1bTgCX7CO2emh/2mkPKrjxqkC7Y+tM9YN88MWkop9ftMMZXadvVMrxWfZ/RCqBR8jRQOQ==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/c-kzg/-/c-kzg-2.0.4.tgz", + "integrity": "sha512-DsHrUSUIvC/k8TuHDTLddMGYBTYfcleyoIjv9k5iv4kJTI4J6gkntEocjKbKDCmohrwms0N4QYqx1ugp3RY3FQ==", "requires": { + "bindings": "^1.5.0", "node-addon-api": "^5.0.0" }, "dependencies": { diff --git a/packages/block/karma.conf.js b/packages/block/karma.conf.js index 3cdcf9821e..f1ad097975 100644 --- a/packages/block/karma.conf.js +++ b/packages/block/karma.conf.js @@ -13,6 +13,7 @@ module.exports = function (config) { acornOptions: { ecmaVersion: 12, }, + ignore: ['c-kzg'], }, }, concurrency: 1, diff --git a/packages/block/package.json b/packages/block/package.json index aea98c469b..cca736dd58 100644 --- a/packages/block/package.json +++ b/packages/block/package.json @@ -48,7 +48,7 @@ }, "devDependencies": { "@types/lru-cache": "^5.1.0", - "c-kzg": "^1.0.8" + "c-kzg": "^2.0.4" }, "engines": { "node": ">=14" diff --git a/packages/block/test/eip4844block.spec.ts b/packages/block/test/eip4844block.spec.ts index c38fe88738..9813e03254 100644 --- a/packages/block/test/eip4844block.spec.ts +++ b/packages/block/test/eip4844block.spec.ts @@ -1,11 +1,12 @@ import { Chain, Common, Hardfork } from '@ethereumjs/common' -import { BlobEIP4844Transaction, initKZG } from '@ethereumjs/tx' +import { BlobEIP4844Transaction } from '@ethereumjs/tx' import { blobsToCommitments, commitmentsToVersionedHashes, getBlobs, -} from '@ethereumjs/tx/dist/utils/blobHelpers' -import { randomBytes } from '@ethereumjs/util' + initKZG, + randomBytes, +} from '@ethereumjs/util' import * as kzg from 'c-kzg' import * as tape from 'tape' diff --git a/packages/client/bin/cli.ts b/packages/client/bin/cli.ts index 11c243224e..cc079ae7d4 100755 --- a/packages/client/bin/cli.ts +++ b/packages/client/bin/cli.ts @@ -4,12 +4,12 @@ import { Block } from '@ethereumjs/block' import { Blockchain, parseGethGenesisState } from '@ethereumjs/blockchain' import { Chain, Common, ConsensusAlgorithm, Hardfork } from '@ethereumjs/common' import { RLP } from '@ethereumjs/rlp' -import { initKZG } from '@ethereumjs/tx' import { Address, bytesToHex, bytesToPrefixedHexString, hexStringToBytes, + initKZG, randomBytes, short, toBytes, diff --git a/packages/client/devnets/4844-interop/tools/txGenerator.ts b/packages/client/devnets/4844-interop/tools/txGenerator.ts index 16dd356693..53bd429a0c 100644 --- a/packages/client/devnets/4844-interop/tools/txGenerator.ts +++ b/packages/client/devnets/4844-interop/tools/txGenerator.ts @@ -1,12 +1,16 @@ // Adapted from - https://github.com/Inphi/eip4844-interop/blob/master/blob_tx_generator/blob.js import { Common, Hardfork } from '@ethereumjs/common' -import { BlobEIP4844Transaction, initKZG } from '@ethereumjs/tx' +import { BlobEIP4844Transaction } from '@ethereumjs/tx' import { + Address, + initKZG, blobsToCommitments, commitmentsToVersionedHashes, getBlobs, -} from '@ethereumjs/tx/dist/utils/blobHelpers' -import { Address, bytesToPrefixedHexString, hexStringToBytes } from '@ethereumjs/util' + bytesToPrefixedHexString, + hexStringToBytes, +} from '@ethereumjs/util' + import * as kzg from 'c-kzg' import { randomBytes } from '@ethereumjs/util' import { Client } from 'jayson/promise' diff --git a/packages/client/package.json b/packages/client/package.json index 9fd6496abb..6de352750b 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -70,10 +70,10 @@ "@ethereumjs/vm": "6.4.1", "abstract-level": "^1.0.3", "body-parser": "^1.19.2", + "c-kzg": "^2.0.4", "chalk": "^4.1.2", "connect": "^3.7.0", "cors": "^2.8.5", - "c-kzg": "^1.0.8", "debug": "^4.3.3", "ethereum-cryptography": "^1.1.2", "fs-extra": "^10.1.0", diff --git a/packages/client/test/miner/pendingBlock.spec.ts b/packages/client/test/miner/pendingBlock.spec.ts index cc8f56e820..a6cc28c83d 100644 --- a/packages/client/test/miner/pendingBlock.spec.ts +++ b/packages/client/test/miner/pendingBlock.spec.ts @@ -1,18 +1,17 @@ import { Block, BlockHeader } from '@ethereumjs/block' import { Common, Chain as CommonChain, Hardfork } from '@ethereumjs/common' -import { BlobEIP4844Transaction, Transaction, initKZG } from '@ethereumjs/tx' -import { - blobsToCommitments, - commitmentsToVersionedHashes, - getBlobs, -} from '@ethereumjs/tx/dist/utils/blobHelpers' +import { BlobEIP4844Transaction, Transaction } from '@ethereumjs/tx' import { Account, Address, + blobsToCommitments, bytesToHex, bytesToPrefixedHexString, + commitmentsToVersionedHashes, equalsBytes, + getBlobs, hexStringToBytes, + initKZG, randomBytes, } from '@ethereumjs/util' import { VM } from '@ethereumjs/vm' @@ -260,11 +259,9 @@ tape('[PendingBlock]', async (t) => { t.test('construct blob bundles', async (st) => { try { - kzg.freeTrustedSetup() - } catch { - /** ensure kzg is setup */ - } - initKZG(kzg, __dirname + '/../../lib/trustedSetups/devnet4.txt') + initKZG(kzg, __dirname + '/../../lib/trustedSetups/devnet4.txt') + // eslint-disable-next-line + } catch {} const gethGenesis = require('../../../block/test/testdata/4844-hardfork.json') const common = Common.fromGethGenesis(gethGenesis, { chain: 'customChain', @@ -297,7 +294,6 @@ tape('[PendingBlock]', async (t) => { await pendingBlock.build(payloadId) const pendingBlob = pendingBlock.blobBundles.get(bytesToPrefixedHexString(payloadId))?.blobs[0] st.ok(pendingBlob !== undefined && equalsBytes(pendingBlob, blobs[0])) - kzg.freeTrustedSetup() st.end() }) t.test('should reset td', (st) => { diff --git a/packages/client/test/rpc/engine/getBlobsBundleV1.spec.ts b/packages/client/test/rpc/engine/getBlobsBundleV1.spec.ts index ce608ca7a8..0b976a8580 100644 --- a/packages/client/test/rpc/engine/getBlobsBundleV1.spec.ts +++ b/packages/client/test/rpc/engine/getBlobsBundleV1.spec.ts @@ -1,7 +1,7 @@ import { Hardfork } from '@ethereumjs/common' import { DefaultStateManager } from '@ethereumjs/statemanager' -import { TransactionFactory, initKZG } from '@ethereumjs/tx' -import { Address, hexStringToBytes } from '@ethereumjs/util' +import { TransactionFactory } from '@ethereumjs/tx' +import { Address, hexStringToBytes, initKZG } from '@ethereumjs/util' import * as kzg from 'c-kzg' import * as tape from 'tape' diff --git a/packages/client/test/rpc/eth/sendRawTransaction.spec.ts b/packages/client/test/rpc/eth/sendRawTransaction.spec.ts index 4bbfd31ed9..a8c9614c65 100644 --- a/packages/client/test/rpc/eth/sendRawTransaction.spec.ts +++ b/packages/client/test/rpc/eth/sendRawTransaction.spec.ts @@ -1,18 +1,16 @@ import { BlockHeader } from '@ethereumjs/block' import { Chain, Common, Hardfork } from '@ethereumjs/common' import { DefaultStateManager } from '@ethereumjs/statemanager' -import { - BlobEIP4844Transaction, - FeeMarketEIP1559Transaction, - Transaction, - initKZG, -} from '@ethereumjs/tx' +import { BlobEIP4844Transaction, FeeMarketEIP1559Transaction, Transaction } from '@ethereumjs/tx' import { blobsToCommitments, + bytesToPrefixedHexString, commitmentsToVersionedHashes, getBlobs, -} from '@ethereumjs/tx/dist/utils/blobHelpers' -import { bytesToPrefixedHexString, hexStringToBytes, randomBytes } from '@ethereumjs/util' + hexStringToBytes, + initKZG, + randomBytes, +} from '@ethereumjs/util' import * as kzg from 'c-kzg' import * as tape from 'tape' @@ -213,11 +211,9 @@ tape('blob EIP 4844 transaction', async (t) => { const consensusFormatValidation = BlockHeader.prototype._consensusFormatValidation BlockHeader.prototype._consensusFormatValidation = (): any => {} try { - kzg.freeTrustedSetup() - } catch { - // NOOP - just verifying KZG is ready if not already - } - initKZG(kzg, __dirname + '/../../../lib/trustedSetups/devnet4.txt') + initKZG(kzg, __dirname + '/../../../lib/trustedSetups/devnet4.txt') + // eslint-disable-next-line + } catch {} const gethGenesis = require('../../../../block/test/testdata/4844-hardfork.json') const common = Common.fromGethGenesis(gethGenesis, { chain: 'customChain', @@ -232,14 +228,14 @@ tape('blob EIP 4844 transaction', async (t) => { const blobs = getBlobs('hello world') const commitments = blobsToCommitments(blobs) const versionedHashes = commitmentsToVersionedHashes(commitments) - const proof = kzg.computeAggregateKzgProof(blobs.map((blob) => Uint8Array.from(blob))) + const proofs = blobs.map((blob, ctx) => kzg.computeBlobKzgProof(blob, commitments[ctx])) const pk = randomBytes(32) const tx = BlobEIP4844Transaction.fromTxData( { versionedHashes, blobs, kzgCommitments: commitments, - kzgProof: proof, + kzgProofs: proofs, maxFeePerDataGas: 1000000n, gasLimit: 0xffffn, maxFeePerGas: 10000000n, @@ -254,7 +250,7 @@ tape('blob EIP 4844 transaction', async (t) => { versionedHashes, blobs, kzgCommitments: commitments, - kzgProof: proof, + kzgProofs: proofs, maxFeePerDataGas: 1000000n, gasLimit: 0xfffffn, maxFeePerGas: 100000000n, diff --git a/packages/client/test/sim/4844.md b/packages/client/test/sim/4844.md index 5eaa42b136..dd004b69be 100644 --- a/packages/client/test/sim/4844.md +++ b/packages/client/test/sim/4844.md @@ -4,22 +4,36 @@ Note: All commands should be run from the `client` package directory root (so so ## Running a local devnet -To run a single EthereumJS client <> Lodestar CL client for testing, run the following command: -`NETWORK=sharding EXTRA_CL_PARAMS="--params.CAPELLA_FORK_EPOCH 0 --params.EIP4844_FORK_EPOCH 0" LODE_IMAGE=g11tech/lodestar:4844-ae177e DATADIR=path/to/your/data/directory test/sim/./single-run.sh` +Step 1. To run a single EthereumJS client <> Lodestar CL client for testing, run the following command: +`NETWORK=sharding EXTRA_CL_PARAMS="--params.CAPELLA_FORK_EPOCH 0 --params.DENEB_FORK_EPOCH 0" LODE_IMAGE=g11tech/lodestar:decoupled DATADIR=path/to/your/data/directory test/sim/./single-run.sh` -To run a second EthereumJS <> Lodestar pair, use this command: -`MULTIPEER=syncpeer NETWORK=sharding EXTRA_CL_PARAMS="--params.CAPELLA_FORK_EPOCH 0 --params.EIP4844_FORK_EPOCH 0" LODE_IMAGE=g11tech/lodestar:4844-ae177e DATADIR=path/to/your/data/directory test/sim/./single-run.sh` +Step 2. (Optional) To run a second EthereumJS <> Lodestar pair, use this command: +`MULTIPEER=syncpeer NETWORK=sharding EXTRA_CL_PARAMS="--params.CAPELLA_FORK_EPOCH 0 --params.DENEB_FORK_EPOCH 0" LODE_IMAGE=g11tech/lodestar:decoupled DATADIR=path/to/your/data/directory test/sim/./single-run.sh` -To send a single blob transaction to the network, you can use the `txGenerator.ts` script as follows: +Step 3. To send a single blob transaction to the network, you may just run spec test: +`EXTERNAL_RUN=true npm run tape -- test/sim/sharding.spec.ts` + +OR, you can use the `txGenerator.ts` script as follows: `ts-node test/sim/txGenerator 8545 'hello'`. The first argument is the port number of the EthereumJS client you which to submit the transaction to and the second is any data to include in the blob. This script was adapted from the [interop repo blob script](https://github.com/Inphi/eip4844-interop/blob/master/blob_tx_generator/blob.js) + ## EIP-4844 spec tests -To run the 4844 spec tests contained in `test/sim/sharding.spec.ts`, use the following command: +You don't need to externally start the nodes, the sim tests will do all that for you as well as run the tests against it. + +Run Step 1 & 3 together: + +`LODE_IMAGE=g11tech/lodestar:decoupled DATADIR=path/to/your/data/directory npm run tape -- + test/sim/sharding.spec.ts` + + +### Run Step 1, 2 & 3 together + -`EXTRA_CL_PARAMS="--params.CAPELLA_FORK_EPOCH 0 --params.EIP4844_FORK_EPOCH 0" LODE_IMAGE=g11tech/lodestar:4844-ae177e DATADIR=/absolute/path/to/your/data/dir npm run tape -- test/sim/sharding.spec.ts` +`WITH_PEER=syncpeer LODE_IMAGE=g11tech/lodestar:decoupled DATADIR=path/to/your/data/directory npm run tape -- + test/sim/sharding.spec.ts` Note, these tests are adapted from the specification tests contained in the [EIP-4844 Interop repo](https://github.com/Inphi/eip4844-interop) diff --git a/packages/client/test/sim/sharding.spec.ts b/packages/client/test/sim/sharding.spec.ts index 7393bf018a..59a5a5624f 100644 --- a/packages/client/test/sim/sharding.spec.ts +++ b/packages/client/test/sim/sharding.spec.ts @@ -2,13 +2,14 @@ import { Common } from '@ethereumjs/common' import { TransactionFactory } from '@ethereumjs/tx' import { bytesToPrefixedHexString, hexStringToBytes, privateToAddress } from '@ethereumjs/util' import { Client } from 'jayson/promise' +import { randomBytes } from 'node:crypto' import * as tape from 'tape' import { + createBlobTxs, filterKeywords, filterOutWords, runBlobTx, - runBlobTxsFromFile, runTxHelper, sleep, startNetwork, @@ -28,6 +29,9 @@ export async function runTx(data: string, to?: string, value?: bigint) { } tape('sharding/eip4844 hardfork tests', async (t) => { + if (process.env.EXTRA_CL_PARAMS === undefined) { + process.env.EXTRA_CL_PARAMS = '--params.CAPELLA_FORK_EPOCH 0 --params.DENEB_FORK_EPOCH 0' + } const { teardownCallBack, result } = await startNetwork(network, client, { filterKeywords, filterOutWords, @@ -55,7 +59,9 @@ tape('sharding/eip4844 hardfork tests', async (t) => { client, 2 ** 14, pkey, - '0x3dA33B9A0894b908DdBb00d96399e506515A1009' + '0x3dA33B9A0894b908DdBb00d96399e506515A1009', + undefined, + { common } ) const eth2res = await (await fetch('http://127.0.0.1:9596/eth/v1/beacon/headers')).json() @@ -96,11 +102,23 @@ tape('sharding/eip4844 hardfork tests', async (t) => { }) t.test('data gas fee market tests', async (st) => { - const res = await runBlobTxsFromFile(client, './test/sim/configs/blobs.txt') + const txns = await createBlobTxs( + 4, + 4096, + pkey, + '0x' + randomBytes(20).toString('hex'), + undefined, + { common } + ) + const txHashes = [] + for (const txn of txns) { + const res = await client.request('eth_sendRawTransaction', [txn], 2.0) + txHashes.push(res.result) + } let done = false let txReceipt while (!done) { - txReceipt = await client.request('eth_getTransactionReceipt', [res[0]], 2.0) + txReceipt = await client.request('eth_getTransactionReceipt', [txHashes[0]], 2.0) if (txReceipt.result !== null) { done = true } diff --git a/packages/client/test/sim/simutils.ts b/packages/client/test/sim/simutils.ts index 4efe3d3d7a..e410e14f21 100644 --- a/packages/client/test/sim/simutils.ts +++ b/packages/client/test/sim/simutils.ts @@ -1,15 +1,15 @@ import { Blockchain } from '@ethereumjs/blockchain' -import { BlobEIP4844Transaction, FeeMarketEIP1559Transaction, initKZG } from '@ethereumjs/tx' -import { - blobsToCommitments, - commitmentsToVersionedHashes, - getBlobs, -} from '@ethereumjs/tx/dist/utils/blobHelpers' +import { BlobEIP4844Transaction, FeeMarketEIP1559Transaction } from '@ethereumjs/tx' import { Address, + blobsToCommitments, + blobsToProofs, bytesToHex, bytesToPrefixedHexString, bytesToUtf8, + commitmentsToVersionedHashes, + getBlobs, + initKZG, randomBytes, } from '@ethereumjs/util' import * as kzg from 'c-kzg' @@ -22,6 +22,7 @@ import { EthereumClient } from '../../lib/client' import { Config } from '../../lib/config' import type { Common } from '@ethereumjs/common' +import type { TxOptions } from '@ethereumjs/tx' import type { ChildProcessWithoutNullStreams } from 'child_process' import type { Client } from 'jayson/promise' @@ -297,10 +298,12 @@ export const runBlobTx = async ( blobSize: number, pkey: Uint8Array, to?: string, - value?: bigint + value?: bigint, + opts?: TxOptions ) => { const blobs = getBlobs(bytesToHex(randomBytes(blobSize))) const commitments = blobsToCommitments(blobs) + const proofs = blobsToProofs(blobs, commitments) const hashes = commitmentsToVersionedHashes(commitments) const sender = Address.fromPrivateKey(pkey) @@ -311,6 +314,7 @@ export const runBlobTx = async ( chainId: '0x1', blobs, kzgCommitments: commitments, + kzgProofs: proofs, versionedHashes: hashes, gas: undefined, maxFeePerDataGas: undefined, @@ -327,7 +331,7 @@ export const runBlobTx = async ( txData['gasLimit'] = BigInt(1000000) as any const nonce = await client.request('eth_getTransactionCount', [sender.toString(), 'latest'], 2.0) txData['nonce'] = BigInt(nonce.result) as any - const blobTx = BlobEIP4844Transaction.fromTxData(txData).sign(pkey) + const blobTx = BlobEIP4844Transaction.fromTxData(txData, opts).sign(pkey) const serializedWrapper = blobTx.serializeNetworkWrapper() @@ -359,12 +363,14 @@ export const createBlobTxs = async ( blobSize = 2 ** 17 - 1, pkey: Uint8Array, to?: string, - value?: bigint + value?: bigint, + opts?: TxOptions ) => { - const txHashes: any = [] + const txHashes: string[] = [] const blobs = getBlobs(bytesToHex(randomBytes(blobSize))) const commitments = blobsToCommitments(blobs) + const proofs = blobsToProofs(blobs, commitments) const hashes = commitmentsToVersionedHashes(commitments) for (let x = 1; x <= numTxs; x++) { @@ -376,6 +382,7 @@ export const createBlobTxs = async ( chainId: '0x1', blobs, kzgCommitments: commitments, + kzgProofs: proofs, versionedHashes: hashes, gas: undefined, maxFeePerDataGas: undefined, @@ -391,7 +398,7 @@ export const createBlobTxs = async ( txData['maxFeePerDataGas'] = BigInt(1000) as any txData['gasLimit'] = BigInt(1000000) as any - const blobTx = BlobEIP4844Transaction.fromTxData(txData).sign(pkey) + const blobTx = BlobEIP4844Transaction.fromTxData(txData, opts).sign(pkey) const serializedWrapper = blobTx.serializeNetworkWrapper() await fs.appendFile('./blobs.txt', bytesToPrefixedHexString(serializedWrapper) + '\n') diff --git a/packages/client/test/sim/single-run.sh b/packages/client/test/sim/single-run.sh index 19a2e3bf63..0222cb298f 100755 --- a/packages/client/test/sim/single-run.sh +++ b/packages/client/test/sim/single-run.sh @@ -87,7 +87,7 @@ case $MULTIPEER in echo "ELCLIENT=$ELCLIENT not implemented" esac - CL_PORT_ARGS="--genesisValidators 8 --startValidators 4..7 --enr.tcp 9001 --port 9001 --execution.urls http://localhost:8552 --rest.port 9597 --server http://localhost:9597 --network.connectToDiscv5Bootnodes true --bootnodes $bootEnrs" + CL_PORT_ARGS="--genesisValidators 8 --startValidators 4..7 --enr.tcp 9001 --port 9001 --execution.urls http://localhost:8552 --rest.port 9597 --server http://127.0.0.1:9597 --network.connectToDiscv5Bootnodes true --bootnodes $bootEnrs" ;; * ) @@ -105,7 +105,7 @@ case $MULTIPEER in echo "ELCLIENT=$ELCLIENT not implemented" esac - CL_PORT_ARGS="--enr.ip 127.0.0.1 --enr.tcp 9000 --enr.udp 9000" + CL_PORT_ARGS="--sync.isSingleNode --enr.ip 127.0.0.1 --enr.tcp 9000 --enr.udp 9000" if [ ! -n "$MULTIPEER" ] then echo "setting up to run as a solo node..." @@ -247,8 +247,17 @@ else responseCmd="curl --location --request GET 'http://localhost:9596/eth/v1/beacon/headers/genesis' --header 'Content-Type: application/json' 2>/dev/null | jq \".data.root\"" CL_GENESIS_HASH=$(eval "$responseCmd") done; - # since peer1 is setup get their enr and enode - bootEnrs=$(sudo cat "$origDataDir/peer1/lodestar/enr") + + # We should curl and get boot enr + while [ ! -n "$bootEnrs" ] + do + sleep 3 + echo "Fetching bootEnrs block from peer1/bootnode ..." + ejsId=$(( ejsId +1 )) + responseCmd="curl --location --request GET 'http://localhost:9596/eth/v1/node/identity' --header 'Content-Type: application/json' 2>/dev/null | jq \".data.enr\"" + bootEnrs=$(eval "$responseCmd") + done; + elBootnode=$(cat "$origDataDir/peer1/ethereumjs/$NETWORK/rlpx"); EL_PORT_ARGS="$EL_PORT_ARGS --bootnodes $elBootnode" CL_PORT_ARGS="$CL_PORT_ARGS --bootnodes $bootEnrs" diff --git a/packages/client/test/sim/txGenerator.ts b/packages/client/test/sim/txGenerator.ts index 9f2985c648..ca245b4246 100644 --- a/packages/client/test/sim/txGenerator.ts +++ b/packages/client/test/sim/txGenerator.ts @@ -1,10 +1,14 @@ // Adapted from - https://github.com/Inphi/eip4844-interop/blob/master/blob_tx_generator/blob.js -import { BlobEIP4844Transaction, initKZG } from '@ethereumjs/tx' +import { BlobEIP4844Transaction } from '@ethereumjs/tx' import { + Address, blobsToCommitments, + bytesToPrefixedHexString, commitmentsToVersionedHashes, -} from '@ethereumjs/tx/test/utils/blobHelpers' -import { Address, bytesToPrefixedHexString, hexStringToBytes, randomBytes } from '@ethereumjs/util' + hexStringToBytes, + initKZG, + randomBytes, +} from '@ethereumjs/util' import * as kzg from 'c-kzg' import { Client } from 'jayson/promise' const clientPort = process.argv[2] diff --git a/packages/evm/package.json b/packages/evm/package.json index cffcd0d013..4e27c4f2ff 100644 --- a/packages/evm/package.json +++ b/packages/evm/package.json @@ -48,7 +48,6 @@ }, "dependencies": { "@ethereumjs/common": "^3.1.1", - "@ethereumjs/tx": "^4.1.1", "@ethereumjs/util": "^8.0.5", "@ethersproject/providers": "^5.7.1", "debug": "^4.3.3", @@ -65,7 +64,7 @@ "@types/minimist": "^1.2.2", "@types/node-dir": "^0.0.34", "benchmark": "^2.1.4", - "c-kzg": "^1.0.8", + "c-kzg": "^2.0.4", "level": "^8.0.0", "memory-level": "^1.0.0", "minimist": "^1.2.5", diff --git a/packages/evm/src/precompiles/14-kzg-point-evaluation.ts b/packages/evm/src/precompiles/14-kzg-point-evaluation.ts index 5db899a67b..e257afee43 100644 --- a/packages/evm/src/precompiles/14-kzg-point-evaluation.ts +++ b/packages/evm/src/precompiles/14-kzg-point-evaluation.ts @@ -1,9 +1,10 @@ -import { computeVersionedHash, kzg } from '@ethereumjs/tx' import { bigIntToBytes, bytesToBigInt, bytesToHex, + computeVersionedHash, concatBytesNoTypeCheck, + kzg, setLengthLeft, short, } from '@ethereumjs/util' diff --git a/packages/evm/test/precompiles/14-pointevaluation.spec.ts b/packages/evm/test/precompiles/14-pointevaluation.spec.ts index b6a4ca1863..3e1ab41b7b 100644 --- a/packages/evm/test/precompiles/14-pointevaluation.spec.ts +++ b/packages/evm/test/precompiles/14-pointevaluation.spec.ts @@ -1,6 +1,12 @@ import { Common, Hardfork } from '@ethereumjs/common' -import { computeVersionedHash, initKZG } from '@ethereumjs/tx' -import { bigIntToBytes, bytesToBigInt, concatBytesNoTypeCheck, unpadBytes } from '@ethereumjs/util' +import { + bigIntToBytes, + bytesToBigInt, + computeVersionedHash, + concatBytesNoTypeCheck, + initKZG, + unpadBytes, +} from '@ethereumjs/util' import * as kzg from 'c-kzg' import { hexToBytes } from 'ethereum-cryptography/utils' import * as tape from 'tape' diff --git a/packages/tx/karma.conf.js b/packages/tx/karma.conf.js index 5f921f9078..837089c906 100644 --- a/packages/tx/karma.conf.js +++ b/packages/tx/karma.conf.js @@ -15,6 +15,7 @@ module.exports = function (config) { acornOptions: { ecmaVersion: 12, }, + ignore: ['c-kzg', 'safer-buffer'], }, }, browsers: ['FirefoxHeadless', 'ChromeHeadless'], diff --git a/packages/tx/package.json b/packages/tx/package.json index 92087337b0..f5257a71ee 100644 --- a/packages/tx/package.json +++ b/packages/tx/package.json @@ -59,7 +59,7 @@ "@ethersproject/providers": "^5.7.2" }, "peerDependencies": { - "c-kzg": "^1.0.8" + "c-kzg": "^2.0.4" }, "peerDependenciesMeta": { "c-kzg": { diff --git a/packages/tx/src/depInterfaces.ts b/packages/tx/src/depInterfaces.ts deleted file mode 100644 index 6d3e3e5659..0000000000 --- a/packages/tx/src/depInterfaces.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Interface for an externally provided kzg library used when creating blob transactions - */ -export interface Kzg { - loadTrustedSetup(filePath: string): void - freeTrustedSetup(): void - blobToKzgCommitment(blob: Uint8Array): Uint8Array - computeAggregateKzgProof(blobs: Uint8Array[]): Uint8Array - verifyKzgProof( - polynomialKzg: Uint8Array, - z: Uint8Array, - y: Uint8Array, - kzgProof: Uint8Array - ): boolean - verifyAggregateKzgProof( - blobs: Uint8Array[], - expectedKzgCommitments: Uint8Array[], - kzgAggregatedProof: Uint8Array - ): boolean -} diff --git a/packages/tx/src/eip4844Transaction.ts b/packages/tx/src/eip4844Transaction.ts index c40a4048f3..6e270bf8f4 100644 --- a/packages/tx/src/eip4844Transaction.ts +++ b/packages/tx/src/eip4844Transaction.ts @@ -7,23 +7,23 @@ import { bytesToBigInt, bytesToHex, bytesToPrefixedHexString, + computeVersionedHash, concatBytes, ecrecover, hexStringToBytes, + kzg, toBytes, } from '@ethereumjs/util' import { keccak256 } from 'ethereum-cryptography/keccak' import { BaseTransaction } from './baseTransaction' import { LIMIT_BLOBS_PER_TX } from './constants' -import { kzg } from './kzg/kzg' import { BlobNetworkTransactionWrapper, BlobTransactionType, SignedBlobTransactionType, } from './types' import { AccessLists, blobTxToNetworkWrapperDataFormat } from './util' -import { computeVersionedHash } from './utils/blobHelpers' import type { AccessList, @@ -44,14 +44,14 @@ const validateBlobTransactionNetworkWrapper = ( versionedHashes: Uint8Array[], blobs: Uint8Array[], commitments: Uint8Array[], - kzgProof: Uint8Array, + kzgProofs: Uint8Array[], version: number ) => { if (!(versionedHashes.length === blobs.length && blobs.length === commitments.length)) { throw new Error('Number of versionedHashes, blobs, and commitments not all equal') } try { - kzg.verifyAggregateKzgProof(blobs, commitments, kzgProof) + kzg.verifyBlobKzgProofBatch(blobs, commitments, kzgProofs) } catch (e) { throw new Error('KZG proof cannot be verified from blobs/commitments') } @@ -82,7 +82,7 @@ export class BlobEIP4844Transaction extends BaseTransaction toBytes(blob)) this.kzgCommitments = txData.kzgCommitments?.map((commitment) => toBytes(commitment)) - this.aggregateKzgProof = toBytes(txData.kzgProof) + this.kzgProofs = txData.kzgProofs?.map((proof) => toBytes(proof)) const freeze = opts?.freeze ?? true if (freeze) { Object.freeze(this) @@ -216,7 +216,7 @@ export class BlobEIP4844Transaction extends BaseTransaction Uint8Array.from(commitment)) ?? [], tx: { ...blobTxToNetworkWrapperDataFormat(this), ...to }, - kzgAggregatedProof: Uint8Array.from(this.aggregateKzgProof ?? []), + blobKzgProofs: this.kzgProofs?.map((proof) => Uint8Array.from(proof)) ?? [], }) return concatBytes(new Uint8Array([0x05]), serializedTxWrapper) } @@ -459,7 +459,7 @@ export class BlobEIP4844Transaction extends BaseTransaction { const blobs = getBlobs('hello world') const commitments = blobsToCommitments(blobs) const versionedHashes = commitmentsToVersionedHashes(commitments) - const proof = kzg.computeAggregateKzgProof(blobs) + const proofs = blobsToProofs(blobs, commitments) const unsignedTx = BlobEIP4844Transaction.fromTxData( { versionedHashes, blobs, kzgCommitments: commitments, - kzgProof: proof, + kzgProofs: proofs, maxFeePerDataGas: 100000000n, gasLimit: 0xffffffn, to: randomBytes(20), @@ -204,6 +205,7 @@ tape('Network wrapper tests', async (t) => { versionedHashes, blobs: blobs.slice(1), kzgCommitments: commitments, + kzgProofs: proofs, maxFeePerDataGas: 100000000n, gasLimit: 0xffffffn, to: randomBytes(20), @@ -230,6 +232,7 @@ tape('Network wrapper tests', async (t) => { versionedHashes, blobs, kzgCommitments: commitments, + kzgProofs: proofs, maxFeePerDataGas: 100000000n, gasLimit: 0xffffffn, to: randomBytes(20), @@ -255,7 +258,7 @@ tape('Network wrapper tests', async (t) => { versionedHashes, blobs, kzgCommitments: commitments, - kzgProof: proof, + kzgProofs: proofs, maxFeePerDataGas: 100000000n, gasLimit: 0xffffffn, to: randomBytes(20), @@ -289,7 +292,7 @@ tape('hash() and signature verification', async (t) => { chainId: 1, nonce: 1, versionedHashes: [ - hexStringToBytes('01624652859a6e98ffc1608e2af0147ca4e86e1ce27672d8d3f3c9d4ffd6ef7e'), + hexToBytes('01624652859a6e98ffc1608e2af0147ca4e86e1ce27672d8d3f3c9d4ffd6ef7e'), ], maxFeePerDataGas: 10000000n, gasLimit: 123457n, diff --git a/packages/util/package.json b/packages/util/package.json index 4ba531a21b..05da274902 100644 --- a/packages/util/package.json +++ b/packages/util/package.json @@ -91,6 +91,14 @@ "@types/bn.js": "^5.1.0", "@types/secp256k1": "^4.0.1" }, + "peerDependencies": { + "c-kzg": "^2.0.4" + }, + "peerDependenciesMeta": { + "c-kzg": { + "optional": true + } + }, "engines": { "node": ">=14" } diff --git a/packages/tx/src/utils/blobHelpers.ts b/packages/util/src/blobHelpers.ts similarity index 93% rename from packages/tx/src/utils/blobHelpers.ts rename to packages/util/src/blobHelpers.ts index 3e5808b609..d14924cdba 100644 --- a/packages/tx/src/utils/blobHelpers.ts +++ b/packages/util/src/blobHelpers.ts @@ -1,7 +1,7 @@ import { sha256 } from 'ethereum-cryptography/sha256' import { utf8ToBytes } from 'ethereum-cryptography/utils' -import { kzg } from '../kzg/kzg' +import { kzg } from './kzg' /** * These utilities for constructing blobs are borrowed from https://github.com/Inphi/eip4844-interop.git @@ -63,6 +63,12 @@ export const blobsToCommitments = (blobs: Uint8Array[]) => { return commitments } +export const blobsToProofs = (blobs: Uint8Array[], commitments: Uint8Array[]) => { + const proofs = blobs.map((blob, ctx) => kzg.computeBlobKzgProof(blob, commitments[ctx])) + + return proofs +} + /** * Converts a vector commitment for a given data blob to its versioned hash. For 4844, this version * number will be 0x01 for KZG vector commitments but could be different if future vector commitment diff --git a/packages/util/src/bytes.ts b/packages/util/src/bytes.ts index faaec5d480..904bab1894 100644 --- a/packages/util/src/bytes.ts +++ b/packages/util/src/bytes.ts @@ -186,10 +186,6 @@ export const toBytes = function (v: ToBytesInputTypes): Uint8Array { return new Uint8Array() } - if (Buffer.isBuffer(v)) { - return Uint8Array.from(v) - } - if (Array.isArray(v) || v instanceof Uint8Array) { return Uint8Array.from(v) } diff --git a/packages/util/src/index.ts b/packages/util/src/index.ts index df57cde654..9a05428d06 100644 --- a/packages/util/src/index.ts +++ b/packages/util/src/index.ts @@ -52,6 +52,7 @@ export * from './encoding' * Export ethjs-util methods */ export * from './asyncEventEmitter' +export * from './blobHelpers' export { arrayContainsArray, fromAscii, @@ -64,4 +65,5 @@ export { stripHexPrefix, toAscii, } from './internal' +export * from './kzg' export * from './lock' diff --git a/packages/util/src/kzg.ts b/packages/util/src/kzg.ts new file mode 100644 index 0000000000..05c48fd5f9 --- /dev/null +++ b/packages/util/src/kzg.ts @@ -0,0 +1,42 @@ +/** + * Interface for an externally provided kzg library used when creating blob transactions + */ +export interface Kzg { + loadTrustedSetup(filePath: string): void + blobToKzgCommitment(blob: Uint8Array): Uint8Array + computeBlobKzgProof(blob: Uint8Array, commitment: Uint8Array): Uint8Array + verifyKzgProof( + polynomialKzg: Uint8Array, + z: Uint8Array, + y: Uint8Array, + kzgProof: Uint8Array + ): boolean + verifyBlobKzgProofBatch( + blobs: Uint8Array[], + expectedKzgCommitments: Uint8Array[], + kzgProofs: Uint8Array[] + ): boolean +} + +function kzgNotLoaded(): never { + throw Error('kzg library not loaded') +} + +// eslint-disable-next-line import/no-mutable-exports +export let kzg: Kzg = { + loadTrustedSetup: kzgNotLoaded, + blobToKzgCommitment: kzgNotLoaded, + computeBlobKzgProof: kzgNotLoaded, + verifyKzgProof: kzgNotLoaded, + verifyBlobKzgProofBatch: kzgNotLoaded, +} + +/** + * @param kzgLib a KZG implementation (defaults to c-kzg) + * @param trustedSetupPath the full path (e.g. "/home/linux/devnet4.txt") to a kzg trusted setup text file + */ +export function initKZG(kzgLib: Kzg, trustedSetupPath: string) { + kzg = kzgLib + + kzg.loadTrustedSetup(trustedSetupPath) +} diff --git a/packages/util/src/types.ts b/packages/util/src/types.ts index 144672eb3c..dd6020addb 100644 --- a/packages/util/src/types.ts +++ b/packages/util/src/types.ts @@ -37,7 +37,6 @@ export interface TransformabletoBytes { } export type NestedUint8Array = Array -export type NestedBufferArray = Array /** * Type output options