From f290abee89594fb6b672c02672302372a810be5d Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Mon, 25 Nov 2024 20:41:10 +0100 Subject: [PATCH 01/17] evm: add devnet-5 EIP-7702 changes --- packages/evm/src/opcodes/functions.ts | 42 ++++++++--- packages/evm/test/eips/eip-7702.spec.ts | 99 +++++++++++++++++++++++++ 2 files changed, 130 insertions(+), 11 deletions(-) create mode 100644 packages/evm/test/eips/eip-7702.spec.ts diff --git a/packages/evm/src/opcodes/functions.ts b/packages/evm/src/opcodes/functions.ts index 6b7ef88852..f78c49671c 100644 --- a/packages/evm/src/opcodes/functions.ts +++ b/packages/evm/src/opcodes/functions.ts @@ -25,6 +25,7 @@ import { concatBytes, equalsBytes, getVerkleTreeIndicesForStorageSlot, + hexToBytes, setLengthLeft, } from '@ethereumjs/util' import { keccak256 } from 'ethereum-cryptography/keccak.js' @@ -61,16 +62,40 @@ export interface AsyncOpHandler { export type OpHandler = SyncOpHandler | AsyncOpHandler +// TODO: verify that this is the correct designator +// The PR https://github.com/ethereum/EIPs/pull/8969 has two definitions of the +// designator: the original (0xef0100) and the designator added in the changes (0xef01) +const eip7702Designator = hexToBytes('0xef01') +const eip7702HashBigInt = bytesToBigInt(keccak256(eip7702Designator)) + function getEIP7702DelegatedAddress(code: Uint8Array) { if (equalsBytes(code.slice(0, 3), DELEGATION_7702_FLAG)) { return new Address(code.slice(3, 24)) } } -async function eip7702CodeCheck(runState: RunState, code: Uint8Array) { +/** + * This method performs checks to transfor the code which the EVM observes regarding EIP-7702. + * If the code is 7702-delegated code, it will retrieve the code of the designated address + * in case of an executable operation (`isReadOperation` == false), or the 7702 designator + * code in case of a read operation + * @param runState + * @param code + * @param isReadOperation Boolean to determine if the target code is meant to be read or executed (default: `false`) + * @returns + */ +async function eip7702CodeCheck( + runState: RunState, + code: Uint8Array, + isReadOperation: boolean = false, +) { const address = getEIP7702DelegatedAddress(code) if (address !== undefined) { - return runState.stateManager.getCode(address) + if (isReadOperation) { + return eip7702Designator + } else { + return runState.stateManager.getCode(address) + } } return code @@ -538,7 +563,7 @@ export const handlers: Map = new Map([ runState.stack.push(BigInt(EOFBYTES.length)) return } else if (common.isActivatedEIP(7702)) { - code = await eip7702CodeCheck(runState, code) + code = await eip7702CodeCheck(runState, code, true) } const size = BigInt(code.length) @@ -560,7 +585,7 @@ export const handlers: Map = new Map([ // In legacy code, the target code is treated as to be "EOFBYTES" code code = EOFBYTES } else if (common.isActivatedEIP(7702)) { - code = await eip7702CodeCheck(runState, code) + code = await eip7702CodeCheck(runState, code, true) } const data = getDataSlice(code, codeOffset, dataLength) @@ -587,13 +612,8 @@ export const handlers: Map = new Map([ } else if (common.isActivatedEIP(7702)) { const possibleDelegatedAddress = getEIP7702DelegatedAddress(code) if (possibleDelegatedAddress !== undefined) { - const account = await runState.stateManager.getAccount(possibleDelegatedAddress) - if (!account || account.isEmpty()) { - runState.stack.push(BIGINT_0) - return - } - - runState.stack.push(BigInt(bytesToHex(account.codeHash))) + // The account is delegated by an EIP-7702 tx. Push the EIP-7702 designator hash to the stack + runState.stack.push(eip7702HashBigInt) return } else { runState.stack.push(bytesToBigInt(keccak256(code))) diff --git a/packages/evm/test/eips/eip-7702.spec.ts b/packages/evm/test/eips/eip-7702.spec.ts new file mode 100644 index 0000000000..7e648b5ed0 --- /dev/null +++ b/packages/evm/test/eips/eip-7702.spec.ts @@ -0,0 +1,99 @@ +import { Common, Hardfork, Mainnet } from '@ethereumjs/common' +import { + Address, + bytesToBigInt, + equalsBytes, + hexToBytes, + setLengthRight, + unprefixedHexToBytes, +} from '@ethereumjs/util' +import { keccak256 } from 'ethereum-cryptography/keccak' +import { assert, describe, it } from 'vitest' + +import { createEVM } from '../../src/index.js' + +const eip7702Designator = hexToBytes('0xef01') +const addressHex = '01'.repeat(20) // Address as unprefixed hex string +const address = new Address(unprefixedHexToBytes(addressHex)) + +const delegationCode = hexToBytes('0xef0100' + addressHex) // This code will delegate to `address` + +// Helpers for unprefixed hex strings of opcodes +const EXTCODESIZE = '3B' +const EXTCODECOPY = '3C' +const EXTCODEHASH = '3F' + +// This code stores the topmost stack item in slot 0 +const STORE_TOP_STACK_CODE = '5F55' + +// This code pushes the `address` to the stack +const PUSH_Address = '73' + addressHex // PUSH20
as unprefixed hex string + +const testCodeAddress = new Address(unprefixedHexToBytes('02'.repeat(20))) + +// Setups EVM with an account at `address` which is delegated to itself +async function getEVM() { + const common = new Common({ + chain: Mainnet, + hardfork: Hardfork.Prague, + }) + const evm = await createEVM({ + common, + }) + await evm.stateManager.putCode(address, delegationCode) + return evm +} + +describe('EIP 7702 tests', () => { + it('EXTCODESIZE', async () => { + const evm = await getEVM() + const code = unprefixedHexToBytes(PUSH_Address + EXTCODESIZE + STORE_TOP_STACK_CODE) + await evm.stateManager.putCode(testCodeAddress, code) + + await evm.runCall({ + to: testCodeAddress, + gasLimit: BigInt(100_000), + }) + + const result = await evm.stateManager.getStorage(testCodeAddress, new Uint8Array(32)) + const expected = BigInt(eip7702Designator.length) + + assert.equal(bytesToBigInt(result), expected) + }) + + it('EXTCODEHASH', async () => { + const evm = await getEVM() + const code = unprefixedHexToBytes(PUSH_Address + EXTCODEHASH + STORE_TOP_STACK_CODE) + await evm.stateManager.putCode(testCodeAddress, code) + + await evm.runCall({ + to: testCodeAddress, + gasLimit: BigInt(100_000), + }) + + const result = await evm.stateManager.getStorage(testCodeAddress, new Uint8Array(32)) + const expected = keccak256(eip7702Designator) + + assert.ok(equalsBytes(result, expected)) + }) + + it('EXTCODECOPY', async () => { + const evm = await getEVM() + // This code does some extra logic than other tests, because it has to setup EXTCODECOPY (4 stack items instead of 1) + // It EXTCODECOPYs 32 bytes of the delegated address in memory at key 0, and then MLOADs key 0 before storing the result + const code = unprefixedHexToBytes( + '60205F5F' + PUSH_Address + EXTCODECOPY + '5F51' + STORE_TOP_STACK_CODE, + ) + await evm.stateManager.putCode(testCodeAddress, code) + + await evm.runCall({ + to: testCodeAddress, + gasLimit: BigInt(100_000), + }) + + const result = await evm.stateManager.getStorage(testCodeAddress, new Uint8Array(32)) + const expected = setLengthRight(eip7702Designator, 32) + + assert.ok(equalsBytes(result, expected)) + }) +}) From 85e937aaa0615f3cbe768af141d0f8596d042979 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Mon, 25 Nov 2024 20:53:27 +0100 Subject: [PATCH 02/17] vm: add devnet-5 EIP-7685 changes --- packages/block/src/helpers.ts | 5 ++++- packages/vm/test/api/EIPs/eip-7685.spec.ts | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/block/src/helpers.ts b/packages/block/src/helpers.ts index 3f0b498796..79228bddb9 100644 --- a/packages/block/src/helpers.ts +++ b/packages/block/src/helpers.ts @@ -173,7 +173,10 @@ export function genRequestsRoot( let flatRequests = new Uint8Array() for (const req of requests) { - flatRequests = concatBytes(flatRequests, sha256Function(req.bytes)) + if (req.bytes.length > 1) { + // Only append requests if they have content + flatRequests = concatBytes(flatRequests, sha256Function(req.bytes)) + } } return sha256Function(flatRequests) diff --git a/packages/vm/test/api/EIPs/eip-7685.spec.ts b/packages/vm/test/api/EIPs/eip-7685.spec.ts index 99b0f4c2d2..9be6ec60fa 100644 --- a/packages/vm/test/api/EIPs/eip-7685.spec.ts +++ b/packages/vm/test/api/EIPs/eip-7685.spec.ts @@ -1,7 +1,7 @@ import { createBlock, genRequestsRoot } from '@ethereumjs/block' import { createBlockchain } from '@ethereumjs/blockchain' import { Common, Hardfork, Mainnet } from '@ethereumjs/common' -import { createCLRequest, hexToBytes } from '@ethereumjs/util' +import { createCLRequest, equalsBytes, hexToBytes } from '@ethereumjs/util' import { sha256 } from 'ethereum-cryptography/sha256' import { assert, describe, expect, it } from 'vitest' @@ -31,6 +31,9 @@ describe('EIP-7685 runBlock tests', () => { generate: true, }) assert.equal(res.gasUsed, 0n) + // Verify that if the requests are empty, the byte-types are not appended to the to-be-hashed flat array + // I.e. the flat array to-be-hashed is not `0x 00 01 02`, but is now the empty bytes array, `0x` + assert.ok(equalsBytes(res.requestsHash!, sha256(new Uint8Array()))) }) it('should error when an invalid requestsHash is provided', async () => { const vm = await setupVM({ common }) From 3525a19b0b866c4fa50381e0fd1e9ab8f1342e7e Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Mon, 25 Nov 2024 20:58:30 +0100 Subject: [PATCH 03/17] monorepo: make cspell happy --- packages/evm/src/opcodes/functions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/evm/src/opcodes/functions.ts b/packages/evm/src/opcodes/functions.ts index f78c49671c..9661b7c8b6 100644 --- a/packages/evm/src/opcodes/functions.ts +++ b/packages/evm/src/opcodes/functions.ts @@ -75,7 +75,7 @@ function getEIP7702DelegatedAddress(code: Uint8Array) { } /** - * This method performs checks to transfor the code which the EVM observes regarding EIP-7702. + * This method performs checks to transform the code which the EVM observes regarding EIP-7702. * If the code is 7702-delegated code, it will retrieve the code of the designated address * in case of an executable operation (`isReadOperation` == false), or the 7702 designator * code in case of a read operation From 4800a9f4ae712674dc0e6607b6dac68139c51b34 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Tue, 26 Nov 2024 16:28:03 +0100 Subject: [PATCH 04/17] vm/client: fix tests --- packages/client/test/rpc/engine/newPayloadV4.spec.ts | 3 ++- packages/vm/test/api/EIPs/eip-7702.spec.ts | 9 ++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/client/test/rpc/engine/newPayloadV4.spec.ts b/packages/client/test/rpc/engine/newPayloadV4.spec.ts index 4538a2d95b..4acd9a9ea4 100644 --- a/packages/client/test/rpc/engine/newPayloadV4.spec.ts +++ b/packages/client/test/rpc/engine/newPayloadV4.spec.ts @@ -162,7 +162,7 @@ describe(`${method}: call with executionPayloadV4`, () => { excessBlobGas: '0x0', parentHash: '0x6abe4c2777a6a1e994d83920cfb95229b071174b95c89343f54b926f733789f2', stateRoot: '0x7aa6e46df1f78988a3141b5e7da8abee78d1daca175f43fe8866b2d1bf8d8ef8', - blockHash: '0x9a5903d803e6e7c3631cd76cb7279f93d7facc995c53eaffadf4e225504b18eb', + blockHash: '0xece8d273a76238c4c9e4e28cbd301d40b04cb21242e83960ca688324e0483fd8', } const oldMethods = ['engine_newPayloadV1', 'engine_newPayloadV2', 'engine_newPayloadV3'] @@ -181,6 +181,7 @@ describe(`${method}: call with executionPayloadV4`, () => { } res = await rpc.request(method, [validBlock, [], parentBeaconBlockRoot, ['0x', '0x', '0x']]) + console.log(res) assert.equal(res.result.status, 'VALID') res = await rpc.request('engine_forkchoiceUpdatedV3', validPayload) diff --git a/packages/vm/test/api/EIPs/eip-7702.spec.ts b/packages/vm/test/api/EIPs/eip-7702.spec.ts index a1a5c107f5..4d4a56c673 100644 --- a/packages/vm/test/api/EIPs/eip-7702.spec.ts +++ b/packages/vm/test/api/EIPs/eip-7702.spec.ts @@ -25,6 +25,9 @@ import type { VM } from '../../../src/index.js' import type { AuthorizationListBytesItem } from '@ethereumjs/tx' import type { PrefixedHexString } from '@ethereumjs/util' +// EIP-7702 code designator. If code starts with these bytes, it is a 7702-delegated address +const eip7702Designator = hexToBytes('0xef01') + const common = new Common({ chain: Mainnet, hardfork: Hardfork.Cancun, eips: [7702] }) const defaultAuthPkey = hexToBytes(`0x${'20'.repeat(32)}`) @@ -248,21 +251,21 @@ describe('test EIP-7702 opcodes', () => { { // PUSH20 EXTCODESIZE PUSH0 SSTORE STOP code: `0x73${defaultAuthAddr.toString().slice(2)}3b5f5500`, - expectedStorage: bigIntToUnpaddedBytes(BigInt(randomCode.length)), + expectedStorage: bigIntToUnpaddedBytes(BigInt(eip7702Designator.length)), name: 'EXTCODESIZE', }, // EXTCODEHASH { // PUSH20 EXTCODEHASH PUSH0 SSTORE STOP code: `0x73${defaultAuthAddr.toString().slice(2)}3f5f5500`, - expectedStorage: keccak256(randomCode), + expectedStorage: keccak256(eip7702Designator), name: 'EXTCODEHASH', }, // EXTCODECOPY { // PUSH1 32 PUSH0 PUSH0 PUSH20 EXTCODEHASH PUSH0 MLOAD PUSH0 SSTORE STOP code: `0x60205f5f73${defaultAuthAddr.toString().slice(2)}3c5f515f5500`, - expectedStorage: setLengthRight(randomCode, 32), + expectedStorage: setLengthRight(eip7702Designator, 32), name: 'EXTCODECOPY', }, ] From d4e94960c02be30a0a6ce73f2e01eaa2c13cbf3e Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Tue, 26 Nov 2024 16:28:55 +0100 Subject: [PATCH 05/17] evm: remove redundant test --- packages/evm/test/eips/eip-7702.spec.ts | 99 ------------------------- 1 file changed, 99 deletions(-) delete mode 100644 packages/evm/test/eips/eip-7702.spec.ts diff --git a/packages/evm/test/eips/eip-7702.spec.ts b/packages/evm/test/eips/eip-7702.spec.ts deleted file mode 100644 index 7e648b5ed0..0000000000 --- a/packages/evm/test/eips/eip-7702.spec.ts +++ /dev/null @@ -1,99 +0,0 @@ -import { Common, Hardfork, Mainnet } from '@ethereumjs/common' -import { - Address, - bytesToBigInt, - equalsBytes, - hexToBytes, - setLengthRight, - unprefixedHexToBytes, -} from '@ethereumjs/util' -import { keccak256 } from 'ethereum-cryptography/keccak' -import { assert, describe, it } from 'vitest' - -import { createEVM } from '../../src/index.js' - -const eip7702Designator = hexToBytes('0xef01') -const addressHex = '01'.repeat(20) // Address as unprefixed hex string -const address = new Address(unprefixedHexToBytes(addressHex)) - -const delegationCode = hexToBytes('0xef0100' + addressHex) // This code will delegate to `address` - -// Helpers for unprefixed hex strings of opcodes -const EXTCODESIZE = '3B' -const EXTCODECOPY = '3C' -const EXTCODEHASH = '3F' - -// This code stores the topmost stack item in slot 0 -const STORE_TOP_STACK_CODE = '5F55' - -// This code pushes the `address` to the stack -const PUSH_Address = '73' + addressHex // PUSH20
as unprefixed hex string - -const testCodeAddress = new Address(unprefixedHexToBytes('02'.repeat(20))) - -// Setups EVM with an account at `address` which is delegated to itself -async function getEVM() { - const common = new Common({ - chain: Mainnet, - hardfork: Hardfork.Prague, - }) - const evm = await createEVM({ - common, - }) - await evm.stateManager.putCode(address, delegationCode) - return evm -} - -describe('EIP 7702 tests', () => { - it('EXTCODESIZE', async () => { - const evm = await getEVM() - const code = unprefixedHexToBytes(PUSH_Address + EXTCODESIZE + STORE_TOP_STACK_CODE) - await evm.stateManager.putCode(testCodeAddress, code) - - await evm.runCall({ - to: testCodeAddress, - gasLimit: BigInt(100_000), - }) - - const result = await evm.stateManager.getStorage(testCodeAddress, new Uint8Array(32)) - const expected = BigInt(eip7702Designator.length) - - assert.equal(bytesToBigInt(result), expected) - }) - - it('EXTCODEHASH', async () => { - const evm = await getEVM() - const code = unprefixedHexToBytes(PUSH_Address + EXTCODEHASH + STORE_TOP_STACK_CODE) - await evm.stateManager.putCode(testCodeAddress, code) - - await evm.runCall({ - to: testCodeAddress, - gasLimit: BigInt(100_000), - }) - - const result = await evm.stateManager.getStorage(testCodeAddress, new Uint8Array(32)) - const expected = keccak256(eip7702Designator) - - assert.ok(equalsBytes(result, expected)) - }) - - it('EXTCODECOPY', async () => { - const evm = await getEVM() - // This code does some extra logic than other tests, because it has to setup EXTCODECOPY (4 stack items instead of 1) - // It EXTCODECOPYs 32 bytes of the delegated address in memory at key 0, and then MLOADs key 0 before storing the result - const code = unprefixedHexToBytes( - '60205F5F' + PUSH_Address + EXTCODECOPY + '5F51' + STORE_TOP_STACK_CODE, - ) - await evm.stateManager.putCode(testCodeAddress, code) - - await evm.runCall({ - to: testCodeAddress, - gasLimit: BigInt(100_000), - }) - - const result = await evm.stateManager.getStorage(testCodeAddress, new Uint8Array(32)) - const expected = setLengthRight(eip7702Designator, 32) - - assert.ok(equalsBytes(result, expected)) - }) -}) From caa9e1cfbb4a9f0728a4071b7725af1c86ea467b Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Wed, 27 Nov 2024 15:02:00 +0100 Subject: [PATCH 06/17] remove accidental console.log --- packages/client/test/rpc/engine/newPayloadV4.spec.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/client/test/rpc/engine/newPayloadV4.spec.ts b/packages/client/test/rpc/engine/newPayloadV4.spec.ts index 4acd9a9ea4..3fdda74f04 100644 --- a/packages/client/test/rpc/engine/newPayloadV4.spec.ts +++ b/packages/client/test/rpc/engine/newPayloadV4.spec.ts @@ -181,7 +181,6 @@ describe(`${method}: call with executionPayloadV4`, () => { } res = await rpc.request(method, [validBlock, [], parentBeaconBlockRoot, ['0x', '0x', '0x']]) - console.log(res) assert.equal(res.result.status, 'VALID') res = await rpc.request('engine_forkchoiceUpdatedV3', validPayload) From 74a2123ef9897731244435ff88f3f5c802202721 Mon Sep 17 00:00:00 2001 From: harkamal Date: Mon, 16 Dec 2024 15:50:34 +0530 Subject: [PATCH 07/17] remove 7742 and add 7691 independently --- packages/block/src/params.ts | 9 +++++++++ packages/common/src/eips.ts | 9 +++++++++ packages/common/src/hardforks.ts | 2 +- packages/evm/src/evm.ts | 2 +- packages/tx/src/4844/tx.ts | 8 +++++--- packages/tx/src/constants.ts | 1 - packages/vm/src/params.ts | 6 ++++++ 7 files changed, 31 insertions(+), 6 deletions(-) diff --git a/packages/block/src/params.ts b/packages/block/src/params.ts index d4277f3329..622937dab2 100644 --- a/packages/block/src/params.ts +++ b/packages/block/src/params.ts @@ -85,4 +85,13 @@ export const paramsBlock: ParamsDict = { // pow difficultyBombDelay: 11400000, // the amount of blocks to delay the difficulty bomb with }, + /** +. * Blob throughput increase +. */ + 7691: { + // gasConfig + targetBlobGasPerBlock: 786432, // The target blob gas consumed per block + maxblobGasPerBlock: 1179648, // The max blob gas allowable per block + blobGasPriceUpdateFraction: 5007716, // The denominator used in the exponential when calculating a blob gas price + }, } diff --git a/packages/common/src/eips.ts b/packages/common/src/eips.ts index 091fae9ce4..6c43bfa341 100644 --- a/packages/common/src/eips.ts +++ b/packages/common/src/eips.ts @@ -415,6 +415,15 @@ export const eipsDict: EIPsDict = { minimumHardfork: Hardfork.Cancun, requiredEIPs: [3675], }, + /** + * Description : Blob throughput increase + * URL : https://eips.ethereum.org/EIPS/eip-7691 + * Status : Review + */ + 7691: { + minimumHardfork: Hardfork.Paris, + requiredEIPs: [4844], + }, /** * Description : EVM Object Format (EOFv1) Meta * URL : https://github.com/ethereum/EIPs/blob/4153e95befd0264082de3c4c2fe3a85cc74d3152/EIPS/eip-7692.md diff --git a/packages/common/src/hardforks.ts b/packages/common/src/hardforks.ts index 1f3a8fbf8e..b6f045d37c 100644 --- a/packages/common/src/hardforks.ts +++ b/packages/common/src/hardforks.ts @@ -160,7 +160,7 @@ export const hardforksDict: HardforksDict = { prague: { // TODO update this accordingly to the right devnet setup //eips: [663, 3540, 3670, 4200, 4750, 5450, 6206, 7069, 7480, 7620, 7692, 7698], // This is EOF-only - eips: [2537, 2935, 6110, 7002, 7251, 7685, 7702], // This is current prague without EOF + eips: [2537, 2935, 6110, 7002, 7251, 7685, 7691, 7702], // This is current prague without EOF }, /** * Description: Next feature hardfork after prague, internally used for verkle testing/implementation (incomplete/experimental) diff --git a/packages/evm/src/evm.ts b/packages/evm/src/evm.ts index 5e86d49d60..218acb07f6 100644 --- a/packages/evm/src/evm.ts +++ b/packages/evm/src/evm.ts @@ -182,7 +182,7 @@ export class EVM implements EVMInterface { const supportedEIPs = [ 663, 1153, 1559, 2537, 2565, 2718, 2929, 2930, 2935, 3198, 3529, 3540, 3541, 3607, 3651, 3670, 3855, 3860, 4200, 4399, 4750, 4788, 4844, 4895, 5133, 5450, 5656, 6110, 6206, 6780, 6800, - 7002, 7069, 7251, 7480, 7516, 7620, 7685, 7692, 7698, 7702, 7709, + 7002, 7069, 7251, 7480, 7516, 7620, 7685, 7691, 7692, 7698, 7702, 7709, ] for (const eip of this.common.eips()) { diff --git a/packages/tx/src/4844/tx.ts b/packages/tx/src/4844/tx.ts index f1af09dc11..d3eedb33ad 100644 --- a/packages/tx/src/4844/tx.ts +++ b/packages/tx/src/4844/tx.ts @@ -15,7 +15,6 @@ import * as EIP1559 from '../capabilities/eip1559.js' import * as EIP2718 from '../capabilities/eip2718.js' import * as EIP2930 from '../capabilities/eip2930.js' import * as Legacy from '../capabilities/legacy.js' -import { LIMIT_BLOBS_PER_TX } from '../constants.js' import { getBaseJSON, sharedConstructor, valueBoundaryCheck } from '../features/util.js' import { TransactionType } from '../types.js' import { AccessLists, validateNotArray } from '../util.js' @@ -172,13 +171,16 @@ export class Blob4844Tx implements TransactionInterface LIMIT_BLOBS_PER_TX) { - const msg = Legacy.errorMsg(this, `tx can contain at most ${LIMIT_BLOBS_PER_TX} blobs`) + + const limitBlobsPerTx = this.common.param('maxblobGasPerBlock') / this.common.param('blobGasPerBlob') + if (this.blobVersionedHashes.length > limitBlobsPerTx) { + const msg = Legacy.errorMsg(this, `tx can contain at most ${limitBlobsPerTx} blobs`) throw new Error(msg) } else if (this.blobVersionedHashes.length === 0) { const msg = Legacy.errorMsg(this, `tx should contain at least one blob`) throw new Error(msg) } + if (this.to === undefined) { const msg = Legacy.errorMsg( this, diff --git a/packages/tx/src/constants.ts b/packages/tx/src/constants.ts index e3f73893f9..828ebb6ba0 100644 --- a/packages/tx/src/constants.ts +++ b/packages/tx/src/constants.ts @@ -3,7 +3,6 @@ export const MAX_CALLDATA_SIZE = 16777216 // 2 ** 24 export const MAX_ACCESS_LIST_SIZE = 16777216 // 2 ** 24 export const MAX_VERSIONED_HASHES_LIST_SIZE = 16777216 // 2 ** 24 -export const LIMIT_BLOBS_PER_TX = 6 // 786432 / 2^17 (`MAX_BLOB_GAS_PER_BLOCK` / `GAS_PER_BLOB`) export const MAX_TX_WRAP_KZG_COMMITMENTS = 16777216 // 2 ** 24 export const FIELD_ELEMENTS_PER_BLOB = 4096 // This is also in the Common 4844 parameters but needed here since types can't access Common params export const BYTES_PER_FIELD_ELEMENT = 32 diff --git a/packages/vm/src/params.ts b/packages/vm/src/params.ts index dfaa021cda..e1bc6d1b67 100644 --- a/packages/vm/src/params.ts +++ b/packages/vm/src/params.ts @@ -91,4 +91,10 @@ export const paramsVM: ParamsDict = { // See: https://github.com/ethereum/EIPs/pull/8934/files consolidationRequestPredeployAddress: '0x01aBEa29659e5e97C95107F20bb753cD3e09bBBb', // Address of the consolidations contract }, + /** +. * Shard Blob Transactions +. */ + 7691: { + maxblobGasPerBlock: 1179648, // The max blob gas allowable per block + }, } From 7f5c4c5f2772615c7970096f0f7a24177279692f Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Mon, 16 Dec 2024 23:56:40 +0100 Subject: [PATCH 08/17] fix t8ntool empty requests reporting --- packages/vm/test/t8n/t8ntool.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/vm/test/t8n/t8ntool.ts b/packages/vm/test/t8n/t8ntool.ts index 19605a8fe1..d57563513e 100644 --- a/packages/vm/test/t8n/t8ntool.ts +++ b/packages/vm/test/t8n/t8ntool.ts @@ -303,7 +303,12 @@ export class TransitionTool { if (requests !== undefined) { // NOTE: EEST currently wants the raw request bytes, **excluding** the type - output.requests = requests.map((request) => bytesToHex(request.bytes.slice(1))) + output.requests = [] + for (const request of requests) { + if (request.bytes.length > 1) { + output.requests.push(bytesToHex(request.bytes.slice(1))) + } + } } if (this.rejected.length > 0) { From 13aafd650dcfafd688436ce82ec44cc9c5f8215b Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Thu, 19 Dec 2024 02:41:49 +0100 Subject: [PATCH 09/17] client: fix engine reporting empty requests --- .../src/rpc/modules/engine/util/getPayload.ts | 13 ++++- .../src/rpc/modules/engine/util/newPayload.ts | 49 ++++++++++--------- .../test/rpc/engine/newPayloadV4.spec.ts | 17 +++++-- 3 files changed, 53 insertions(+), 26 deletions(-) diff --git a/packages/client/src/rpc/modules/engine/util/getPayload.ts b/packages/client/src/rpc/modules/engine/util/getPayload.ts index fec0814f2d..615fbe04f7 100644 --- a/packages/client/src/rpc/modules/engine/util/getPayload.ts +++ b/packages/client/src/rpc/modules/engine/util/getPayload.ts @@ -25,9 +25,20 @@ export const blockToExecutionPayload = ( // ethereumjs does not provide any transaction censoring detection (yet) to suggest // overriding builder/mev-boost blocks const shouldOverrideBuilder = false + + let executionRequests = undefined + if (requests !== undefined) { + executionRequests = [] + for (const request of requests) { + if (request.bytes.length > 1) { + executionRequests.push(bytesToHex(request.bytes)) + } + } + } + return { executionPayload, - executionRequests: requests?.map((req) => bytesToHex(req.data)), + executionRequests, blockValue: bigIntToHex(value), blobsBundle, shouldOverrideBuilder, diff --git a/packages/client/src/rpc/modules/engine/util/newPayload.ts b/packages/client/src/rpc/modules/engine/util/newPayload.ts index 5155ddfc5a..c2c42e6f7a 100644 --- a/packages/client/src/rpc/modules/engine/util/newPayload.ts +++ b/packages/client/src/rpc/modules/engine/util/newPayload.ts @@ -57,30 +57,35 @@ export const validateAndGen7685RequestsHash = ( common: Common, executionRequests: PrefixedHexString[], ): PrefixedHexString => { - let validationError: string | null = null - const requests: CLRequest[] = [] - let requestIndex = 0 - if (common.isActivatedEIP(6110)) { - requests.push(new CLRequest(CLRequestType.Deposit, hexToBytes(executionRequests[requestIndex]))) - requestIndex++ - } - if (common.isActivatedEIP(7002)) { - requests.push( - new CLRequest(CLRequestType.Withdrawal, hexToBytes(executionRequests[requestIndex])), - ) - requestIndex++ - } - if (common.isActivatedEIP(7251)) { - requests.push( - new CLRequest(CLRequestType.Consolidation, hexToBytes(executionRequests[requestIndex])), - ) - requestIndex++ - } - if (requestIndex !== executionRequests.length) { - validationError = `Invalid executionRequests=${executionRequests.length} expected=${requestIndex}` - throw validationError + for (const request of executionRequests) { + const bytes = hexToBytes(request) + if (bytes.length === 0) { + throw new Error('Got a request without a request-identifier') + } + switch (bytes[0]) { + case 0: + if (!common.isActivatedEIP(6110)) { + throw new Error(`Deposit requests are not active`) + } + requests.push(new CLRequest(CLRequestType.Deposit, bytes.slice(1))) + break + case 1: + if (!common.isActivatedEIP(7002)) { + throw new Error(`Withdrawal requests are not active`) + } + requests.push(new CLRequest(CLRequestType.Withdrawal, bytes.slice(1))) + break + case 2: + if (!common.isActivatedEIP(7251)) { + throw new Error(`Consolidation requests are not active`) + } + requests.push(new CLRequest(CLRequestType.Consolidation, bytes.slice(1))) + break + default: + throw new Error(`Unknown request identifier: got ${bytes[0]}`) + } } const sha256Function = common.customCrypto.sha256 ?? sha256 diff --git a/packages/client/test/rpc/engine/newPayloadV4.spec.ts b/packages/client/test/rpc/engine/newPayloadV4.spec.ts index 3fdda74f04..6b5634024b 100644 --- a/packages/client/test/rpc/engine/newPayloadV4.spec.ts +++ b/packages/client/test/rpc/engine/newPayloadV4.spec.ts @@ -180,7 +180,7 @@ describe(`${method}: call with executionPayloadV4`, () => { assert.ok(res.error.message.includes(expectedError)) } - res = await rpc.request(method, [validBlock, [], parentBeaconBlockRoot, ['0x', '0x', '0x']]) + res = await rpc.request(method, [validBlock, [], parentBeaconBlockRoot, []]) assert.equal(res.result.status, 'VALID') res = await rpc.request('engine_forkchoiceUpdatedV3', validPayload) @@ -201,9 +201,20 @@ describe(`${method}: call with executionPayloadV4`, () => { res = await rpc.request('engine_getPayloadV4', [payloadId]) const { executionPayload, executionRequests } = res.result + + assert.ok( + executionRequests?.length === 1, + 'executionRequests should have the deposit request, and should exclude the other requests (these are empty)', + ) + + const depositRequestBytes = hexToBytes(executionRequests[0]) + assert.ok( + depositRequestBytes[0] === 0x00, + 'deposit request byte 0 is the deposit request identifier byte (0x00)', + ) assert.ok( - executionRequests?.length === 3, - 'executionRequests should have 3 entries for each request type', + depositRequestBytes.length > 1, + 'deposit request includes data (and is thus not empty)', ) res = await rpc.request(method, [ From fc3c150fdd675bb05c078afbef9dadecffb4abf8 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Thu, 19 Dec 2024 02:52:24 +0100 Subject: [PATCH 10/17] t8n: fix requests reporting --- packages/vm/test/t8n/t8ntool.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vm/test/t8n/t8ntool.ts b/packages/vm/test/t8n/t8ntool.ts index d57563513e..fe322ffac7 100644 --- a/packages/vm/test/t8n/t8ntool.ts +++ b/packages/vm/test/t8n/t8ntool.ts @@ -306,7 +306,7 @@ export class TransitionTool { output.requests = [] for (const request of requests) { if (request.bytes.length > 1) { - output.requests.push(bytesToHex(request.bytes.slice(1))) + output.requests.push(bytesToHex(request.bytes)) } } } From fb7e67f73b5af74c94ccd7b45ebb57a7e24723c3 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Thu, 19 Dec 2024 04:18:16 +0100 Subject: [PATCH 11/17] Implement EIP 7623: calldata cost increase (#3813) * common/evm/vm: implement EIP7623 calldata cost increase * move the floorcost update and add debug log * fix t8ntool empty requests reporting * common: add 7623 * tx: add 7623 validator * t8n: throw on invalid txs * make cspell happy --------- Co-authored-by: harkamal --- packages/common/src/eips.ts | 9 ++ packages/common/src/hardforks.ts | 2 +- packages/tx/src/capabilities/legacy.ts | 17 ++- packages/tx/src/params.ts | 6 + packages/vm/src/runTx.ts | 29 ++++- packages/vm/test/api/EIPs/eip-7623.spec.ts | 125 +++++++++++++++++++++ packages/vm/test/t8n/t8ntool.ts | 5 +- 7 files changed, 187 insertions(+), 6 deletions(-) create mode 100644 packages/vm/test/api/EIPs/eip-7623.spec.ts diff --git a/packages/common/src/eips.ts b/packages/common/src/eips.ts index 6c43bfa341..1c6e31aab0 100644 --- a/packages/common/src/eips.ts +++ b/packages/common/src/eips.ts @@ -405,6 +405,15 @@ export const eipsDict: EIPsDict = { */ requiredEIPs: [3540, 3541, 3670], }, + /** + * Description : Increase calldata cost to reduce maximum block size + * URL : https://github.com/ethereum/EIPs/blob/da2a86bf15044416e8eb0301c9bdb8d561feeb32/EIPS/eip-7623.md + * Status : Review + */ + 7623: { + minimumHardfork: Hardfork.Chainstart, + requiredEIPs: [], + }, /** * Description : General purpose execution layer requests * URL : https://eips.ethereum.org/EIPS/eip-7685 diff --git a/packages/common/src/hardforks.ts b/packages/common/src/hardforks.ts index b6f045d37c..f83f077111 100644 --- a/packages/common/src/hardforks.ts +++ b/packages/common/src/hardforks.ts @@ -160,7 +160,7 @@ export const hardforksDict: HardforksDict = { prague: { // TODO update this accordingly to the right devnet setup //eips: [663, 3540, 3670, 4200, 4750, 5450, 6206, 7069, 7480, 7620, 7692, 7698], // This is EOF-only - eips: [2537, 2935, 6110, 7002, 7251, 7685, 7691, 7702], // This is current prague without EOF + eips: [2537, 2935, 6110, 7002, 7251, 7623, 7685, 7691, 7702], // This is current prague without EOF }, /** * Description: Next feature hardfork after prague, internally used for verkle testing/implementation (incomplete/experimental) diff --git a/packages/tx/src/capabilities/legacy.ts b/packages/tx/src/capabilities/legacy.ts index be56064168..39813721e9 100644 --- a/packages/tx/src/capabilities/legacy.ts +++ b/packages/tx/src/capabilities/legacy.ts @@ -2,6 +2,7 @@ import { Address, BIGINT_0, SECP256K1_ORDER_DIV_2, + bigIntMax, bigIntToUnpaddedBytes, bytesToHex, ecrecover, @@ -167,8 +168,20 @@ export function getValidationErrors(tx: LegacyTxInterface): string[] { errors.push('Invalid Signature') } - if (tx.getIntrinsicGas() > tx.gasLimit) { - errors.push(`gasLimit is too low. given ${tx.gasLimit}, need at least ${tx.getIntrinsicGas()}`) + let intrinsicGas = tx.getIntrinsicGas() + if (tx.common.isActivatedEIP(7623)) { + let tokens = 0 + for (let i = 0; i < tx.data.length; i++) { + tokens += tx.data[i] === 0 ? 1 : 4 + } + const floorCost = + tx.common.param('txGas') + tx.common.param('totalCostFloorPerToken') * BigInt(tokens) + intrinsicGas = bigIntMax(intrinsicGas, floorCost) + } + if (intrinsicGas > tx.gasLimit) { + errors.push( + `gasLimit is too low. The gasLimit is lower than the minimum gas limit of ${tx.getIntrinsicGas()}, the gas limit is: ${tx.gasLimit}`, + ) } return errors diff --git a/packages/tx/src/params.ts b/packages/tx/src/params.ts index 66ce2302bb..d5de087267 100644 --- a/packages/tx/src/params.ts +++ b/packages/tx/src/params.ts @@ -44,6 +44,12 @@ export const paramsTx: ParamsDict = { blobCommitmentVersionKzg: 1, // The number indicated a versioned hash is a KZG commitment }, /** + * Increase calldata cost to reduce maximum block size + */ + 7623: { + totalCostFloorPerToken: 10, + }, + /** . * Set EOA account code for one transaction . */ 7702: { diff --git a/packages/vm/src/runTx.ts b/packages/vm/src/runTx.ts index 3697f810ad..7624dc34dc 100644 --- a/packages/vm/src/runTx.ts +++ b/packages/vm/src/runTx.ts @@ -12,6 +12,7 @@ import { KECCAK256_NULL, MAX_UINT64, SECP256K1_ORDER_DIV_2, + bigIntMax, bytesToBigInt, bytesToHex, bytesToUnprefixedHex, @@ -249,11 +250,24 @@ async function _runTx(vm: VM, opts: RunTxOpts): Promise { // Validate gas limit against tx base fee (DataFee + TxFee + Creation Fee) const intrinsicGas = tx.getIntrinsicGas() + let floorCost = BIGINT_0 + + if (vm.common.isActivatedEIP(7623)) { + // Tx should at least cover the floor price for tx data + let tokens = 0 + for (let i = 0; i < tx.data.length; i++) { + tokens += tx.data[i] === 0 ? 1 : 4 + } + floorCost = + tx.common.param('txGas') + tx.common.param('totalCostFloorPerToken') * BigInt(tokens) + } + let gasLimit = tx.gasLimit - if (gasLimit < intrinsicGas) { + const minGasLimit = bigIntMax(intrinsicGas, floorCost) + if (gasLimit < minGasLimit) { const msg = _errorMsg( `tx gas limit ${Number(gasLimit)} is lower than the minimum gas limit of ${Number( - intrinsicGas, + minGasLimit, )}`, vm, block, @@ -614,6 +628,15 @@ async function _runTx(vm: VM, opts: RunTxOpts): Promise { debugGas(`tx add baseFee ${intrinsicGas} to totalGasSpent (-> ${results.totalGasSpent})`) } + if (vm.common.isActivatedEIP(7623)) { + if (results.totalGasSpent < floorCost) { + results.totalGasSpent = floorCost + if (vm.DEBUG) { + debugGas(`tx apply floorCost ${floorCost} to totalGasSpent (-> ${results.totalGasSpent})`) + } + } + } + // Add blob gas used to result if (isBlob4844Tx(tx)) { results.blobGasUsed = totalblobGas @@ -635,6 +658,8 @@ async function _runTx(vm: VM, opts: RunTxOpts): Promise { debug(`No tx gasRefund`) } } + + results.amountSpent = results.totalGasSpent * gasPrice // Update sender's balance diff --git a/packages/vm/test/api/EIPs/eip-7623.spec.ts b/packages/vm/test/api/EIPs/eip-7623.spec.ts new file mode 100644 index 0000000000..f362cb8b66 --- /dev/null +++ b/packages/vm/test/api/EIPs/eip-7623.spec.ts @@ -0,0 +1,125 @@ +import { createBlock } from '@ethereumjs/block' +import { Common, Hardfork, Mainnet } from '@ethereumjs/common' +import { createLegacyTx } from '@ethereumjs/tx' +import { Account, Address, createZeroAddress, hexToBytes, privateToAddress } from '@ethereumjs/util' +import { assert, describe, it } from 'vitest' + +import { createVM, runTx } from '../../../src/index.js' + +const common = new Common({ chain: Mainnet, hardfork: Hardfork.Prague }) + +const pkey = hexToBytes(`0x${'20'.repeat(32)}`) +const GWEI = BigInt(1000000000) +const sender = new Address(privateToAddress(pkey)) + +const coinbase = new Address(hexToBytes(`0x${'ff'.repeat(20)}`)) + +const block = createBlock( + { + header: { + baseFeePerGas: 7, + coinbase, + }, + }, + { common }, +) + +const code = hexToBytes('0x60008080806001415AF100') +const contractAddress = new Address(hexToBytes(`0x${'ee'.repeat(20)}`)) + +async function getVM(common: Common) { + const vm = await createVM({ common }) + await vm.stateManager.putAccount(sender, new Account()) + const account = await vm.stateManager.getAccount(sender) + const balance = GWEI * BigInt(21000) * BigInt(10000000) + account!.balance = balance + await vm.stateManager.putAccount(sender, account!) + + await vm.stateManager.putCode(contractAddress, code) + return vm +} + +describe('EIP 7623 calldata cost increase tests', () => { + it('charges floor gas', async () => { + const vm = await getVM(common) + + const tx = createLegacyTx( + { + to: createZeroAddress(), + data: new Uint8Array(100).fill(1), + gasLimit: 1000000, + gasPrice: 10, + }, + { common }, + ).sign(pkey) + + const result = await runTx(vm, { + block, + tx, + skipHardForkValidation: true, + }) + + const baseCost = tx.common.param('txGas') + const floorCost = tx.common.param('totalCostFloorPerToken') + + const expected = baseCost + BigInt(tx.data.length) * BigInt(4) * floorCost + + assert.equal(result.totalGasSpent, expected) + }) + it('rejects transactions having a gas limit below the floor gas limit', async () => { + const vm = await getVM(common) + + const tx = createLegacyTx( + { + to: createZeroAddress(), + data: new Uint8Array(100).fill(1), + gasLimit: 21000 + 100 * 4, + gasPrice: 10, + }, + { common }, + ).sign(pkey) + try { + await runTx(vm, { + block, + tx, + skipHardForkValidation: true, + }) + assert.fail('runTx should throw') + } catch (e) { + assert.ok('Successfully failed') + } + }) + it('correctly charges execution gas instead of floor gas when execution gas exceeds the floor gas', async () => { + const vm = await getVM(common) + const to = createZeroAddress() + + // Store 1 in slot 1 + await vm.stateManager.putCode(to, hexToBytes('0x6001600155')) + + const tx = createLegacyTx( + { + to: createZeroAddress(), + data: new Uint8Array(100).fill(1), + gasLimit: 1000000, + gasPrice: 10, + }, + { common }, + ).sign(pkey) + + const result = await runTx(vm, { + block, + tx, + skipHardForkValidation: true, + }) + + const baseCost = tx.common.param('txGas') + + const expected = + baseCost + + BigInt(tx.data.length) * tx.common.param('txDataNonZeroGas') + + BigInt(2 * 3) + + BigInt(22_100) + + assert.equal(result.totalGasSpent, expected) + }) +}) diff --git a/packages/vm/test/t8n/t8ntool.ts b/packages/vm/test/t8n/t8ntool.ts index fe322ffac7..bfb93d3b79 100644 --- a/packages/vm/test/t8n/t8ntool.ts +++ b/packages/vm/test/t8n/t8ntool.ts @@ -171,6 +171,9 @@ export class TransitionTool { for (const txData of this.txsData) { try { const tx = createTx(txData, { common: this.common }) + if (!tx.isValid()) { + throw new Error(tx.getValidationErrors().join(', ')) + } // Set `allowNoBlobs` to `true`, since the test might not have the blob // The 4844-tx at this should still be valid, since it has the `blobHashes` field await builder.addTransaction(tx, { allowNoBlobs: true }) @@ -306,7 +309,7 @@ export class TransitionTool { output.requests = [] for (const request of requests) { if (request.bytes.length > 1) { - output.requests.push(bytesToHex(request.bytes)) + output.requests.push(bytesToHex(request.bytes.slice(1))) } } } From 98806ff463ec42ad8f2ae17246cb7090350ba1a1 Mon Sep 17 00:00:00 2001 From: Lucas Saldanha Date: Thu, 19 Dec 2024 17:29:03 +1300 Subject: [PATCH 12/17] client: fix requests contract address (devnet-5) (#3818) --- packages/vm/src/params.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/vm/src/params.ts b/packages/vm/src/params.ts index e1bc6d1b67..da50338b39 100644 --- a/packages/vm/src/params.ts +++ b/packages/vm/src/params.ts @@ -39,7 +39,7 @@ export const paramsVM: ParamsDict = { */ 2935: { // config - historyStorageAddress: '0x0aae40965e6800cd9b1f4b05ff21581047e3f91e', // The address where the historical blockhashes are stored + historyStorageAddress: '0x0F792be4B0c0cb4DAE440Ef133E90C0eCD48CCCC', // The address where the historical blockhashes are stored historyServeWindow: 8192, // The amount of blocks to be served by the historical blockhash contract }, /** @@ -79,7 +79,7 @@ export const paramsVM: ParamsDict = { // config systemAddress: '0xfffffffffffffffffffffffffffffffffffffffe', // The system address to perform operations on the withdrawal requests predeploy address // See: https://github.com/ethereum/EIPs/pull/8934/files - withdrawalRequestPredeployAddress: '0x09Fc772D0857550724b07B850a4323f39112aAaA', // Address of the validator excess address + withdrawalRequestPredeployAddress: '0x0c15F14308530b7CDB8460094BbB9cC28b9AaaAA', // Address of the validator excess address }, /** @@ -89,7 +89,7 @@ export const paramsVM: ParamsDict = { // config systemAddress: '0xfffffffffffffffffffffffffffffffffffffffe', // The system address to perform operations on the consolidation requests predeploy address // See: https://github.com/ethereum/EIPs/pull/8934/files - consolidationRequestPredeployAddress: '0x01aBEa29659e5e97C95107F20bb753cD3e09bBBb', // Address of the consolidations contract + consolidationRequestPredeployAddress: '0x00431F263cE400f4455c2dCf564e53007Ca4bbBb', // Address of the consolidations contract }, /** . * Shard Blob Transactions From 5079664401866fa2d3b3de04686827334687bc32 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Sun, 22 Dec 2024 03:28:45 +0100 Subject: [PATCH 13/17] update chainid to uint256 --- packages/tx/src/util.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/tx/src/util.ts b/packages/tx/src/util.ts index 983111fdae..5a051167a4 100644 --- a/packages/tx/src/util.ts +++ b/packages/tx/src/util.ts @@ -210,8 +210,8 @@ export class AuthorizationLists { if (address.length !== 20) { throw new Error('Invalid EIP-7702 transaction: address length should be 20 bytes') } - if (bytesToBigInt(chainId) > MAX_UINT64) { - throw new Error('Invalid EIP-7702 transaction: chainId exceeds 2^64 - 1') + if (bytesToBigInt(chainId) > MAX_INTEGER) { + throw new Error('Invalid EIP-7702 transaction: chainId exceeds 2^256 - 1') } if (bytesToBigInt(nonce) > MAX_UINT64) { throw new Error('Invalid EIP-7702 transaction: nonce exceeds 2^64 - 1') From c827859d1d3accdc1cb7202c01eb4f3bf99108b2 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Sun, 22 Dec 2024 13:07:10 +0100 Subject: [PATCH 14/17] evm: update bls gas (eips PR 9097) --- packages/evm/src/params.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/evm/src/params.ts b/packages/evm/src/params.ts index 397e746e20..4ebf09fb5e 100644 --- a/packages/evm/src/params.ts +++ b/packages/evm/src/params.ts @@ -234,14 +234,14 @@ export const paramsEVM: ParamsDict = { */ 2537: { // gasPrices - bls12381G1AddGas: 500, // Gas cost of a single BLS12-381 G1 addition precompile-call + bls12381G1AddGas: 375, // Gas cost of a single BLS12-381 G1 addition precompile-call bls12381G1MulGas: 12000, // Gas cost of a single BLS12-381 G1 multiplication precompile-call - bls12381G2AddGas: 800, // Gas cost of a single BLS12-381 G2 addition precompile-call - bls12381G2MulGas: 45000, // Gas cost of a single BLS12-381 G2 multiplication precompile-call + bls12381G2AddGas: 600, // Gas cost of a single BLS12-381 G2 addition precompile-call + bls12381G2MulGas: 22500, // Gas cost of a single BLS12-381 G2 multiplication precompile-call bls12381PairingBaseGas: 65000, // Base gas cost of BLS12-381 pairing check bls12381PairingPerPairGas: 43000, // Per-pair gas cost of BLS12-381 pairing check bls12381MapG1Gas: 5500, // Gas cost of BLS12-381 map field element to G1 - bls12381MapG2Gas: 75000, // Gas cost of BLS12-381 map field element to G2 + bls12381MapG2Gas: 23800, // Gas cost of BLS12-381 map field element to G2 }, /** . * Gas cost increases for state access opcodes From e4adff7f59c884aba77123a25c4de511abe00eef Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Sun, 22 Dec 2024 13:12:58 +0100 Subject: [PATCH 15/17] evm: bls update msm gas (eips PR 9116) --- .../evm/src/precompiles/0d-bls12-g1msm.ts | 8 +- .../evm/src/precompiles/10-bls12-g2msm.ts | 8 +- .../src/precompiles/bls12_381/constants.ts | 391 ++++++++++++------ .../evm/src/precompiles/bls12_381/util.ts | 14 +- 4 files changed, 281 insertions(+), 140 deletions(-) diff --git a/packages/evm/src/precompiles/0d-bls12-g1msm.ts b/packages/evm/src/precompiles/0d-bls12-g1msm.ts index ac67b5be48..5ec0e3829f 100644 --- a/packages/evm/src/precompiles/0d-bls12-g1msm.ts +++ b/packages/evm/src/precompiles/0d-bls12-g1msm.ts @@ -3,7 +3,11 @@ import { bytesToHex } from '@ethereumjs/util' import { EvmErrorResult, OOGResult } from '../evm.js' import { ERROR, EvmError } from '../exceptions.js' -import { leading16ZeroBytesCheck, msmGasUsed } from './bls12_381/index.js' +import { + BLS_GAS_DISCOUNT_PAIRS_G1, + leading16ZeroBytesCheck, + msmGasUsed, +} from './bls12_381/index.js' import { gasLimitCheck, moduloLengthCheck } from './util.js' import { getPrecompileName } from './index.js' @@ -29,7 +33,7 @@ export async function precompile0d(opts: PrecompileInput): Promise { // validation (same for g2msm) const numPairs = Math.floor(inputData.length / 160) const gasUsedPerPair = opts.common.paramByEIP('bls12381G1MulGas', 2537) ?? BigInt(0) - const gasUsed = msmGasUsed(numPairs, gasUsedPerPair) + const gasUsed = msmGasUsed(numPairs, gasUsedPerPair, BLS_GAS_DISCOUNT_PAIRS_G1) if (!gasLimitCheck(opts, gasUsed, pName)) { return OOGResult(opts.gasLimit) diff --git a/packages/evm/src/precompiles/10-bls12-g2msm.ts b/packages/evm/src/precompiles/10-bls12-g2msm.ts index b327e3ca6f..b5255ee696 100644 --- a/packages/evm/src/precompiles/10-bls12-g2msm.ts +++ b/packages/evm/src/precompiles/10-bls12-g2msm.ts @@ -3,7 +3,11 @@ import { bytesToHex } from '@ethereumjs/util' import { EvmErrorResult, OOGResult } from '../evm.js' import { ERROR, EvmError } from '../exceptions.js' -import { leading16ZeroBytesCheck, msmGasUsed } from './bls12_381/index.js' +import { + BLS_GAS_DISCOUNT_PAIRS_G2, + leading16ZeroBytesCheck, + msmGasUsed, +} from './bls12_381/index.js' import { gasLimitCheck, moduloLengthCheck } from './util.js' import { getPrecompileName } from './index.js' @@ -24,7 +28,7 @@ export async function precompile10(opts: PrecompileInput): Promise { const numPairs = Math.floor(opts.data.length / 288) const gasUsedPerPair = opts.common.paramByEIP('bls12381G2MulGas', 2537) ?? BigInt(0) - const gasUsed = msmGasUsed(numPairs, gasUsedPerPair) + const gasUsed = msmGasUsed(numPairs, gasUsedPerPair, BLS_GAS_DISCOUNT_PAIRS_G2) if (!gasLimitCheck(opts, gasUsed, pName)) { return OOGResult(opts.gasLimit) diff --git a/packages/evm/src/precompiles/bls12_381/constants.ts b/packages/evm/src/precompiles/bls12_381/constants.ts index a869e7cb5b..f143bd053f 100644 --- a/packages/evm/src/precompiles/bls12_381/constants.ts +++ b/packages/evm/src/precompiles/bls12_381/constants.ts @@ -14,134 +14,265 @@ export const BLS_G2_INFINITY_POINT_BYTES = new Uint8Array(BLS_G2_POINT_BYTE_LENG export const BLS_ZERO_BUFFER = new Uint8Array(32) export const BLS_ONE_BUFFER = concatBytes(new Uint8Array(31), hexToBytes('0x01')) -// gas discount pairs taken from EIP-2537 `bls12381MultiExpGasDiscount` parameter -export const BLS_GAS_DISCOUNT_PAIRS = [ - [1, 1200], - [2, 888], - [3, 764], - [4, 641], - [5, 594], - [6, 547], - [7, 500], - [8, 453], - [9, 438], - [10, 423], - [11, 408], - [12, 394], - [13, 379], - [14, 364], - [15, 349], - [16, 334], - [17, 330], - [18, 326], - [19, 322], - [20, 318], - [21, 314], - [22, 310], - [23, 306], - [24, 302], - [25, 298], - [26, 294], - [27, 289], - [28, 285], - [29, 281], - [30, 277], - [31, 273], - [32, 269], - [33, 268], - [34, 266], - [35, 265], - [36, 263], - [37, 262], - [38, 260], - [39, 259], - [40, 257], - [41, 256], - [42, 254], - [43, 253], - [44, 251], - [45, 250], - [46, 248], - [47, 247], - [48, 245], - [49, 244], - [50, 242], - [51, 241], - [52, 239], - [53, 238], - [54, 236], - [55, 235], - [56, 233], - [57, 232], - [58, 231], - [59, 229], - [60, 228], - [61, 226], - [62, 225], - [63, 223], - [64, 222], - [65, 221], - [66, 220], - [67, 219], - [68, 219], - [69, 218], - [70, 217], - [71, 216], - [72, 216], - [73, 215], - [74, 214], - [75, 213], - [76, 213], - [77, 212], - [78, 211], - [79, 211], - [80, 210], - [81, 209], - [82, 208], - [83, 208], - [84, 207], - [85, 206], - [86, 205], - [87, 205], - [88, 204], - [89, 203], - [90, 202], - [91, 202], - [92, 201], - [93, 200], - [94, 199], - [95, 199], - [96, 198], - [97, 197], - [98, 196], - [99, 196], - [100, 195], - [101, 194], - [102, 193], - [103, 193], - [104, 192], - [105, 191], - [106, 191], - [107, 190], - [108, 189], - [109, 188], - [110, 188], - [111, 187], - [112, 186], - [113, 185], - [114, 185], - [115, 184], - [116, 183], - [117, 182], - [118, 182], - [119, 181], - [120, 180], - [121, 179], - [122, 179], - [123, 178], - [124, 177], - [125, 176], - [126, 176], - [127, 175], - [128, 174], +// G1/G2 MSM discount constants taken from EIP-2537 +export const BLS_GAS_DISCOUNT_PAIRS_G1: [number, number][] = [ + [1, 1000], + [2, 949], + [3, 848], + [4, 797], + [5, 764], + [6, 750], + [7, 738], + [8, 728], + [9, 719], + [10, 712], + [11, 705], + [12, 698], + [13, 692], + [14, 687], + [15, 682], + [16, 677], + [17, 673], + [18, 669], + [19, 665], + [20, 661], + [21, 658], + [22, 654], + [23, 651], + [24, 648], + [25, 645], + [26, 642], + [27, 640], + [28, 637], + [29, 635], + [30, 632], + [31, 630], + [32, 627], + [33, 625], + [34, 623], + [35, 621], + [36, 619], + [37, 617], + [38, 615], + [39, 613], + [40, 611], + [41, 609], + [42, 608], + [43, 606], + [44, 604], + [45, 603], + [46, 601], + [47, 599], + [48, 598], + [49, 596], + [50, 595], + [51, 593], + [52, 592], + [53, 591], + [54, 589], + [55, 588], + [56, 586], + [57, 585], + [58, 584], + [59, 582], + [60, 581], + [61, 580], + [62, 579], + [63, 577], + [64, 576], + [65, 575], + [66, 574], + [67, 573], + [68, 572], + [69, 570], + [70, 569], + [71, 568], + [72, 567], + [73, 566], + [74, 565], + [75, 564], + [76, 563], + [77, 562], + [78, 561], + [79, 560], + [80, 559], + [81, 558], + [82, 557], + [83, 556], + [84, 555], + [85, 554], + [86, 553], + [87, 552], + [88, 551], + [89, 550], + [90, 549], + [91, 548], + [92, 547], + [93, 547], + [94, 546], + [95, 545], + [96, 544], + [97, 543], + [98, 542], + [99, 541], + [100, 540], + [101, 540], + [102, 539], + [103, 538], + [104, 537], + [105, 536], + [106, 536], + [107, 535], + [108, 534], + [109, 533], + [110, 532], + [111, 532], + [112, 531], + [113, 530], + [114, 529], + [115, 528], + [116, 528], + [117, 527], + [118, 526], + [119, 525], + [120, 525], + [121, 524], + [122, 523], + [123, 522], + [124, 522], + [125, 521], + [126, 520], + [127, 520], + [128, 519], +] + +export const BLS_GAS_DISCOUNT_PAIRS_G2: [number, number][] = [ + [1, 1000], + [2, 1000], + [3, 923], + [4, 884], + [5, 855], + [6, 832], + [7, 812], + [8, 796], + [9, 782], + [10, 770], + [11, 759], + [12, 749], + [13, 740], + [14, 732], + [15, 724], + [16, 717], + [17, 711], + [18, 704], + [19, 699], + [20, 693], + [21, 688], + [22, 683], + [23, 679], + [24, 674], + [25, 670], + [26, 666], + [27, 663], + [28, 659], + [29, 655], + [30, 652], + [31, 649], + [32, 646], + [33, 643], + [34, 640], + [35, 637], + [36, 634], + [37, 632], + [38, 629], + [39, 627], + [40, 624], + [41, 622], + [42, 620], + [43, 618], + [44, 615], + [45, 613], + [46, 611], + [47, 609], + [48, 607], + [49, 606], + [50, 604], + [51, 602], + [52, 600], + [53, 598], + [54, 597], + [55, 595], + [56, 593], + [57, 592], + [58, 590], + [59, 589], + [60, 587], + [61, 586], + [62, 584], + [63, 583], + [64, 582], + [65, 580], + [66, 579], + [67, 578], + [68, 576], + [69, 575], + [70, 574], + [71, 573], + [72, 571], + [73, 570], + [74, 569], + [75, 568], + [76, 567], + [77, 566], + [78, 565], + [79, 563], + [80, 562], + [81, 561], + [82, 560], + [83, 559], + [84, 558], + [85, 557], + [86, 556], + [87, 555], + [88, 554], + [89, 553], + [90, 552], + [91, 552], + [92, 551], + [93, 550], + [94, 549], + [95, 548], + [96, 547], + [97, 546], + [98, 545], + [99, 545], + [100, 544], + [101, 543], + [102, 542], + [103, 541], + [104, 541], + [105, 540], + [106, 539], + [107, 538], + [108, 537], + [109, 537], + [110, 536], + [111, 535], + [112, 535], + [113, 534], + [114, 533], + [115, 532], + [116, 532], + [117, 531], + [118, 530], + [119, 530], + [120, 529], + [121, 528], + [122, 528], + [123, 527], + [124, 526], + [125, 526], + [126, 525], + [127, 524], + [128, 524], ] diff --git a/packages/evm/src/precompiles/bls12_381/util.ts b/packages/evm/src/precompiles/bls12_381/util.ts index d6dbb2f8ef..e4d449033d 100644 --- a/packages/evm/src/precompiles/bls12_381/util.ts +++ b/packages/evm/src/precompiles/bls12_381/util.ts @@ -1,7 +1,5 @@ import { equalsBytes } from '@ethereumjs/util' -import { BLS_GAS_DISCOUNT_PAIRS } from './constants.js' - import type { PrecompileInput } from '../types.js' const ZERO_BYTES_16 = new Uint8Array(16) @@ -14,15 +12,19 @@ const ZERO_BYTES_16 = new Uint8Array(16) * @param gasUsedPerPair * @returns */ -export const msmGasUsed = (numPairs: number, gasUsedPerPair: bigint) => { - const gasDiscountMax = BLS_GAS_DISCOUNT_PAIRS[BLS_GAS_DISCOUNT_PAIRS.length - 1][1] +export const msmGasUsed = ( + numPairs: number, + gasUsedPerPair: bigint, + discountTable: [number, number][], +) => { + const gasDiscountMax = discountTable[discountTable.length - 1][1] let gasDiscountMultiplier - if (numPairs <= BLS_GAS_DISCOUNT_PAIRS.length) { + if (numPairs <= discountTable.length) { if (numPairs === 0) { gasDiscountMultiplier = 0 // this implicitly sets gasUsed to 0 as per the EIP. } else { - gasDiscountMultiplier = BLS_GAS_DISCOUNT_PAIRS[numPairs - 1][1] + gasDiscountMultiplier = discountTable[numPairs - 1][1] } } else { gasDiscountMultiplier = gasDiscountMax From 34a4882cb24bdcafe6fc68786abbdcf3dab75cdb Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Sun, 22 Dec 2024 13:13:47 +0100 Subject: [PATCH 16/17] evm: bls update pairing gas (eips PR 9098) --- packages/evm/src/params.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/evm/src/params.ts b/packages/evm/src/params.ts index 4ebf09fb5e..03ef11aa00 100644 --- a/packages/evm/src/params.ts +++ b/packages/evm/src/params.ts @@ -238,8 +238,8 @@ export const paramsEVM: ParamsDict = { bls12381G1MulGas: 12000, // Gas cost of a single BLS12-381 G1 multiplication precompile-call bls12381G2AddGas: 600, // Gas cost of a single BLS12-381 G2 addition precompile-call bls12381G2MulGas: 22500, // Gas cost of a single BLS12-381 G2 multiplication precompile-call - bls12381PairingBaseGas: 65000, // Base gas cost of BLS12-381 pairing check - bls12381PairingPerPairGas: 43000, // Per-pair gas cost of BLS12-381 pairing check + bls12381PairingBaseGas: 37700, // Base gas cost of BLS12-381 pairing check + bls12381PairingPerPairGas: 32600, // Per-pair gas cost of BLS12-381 pairing check bls12381MapG1Gas: 5500, // Gas cost of BLS12-381 map field element to G1 bls12381MapG2Gas: 23800, // Gas cost of BLS12-381 map field element to G2 }, From 3938e7fdb43d1869206d0a68a432dd5720806253 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Sun, 22 Dec 2024 13:20:35 +0100 Subject: [PATCH 17/17] evm: rename bls files and remove g1mul, g2mul (eips PR 8945) --- .../{0d-bls12-g1msm.ts => 0c-bls12-g1msm.ts} | 2 +- .../evm/src/precompiles/0c-bls12-g1mul.ts | 55 ----------------- .../{0e-bls12-g2add.ts => 0d-bls12-g2add.ts} | 2 +- .../{10-bls12-g2msm.ts => 0e-bls12-g2msm.ts} | 2 +- .../evm/src/precompiles/0f-bls12-g2mul.ts | 59 ------------------- ...1-bls12-pairing.ts => 0f-bls12-pairing.ts} | 2 +- ...p-fp-to-g1.ts => 10-bls12-map-fp-to-g1.ts} | 2 +- ...fp2-to-g2.ts => 11-bls12-map-fp2-to-g2.ts} | 2 +- packages/evm/src/precompiles/index.ts | 46 ++++----------- 9 files changed, 18 insertions(+), 154 deletions(-) rename packages/evm/src/precompiles/{0d-bls12-g1msm.ts => 0c-bls12-g1msm.ts} (97%) delete mode 100644 packages/evm/src/precompiles/0c-bls12-g1mul.ts rename packages/evm/src/precompiles/{0e-bls12-g2add.ts => 0d-bls12-g2add.ts} (96%) rename packages/evm/src/precompiles/{10-bls12-g2msm.ts => 0e-bls12-g2msm.ts} (97%) delete mode 100644 packages/evm/src/precompiles/0f-bls12-g2mul.ts rename packages/evm/src/precompiles/{11-bls12-pairing.ts => 0f-bls12-pairing.ts} (97%) rename packages/evm/src/precompiles/{12-bls12-map-fp-to-g1.ts => 10-bls12-map-fp-to-g1.ts} (96%) rename packages/evm/src/precompiles/{13-bls12-map-fp2-to-g2.ts => 11-bls12-map-fp2-to-g2.ts} (96%) diff --git a/packages/evm/src/precompiles/0d-bls12-g1msm.ts b/packages/evm/src/precompiles/0c-bls12-g1msm.ts similarity index 97% rename from packages/evm/src/precompiles/0d-bls12-g1msm.ts rename to packages/evm/src/precompiles/0c-bls12-g1msm.ts index 5ec0e3829f..dec05e962e 100644 --- a/packages/evm/src/precompiles/0d-bls12-g1msm.ts +++ b/packages/evm/src/precompiles/0c-bls12-g1msm.ts @@ -15,7 +15,7 @@ import { getPrecompileName } from './index.js' import type { EVMBLSInterface, ExecResult } from '../types.js' import type { PrecompileInput } from './types.js' -export async function precompile0d(opts: PrecompileInput): Promise { +export async function precompile0c(opts: PrecompileInput): Promise { const pName = getPrecompileName('0d') const bls = (opts._EVM)._bls! as EVMBLSInterface diff --git a/packages/evm/src/precompiles/0c-bls12-g1mul.ts b/packages/evm/src/precompiles/0c-bls12-g1mul.ts deleted file mode 100644 index aedb566405..0000000000 --- a/packages/evm/src/precompiles/0c-bls12-g1mul.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { bytesToHex } from '@ethereumjs/util' - -import { EvmErrorResult, OOGResult } from '../evm.js' -import { ERROR, EvmError } from '../exceptions.js' - -import { leading16ZeroBytesCheck } from './bls12_381/index.js' -import { equalityLengthCheck, gasLimitCheck } from './util.js' - -import { getPrecompileName } from './index.js' - -import type { EVMBLSInterface, ExecResult } from '../types.js' -import type { PrecompileInput } from './types.js' - -export async function precompile0c(opts: PrecompileInput): Promise { - const pName = getPrecompileName('0c') - const bls = (opts._EVM)._bls! as EVMBLSInterface - - // note: the gas used is constant; even if the input is incorrect. - const gasUsed = opts.common.paramByEIP('bls12381G1MulGas', 2537) ?? BigInt(0) - if (!gasLimitCheck(opts, gasUsed, pName)) { - return OOGResult(opts.gasLimit) - } - - if (!equalityLengthCheck(opts, 160, pName)) { - return EvmErrorResult(new EvmError(ERROR.BLS_12_381_INVALID_INPUT_LENGTH), opts.gasLimit) - } - - // check if some parts of input are zero bytes. - const zeroByteRanges = [ - [0, 16], - [64, 80], - ] - if (!leading16ZeroBytesCheck(opts, zeroByteRanges, pName)) { - return EvmErrorResult(new EvmError(ERROR.BLS_12_381_POINT_NOT_ON_CURVE), opts.gasLimit) - } - - let returnValue - try { - returnValue = bls.mulG1(opts.data) - } catch (e: any) { - if (opts._debug !== undefined) { - opts._debug(`${pName} failed: ${e.message}`) - } - return EvmErrorResult(e, opts.gasLimit) - } - - if (opts._debug !== undefined) { - opts._debug(`${pName} return value=${bytesToHex(returnValue)}`) - } - - return { - executionGasUsed: gasUsed, - returnValue, - } -} diff --git a/packages/evm/src/precompiles/0e-bls12-g2add.ts b/packages/evm/src/precompiles/0d-bls12-g2add.ts similarity index 96% rename from packages/evm/src/precompiles/0e-bls12-g2add.ts rename to packages/evm/src/precompiles/0d-bls12-g2add.ts index d1bc0e4807..dbbd53a98f 100644 --- a/packages/evm/src/precompiles/0e-bls12-g2add.ts +++ b/packages/evm/src/precompiles/0d-bls12-g2add.ts @@ -11,7 +11,7 @@ import { getPrecompileName } from './index.js' import type { EVMBLSInterface, ExecResult } from '../types.js' import type { PrecompileInput } from './types.js' -export async function precompile0e(opts: PrecompileInput): Promise { +export async function precompile0d(opts: PrecompileInput): Promise { const pName = getPrecompileName('0e') const bls = (opts._EVM)._bls! as EVMBLSInterface diff --git a/packages/evm/src/precompiles/10-bls12-g2msm.ts b/packages/evm/src/precompiles/0e-bls12-g2msm.ts similarity index 97% rename from packages/evm/src/precompiles/10-bls12-g2msm.ts rename to packages/evm/src/precompiles/0e-bls12-g2msm.ts index b5255ee696..554ad6fd24 100644 --- a/packages/evm/src/precompiles/10-bls12-g2msm.ts +++ b/packages/evm/src/precompiles/0e-bls12-g2msm.ts @@ -15,7 +15,7 @@ import { getPrecompileName } from './index.js' import type { EVMBLSInterface, ExecResult } from '../types.js' import type { PrecompileInput } from './types.js' -export async function precompile10(opts: PrecompileInput): Promise { +export async function precompile0e(opts: PrecompileInput): Promise { const pName = getPrecompileName('10') const bls = (opts._EVM)._bls! as EVMBLSInterface diff --git a/packages/evm/src/precompiles/0f-bls12-g2mul.ts b/packages/evm/src/precompiles/0f-bls12-g2mul.ts deleted file mode 100644 index 3e6e16d6eb..0000000000 --- a/packages/evm/src/precompiles/0f-bls12-g2mul.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { bytesToHex } from '@ethereumjs/util' - -import { EvmErrorResult, OOGResult } from '../evm.js' -import { ERROR, EvmError } from '../exceptions.js' - -import { leading16ZeroBytesCheck } from './bls12_381/index.js' -import { equalityLengthCheck, gasLimitCheck } from './util.js' - -import { getPrecompileName } from './index.js' - -import type { EVMBLSInterface, ExecResult } from '../types.js' -import type { PrecompileInput } from './types.js' - -export async function precompile0f(opts: PrecompileInput): Promise { - const pName = getPrecompileName('0f') - const bls = (opts._EVM)._bls! as EVMBLSInterface - - // note: the gas used is constant; even if the input is incorrect. - const gasUsed = opts.common.paramByEIP('bls12381G2MulGas', 2537) ?? BigInt(0) - if (!gasLimitCheck(opts, gasUsed, pName)) { - return OOGResult(opts.gasLimit) - } - - if (!equalityLengthCheck(opts, 288, pName)) { - return EvmErrorResult(new EvmError(ERROR.BLS_12_381_INVALID_INPUT_LENGTH), opts.gasLimit) - } - - // check if some parts of input are zero bytes. - const zeroByteRanges = [ - [0, 16], - [64, 80], - [128, 144], - [192, 208], - ] - if (!leading16ZeroBytesCheck(opts, zeroByteRanges, pName)) { - return EvmErrorResult(new EvmError(ERROR.BLS_12_381_POINT_NOT_ON_CURVE), opts.gasLimit) - } - - // TODO: verify that point is on G2 - - let returnValue - try { - returnValue = bls.mulG2(opts.data) - } catch (e: any) { - if (opts._debug !== undefined) { - opts._debug(`${pName} failed: ${e.message}`) - } - return EvmErrorResult(e, opts.gasLimit) - } - - if (opts._debug !== undefined) { - opts._debug(`${pName} return value=${bytesToHex(returnValue)}`) - } - - return { - executionGasUsed: gasUsed, - returnValue, - } -} diff --git a/packages/evm/src/precompiles/11-bls12-pairing.ts b/packages/evm/src/precompiles/0f-bls12-pairing.ts similarity index 97% rename from packages/evm/src/precompiles/11-bls12-pairing.ts rename to packages/evm/src/precompiles/0f-bls12-pairing.ts index fa8a03ecc9..c8ee1df3c5 100644 --- a/packages/evm/src/precompiles/11-bls12-pairing.ts +++ b/packages/evm/src/precompiles/0f-bls12-pairing.ts @@ -11,7 +11,7 @@ import { getPrecompileName } from './index.js' import type { EVMBLSInterface, ExecResult } from '../types.js' import type { PrecompileInput } from './types.js' -export async function precompile11(opts: PrecompileInput): Promise { +export async function precompile0f(opts: PrecompileInput): Promise { const pName = getPrecompileName('11') const bls = (opts._EVM)._bls! as EVMBLSInterface diff --git a/packages/evm/src/precompiles/12-bls12-map-fp-to-g1.ts b/packages/evm/src/precompiles/10-bls12-map-fp-to-g1.ts similarity index 96% rename from packages/evm/src/precompiles/12-bls12-map-fp-to-g1.ts rename to packages/evm/src/precompiles/10-bls12-map-fp-to-g1.ts index 93381090db..79ce977ac1 100644 --- a/packages/evm/src/precompiles/12-bls12-map-fp-to-g1.ts +++ b/packages/evm/src/precompiles/10-bls12-map-fp-to-g1.ts @@ -11,7 +11,7 @@ import { getPrecompileName } from './index.js' import type { EVMBLSInterface, ExecResult } from '../types.js' import type { PrecompileInput } from './types.js' -export async function precompile12(opts: PrecompileInput): Promise { +export async function precompile10(opts: PrecompileInput): Promise { const pName = getPrecompileName('12') const bls = (opts._EVM)._bls! as EVMBLSInterface diff --git a/packages/evm/src/precompiles/13-bls12-map-fp2-to-g2.ts b/packages/evm/src/precompiles/11-bls12-map-fp2-to-g2.ts similarity index 96% rename from packages/evm/src/precompiles/13-bls12-map-fp2-to-g2.ts rename to packages/evm/src/precompiles/11-bls12-map-fp2-to-g2.ts index ff4a60f3c4..0c6882d025 100644 --- a/packages/evm/src/precompiles/13-bls12-map-fp2-to-g2.ts +++ b/packages/evm/src/precompiles/11-bls12-map-fp2-to-g2.ts @@ -11,7 +11,7 @@ import { getPrecompileName } from './index.js' import type { EVMBLSInterface, ExecResult } from '../types.js' import type { PrecompileInput } from './types.js' -export async function precompile13(opts: PrecompileInput): Promise { +export async function precompile11(opts: PrecompileInput): Promise { const pName = getPrecompileName('13') const bls = (opts._EVM)._bls! as EVMBLSInterface diff --git a/packages/evm/src/precompiles/index.ts b/packages/evm/src/precompiles/index.ts index 03fef4f1c9..ae7a44fb65 100644 --- a/packages/evm/src/precompiles/index.ts +++ b/packages/evm/src/precompiles/index.ts @@ -12,14 +12,12 @@ import { precompile08 } from './08-bn254-pairing.js' import { precompile09 } from './09-blake2f.js' import { precompile0a } from './0a-kzg-point-evaluation.js' import { precompile0b } from './0b-bls12-g1add.js' -import { precompile0c } from './0c-bls12-g1mul.js' -import { precompile0d } from './0d-bls12-g1msm.js' -import { precompile0e } from './0e-bls12-g2add.js' -import { precompile0f } from './0f-bls12-g2mul.js' -import { precompile10 } from './10-bls12-g2msm.js' -import { precompile11 } from './11-bls12-pairing.js' -import { precompile12 } from './12-bls12-map-fp-to-g1.js' -import { precompile13 } from './13-bls12-map-fp2-to-g2.js' +import { precompile0c } from './0c-bls12-g1msm.js' +import { precompile0d } from './0d-bls12-g2add.js' +import { precompile0e } from './0e-bls12-g2msm.js' +import { precompile0f } from './0f-bls12-pairing.js' +import { precompile10 } from './10-bls12-map-fp-to-g1.js' +import { precompile11 } from './11-bls12-map-fp2-to-g2.js' import { MCLBLS, NobleBLS } from './bls12_381/index.js' import { NobleBN254, RustBN254 } from './bn254/index.js' @@ -165,7 +163,7 @@ const precompileEntries: PrecompileEntry[] = [ param: 2537, }, precompile: precompile0c, - name: 'BLS12_G1MUL (0x0c)', + name: 'BLS12_G1MSM (0x0c)', }, { address: BYTES_19 + '0d', @@ -174,7 +172,7 @@ const precompileEntries: PrecompileEntry[] = [ param: 2537, }, precompile: precompile0d, - name: 'BLS12_G1MSM (0x0d)', + name: 'BLS12_G1ADD (0x0d)', }, { address: BYTES_19 + '0e', @@ -183,7 +181,7 @@ const precompileEntries: PrecompileEntry[] = [ param: 2537, }, precompile: precompile0e, - name: 'BLS12_G2ADD (0x0e)', + name: 'BLS12_G2MSM (0x0e)', }, { address: BYTES_19 + '0f', @@ -192,7 +190,7 @@ const precompileEntries: PrecompileEntry[] = [ param: 2537, }, precompile: precompile0f, - name: 'BLS12_G2MUL (0x0f)', + name: 'BLS12_PAIRING (0x0f)', }, { address: BYTES_19 + '10', @@ -201,7 +199,7 @@ const precompileEntries: PrecompileEntry[] = [ param: 2537, }, precompile: precompile10, - name: 'BLS12_G2MSM (0x10)', + name: 'BLS12_MAP_FP_TO_G1 (0x10)', }, { address: BYTES_19 + '11', @@ -210,25 +208,7 @@ const precompileEntries: PrecompileEntry[] = [ param: 2537, }, precompile: precompile11, - name: 'BLS12_PAIRING (0x11)', - }, - { - address: BYTES_19 + '12', - check: { - type: PrecompileAvailabilityCheck.EIP, - param: 2537, - }, - precompile: precompile12, - name: 'BLS12_MAP_FP_TO_G1 (0x12)', - }, - { - address: BYTES_19 + '13', - check: { - type: PrecompileAvailabilityCheck.EIP, - param: 2537, - }, - precompile: precompile13, - name: 'BLS12_MAP_FP2_TO_G2 (0x13)', + name: 'BLS12_MAP_FP_TO_G2 (0x11)', }, ] @@ -250,8 +230,6 @@ const precompiles: Precompiles = { [BYTES_19 + '0f']: precompile0f, [BYTES_19 + '10']: precompile10, [BYTES_19 + '11']: precompile11, - [BYTES_19 + '12']: precompile12, - [BYTES_19 + '13']: precompile13, } type DeletePrecompile = {