Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions packages/common/src/eips.ts
Original file line number Diff line number Diff line change
Expand Up @@ -478,4 +478,12 @@ export const eipsDict: EIPsDict = {
7864: {
minimumHardfork: Hardfork.London,
},
/**
* Description : Meter Contract Code Size And Increase Limit
* URL : https://eips.ethereum.org/EIPS/eip-7907
* Status : Draft
*/
7907: {
minimumHardfork: Hardfork.Osaka,
},
}
4 changes: 2 additions & 2 deletions packages/common/src/hardforks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,11 +162,11 @@ export const hardforksDict: HardforksDict = {
},
/**
* Description: Next feature hardfork after prague, internally used for peerdas/EOF testing/implementation (incomplete/experimental)
* URL : https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/osaka.md
* URL : https://eips.ethereum.org/EIPS/eip-7607
* Status : Final
*/
osaka: {
eips: [663, 3540, 3670, 4200, 4750, 5450, 6206, 7069, 7480, 7620, 7692, 7698],
eips: [663, 3540, 3670, 4200, 4750, 5450, 6206, 7069, 7480, 7620, 7692, 7698, 7907],
},
/**
* Description: Next feature hardfork after osaka, internally used for verkle testing/implementation (incomplete/experimental)
Expand Down
16 changes: 12 additions & 4 deletions packages/evm/src/evm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@
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, 7691, 7692, 7698, 7702, 7709,
7002, 7069, 7251, 7480, 7516, 7620, 7685, 7691, 7692, 7698, 7702, 7709, 7907,
]

for (const eip of this.common.eips()) {
Expand Down Expand Up @@ -500,7 +500,7 @@
// Reduce tx value from sender
await this._reduceSenderBalance(account, message)

if (this.common.isActivatedEIP(3860)) {
if (this.common.isActivatedEIP(3860) || this.common.isActivatedEIP(7907)) {
if (
message.data.length > Number(this.common.param('maxInitCodeSize')) &&
!this.allowUnlimitedInitCodeSize
Expand Down Expand Up @@ -670,7 +670,7 @@
}
}

// Check for SpuriousDragon EIP-170 code size limit
// Check for SpuriousDragon EIP-170 or Osaka EIP-7907 code size limit
let allowedCodeSize = true
if (
!result.exceptionError &&
Expand Down Expand Up @@ -707,7 +707,15 @@
if (this.common.gteHardfork(Hardfork.Homestead)) {
if (!allowedCodeSize) {
if (this.DEBUG) {
debug(`Code size exceeds maximum code size (>= SpuriousDragon)`)
if (this.common.isActivatedEIP(7907)) {
debug(
`Code size exceeds maximum code size ${this.common.param('maxCodeSize')}KB (>= Osaka)`,
)

Check warning on line 713 in packages/evm/src/evm.ts

View check run for this annotation

Codecov / codecov/patch

packages/evm/src/evm.ts#L711-L713

Added lines #L711 - L713 were not covered by tests
} else {
debug(
`Code size exceeds maximum code size ${this.common.param('maxCodeSize')}KB (>= SpuriousDragon)`,
)
}
}
result = { ...result, ...CodesizeExceedsMaximumError(message.gasLimit) }
} else {
Expand Down
2 changes: 1 addition & 1 deletion packages/evm/src/interpreter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1123,7 +1123,7 @@ export class Interpreter {
this._env.contract.nonce += BIGINT_1
await this.journal.putAccount(this._env.address, this._env.contract)

if (this.common.isActivatedEIP(3860)) {
if (this.common.isActivatedEIP(3860) || this.common.isActivatedEIP(7907)) {
if (
codeToRun.length > Number(this.common.param('maxInitCodeSize')) &&
this._evm.allowUnlimitedInitCodeSize === false
Expand Down
4 changes: 2 additions & 2 deletions packages/evm/src/opcodes/functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1340,7 +1340,7 @@ export const handlers: Map<number, OpHandler> = new Map([
const [value, offset, length] = runState.stack.popN(3)

if (
common.isActivatedEIP(3860) &&
(common.isActivatedEIP(3860) || common.isActivatedEIP(7907)) &&
length > Number(common.param('maxInitCodeSize')) &&
!runState.interpreter._evm.allowUnlimitedInitCodeSize
) {
Expand Down Expand Up @@ -1376,7 +1376,7 @@ export const handlers: Map<number, OpHandler> = new Map([
const [value, offset, length, salt] = runState.stack.popN(4)

if (
common.isActivatedEIP(3860) &&
(common.isActivatedEIP(3860) || common.isActivatedEIP(7907)) &&
length > Number(common.param('maxInitCodeSize')) &&
!runState.interpreter._evm.allowUnlimitedInitCodeSize
) {
Expand Down
9 changes: 9 additions & 0 deletions packages/evm/src/params.ts
Original file line number Diff line number Diff line change
Expand Up @@ -409,4 +409,13 @@ export const paramsEVM: ParamsDict = {
eofcreateGas: 32000, // Base fee of the EOFCREATE opcode (Same as CREATE/CREATE2)
returncontractGas: 0, // Base fee of the RETURNCONTRACT opcode
},
/**
* Meter Contract Code Size And Increase Limit
*/
7907: {
// Maximum length of contract code
maxCodeSize: 262144, // 256 × 1024 = 262,144 bytes (0x40000)
// Maximum length of initialization code when creating a contract
maxInitCodeSize: 524288, // 512 × 1024 = 524,288 bytes (0x80000)
},
}
282 changes: 282 additions & 0 deletions packages/evm/test/eips/eip-7907-initcode.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,282 @@
import { Common, Hardfork, Mainnet } from '@ethereumjs/common'
import {
Address,
concatBytes,
// createAddressFromString,
// equalsBytes,
hexToBytes,
privateToAddress,
} from '@ethereumjs/util'
import { assert, describe, it } from 'vitest'

import { createEVM } from '../../src/index.ts'

const pkey = hexToBytes(`0x${'20'.repeat(32)}`)
const sender = new Address(privateToAddress(pkey))

describe('EIP 7907 initcode size tests', () => {
it('code 512KB exceeds max initcode size', async () => {
const common = new Common({
chain: Mainnet,
hardfork: Hardfork.Osaka,
eips: [7907],
})
const evm = await createEVM({
common,
})

// 524,288 (512KB) length, 8 bit array, filled
const buffer = new Uint8Array(524288).fill(0x60)

// setup the call arguments
const runCallArgs = {
sender, // call address
gasLimit: BigInt(0xffffffffff), // ensure we pass a lot of gas, so we do not run out of gas
// Simple test, PUSH <big number> PUSH 0 RETURN
// It tries to deploy a contract too large, where the code is all zeros
// (since memory which is not allocated/resized to yet is always defaulted to 0)
data: concatBytes(
hexToBytes(
'0x7F6000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060005260206000F3',
),
buffer,
),
}
const result = await evm.runCall(runCallArgs)
assert.isTrue(
(result.execResult.exceptionError?.error as string) === 'initcode exceeds max initcode size',
'initcode exceeds max size',
)
})

it('code 512KB - 100 bytes does not exceed max initcode size', async () => {
const common = new Common({
chain: Mainnet,
hardfork: Hardfork.Osaka,
eips: [7907],
})
const evm = await createEVM({
common,
})

// 524288 - 100("512K-100") length, 8 bit array, filled
const buffer = new Uint8Array(524188).fill(0x60)

// setup the call arguments
const runCallArgs = {
sender, // call address
gasLimit: BigInt(0xffffffffff), // ensure we pass a lot of gas, so we do not run out of gas
// Simple test, PUSH <big number> PUSH 0 RETURN
// It tries to deploy a contract too large, where the code is all zeros
// (since memory which is not allocated/resized to yet is always defaulted to 0)
data: concatBytes(
hexToBytes(
'0x7F6000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060005260206000F3',
),
buffer,
),
}
const result = await evm.runCall(runCallArgs)
assert.isTrue(
result.execResult.exceptionError === undefined,
'successfully created a contract with data size 250KB (> 24KB and < 256KB)',
)
})

// it('ensure EIP-7907 gas is applied on CREATE calls', async () => {
// // Transaction/Contract data taken from https://github.com/ethereum/tests/pull/990
// const commonWith7907 = new Common({
// chain: Mainnet,
// hardfork: Hardfork.Osaka,
// eips: [7907],
// })
// const commonWithout7907 = new Common({
// chain: Mainnet,
// hardfork: Hardfork.Osaka,
// eips: [],
// })
// const caller = createAddressFromString('0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b')
// const evm = await createEVM({
// common: commonWith7907,
// })
// const evmWithout7907 = await createEVM({
// common: commonWithout7907,
// })
// const contractFactory = createAddressFromString('0xb94f5374fce5edbc8e2a8697c15331677e6ebf0b')
// const contractAccount = await evm.stateManager.getAccount(contractFactory)
// await evm.stateManager.putAccount(contractFactory, contractAccount!)
// await evmWithout7907.stateManager.putAccount(contractFactory, contractAccount!)
// const factoryCode = hexToBytes(
// '0x7f600a80600080396000f3000000000000000000000000000000000000000000006000526000355a8160006000f05a8203600a55806000556001600155505050',
// )

// await evm.stateManager.putCode(contractFactory, factoryCode)
// await evmWithout7907.stateManager.putCode(contractFactory, factoryCode)
// const data = hexToBytes('0x000000000000000000000000000000000000000000000000000000000000c000')
// const runCallArgs = {
// from: caller,
// to: contractFactory,
// data,
// gasLimit: BigInt(0xfffffffff),
// }
// const res = await evm.runCall(runCallArgs)
// const res2 = await evmWithout7907.runCall(runCallArgs)
// assert.isTrue(
// res.execResult.executionGasUsed > res2.execResult.executionGasUsed,
// 'execution gas used is higher with EIP 7907 active',
// )
// })

// it('ensure EIP-7907 gas is applied on CREATE2 calls', async () => {
// // Transaction/Contract data taken from https://github.com/ethereum/tests/pull/990
// const commonWith7907 = new Common({
// chain: Mainnet,
// hardfork: Hardfork.Osaka,
// eips: [7907],
// })
// const commonWithout7907 = new Common({
// chain: Mainnet,
// hardfork: Hardfork.Osaka,
// eips: [],
// })
// const caller = createAddressFromString('0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b')
// const evm = await createEVM({
// common: commonWith7907,
// })
// const evmWithout7907 = await createEVM({
// common: commonWithout7907,
// })
// const contractFactory = createAddressFromString('0xb94f5374fce5edbc8e2a8697c15331677e6ebf0b')
// const contractAccount = await evm.stateManager.getAccount(contractFactory)
// await evm.stateManager.putAccount(contractFactory, contractAccount!)
// await evmWithout7907.stateManager.putAccount(contractFactory, contractAccount!)
// const factoryCode = hexToBytes(
// '0x7f600a80600080396000f3000000000000000000000000000000000000000000006000526000355a60008260006000f55a8203600a55806000556001600155505050',
// )

// await evm.stateManager.putCode(contractFactory, factoryCode)
// await evmWithout7907.stateManager.putCode(contractFactory, factoryCode)
// const data = hexToBytes('0x000000000000000000000000000000000000000000000000000000000000c000')
// const runCallArgs = {
// from: caller,
// to: contractFactory,
// data,
// gasLimit: BigInt(0xfffffffff),
// }
// const res = await evm.runCall(runCallArgs)
// const res2 = await evmWithout7907.runCall(runCallArgs)
// assert.isTrue(
// res.execResult.executionGasUsed > res2.execResult.executionGasUsed,
// 'execution gas used is higher with EIP 7907 active',
// )
// })

it('code exceeds max initcode size: allowUnlimitedInitCodeSize active', async () => {
const common = new Common({
chain: Mainnet,
hardfork: Hardfork.Osaka,
eips: [7907],
})
const evm = await createEVM({
common,
allowUnlimitedInitCodeSize: true,
})

const bytes = new Uint8Array(1000000).fill(0x60)

// setup the call arguments
const runCallArgs = {
sender, // call address
gasLimit: BigInt(0xffffffffff), // ensure we pass a lot of gas, so we do not run out of gas
// Simple test, PUSH <big number> PUSH 0 RETURN
// It tries to deploy a contract too large, where the code is all zeros
// (since memory which is not allocated/resized to yet is always defaulted to 0)
data: concatBytes(
hexToBytes(`0x${'00'.repeat(Number(common.param('maxInitCodeSize')) + 1)}`),
bytes,
),
}
const result = await evm.runCall(runCallArgs)
assert.isTrue(
result.execResult.exceptionError === undefined,
'successfully created a contract with data size > MAX_INITCODE_SIZE and allowUnlimitedInitCodeSize active',
)
})

// it('CREATE with MAX_INITCODE_SIZE+1, allowUnlimitedContractSize active', async () => {
// const commonWith7907 = new Common({
// chain: Mainnet,
// hardfork: Hardfork.Osaka,
// eips: [7907],
// })
// const caller = createAddressFromString('0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b')
// for (const code of ['F0', 'F5']) {
// const evm = await createEVM({
// common: commonWith7907,

// allowUnlimitedInitCodeSize: true,
// })
// const evmDisabled = await createEVM({
// common: commonWith7907,
// allowUnlimitedInitCodeSize: false,
// })
// const contractFactory = createAddressFromString('0xb94f5374fce5edbc8e2a8697c15331677e6ebf0b')
// const contractAccount = await evm.stateManager.getAccount(contractFactory)
// await evm.stateManager.putAccount(contractFactory, contractAccount!)
// await evmDisabled.stateManager.putAccount(contractFactory, contractAccount!)
// // This factory code:
// // -> reads 32 bytes from the calldata (X)
// // Attempts to create a contract of X size
// // (the initcode of this contract is just zeros, so STOP opcode
// // It stores the topmost stack item of this CREATE(2) at slot 0
// // This is either the contract address if it was successful, or 0 in case of error
// const factoryCode = hexToBytes(`0x600060003560006000${code}600055`)

// await evm.stateManager.putCode(contractFactory, factoryCode)
// await evmDisabled.stateManager.putCode(contractFactory, factoryCode)

// const runCallArgs = {
// from: caller,
// to: contractFactory,
// gasLimit: BigInt(0xfffffffff),
// data: hexToBytes(`0x${'00'.repeat(30)}C001`),
// }

// const res = await evm.runCall(runCallArgs)
// await evmDisabled.runCall(runCallArgs)

// const key0 = hexToBytes(`0x${'00'.repeat(32)}`)
// const storageActive = await evm.stateManager.getStorage(contractFactory, key0)
// const storageInactive = await evmDisabled.stateManager.getStorage(contractFactory, key0)

// assert.isTrue(
// !equalsBytes(storageActive, new Uint8Array()),
// 'created contract with MAX_INITCODE_SIZE + 1 length, allowUnlimitedInitCodeSize=true',
// )
// assert.isTrue(
// equalsBytes(storageInactive, new Uint8Array()),
// 'did not create contract with MAX_INITCODE_SIZE + 1 length, allowUnlimitedInitCodeSize=false',
// )

// // gas check

// const runCallArgs2 = {
// from: caller,
// to: contractFactory,
// gasLimit: BigInt(0xfffffffff),
// data: hexToBytes(`0x${'00'.repeat(30)}C000`),
// }

// // Test:
// // On the `allowUnlimitedInitCodeSize = true`, create contract with MAX_INITCODE_SIZE + 1
// // On `allowUnlimitedInitCodeSize = false`, create contract with MAX_INITCODE_SIZE
// // Verify that the gas cost on the prior one is higher than the first one
// const res2 = await evmDisabled.runCall(runCallArgs2)

// assert.isTrue(
// res.execResult.executionGasUsed > res2.execResult.executionGasUsed,
// 'charged initcode analysis gas cost on both allowUnlimitedCodeSize=true, allowUnlimitedInitCodeSize=false',
// )
// }
// })
})
Loading
Loading