Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
8 changes: 5 additions & 3 deletions packages/evm/src/evm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,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, 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 @@ -670,7 +670,7 @@ export class EVM implements EVMInterface {
}
}

// 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,9 @@ export class EVM implements EVMInterface {
if (this.common.gteHardfork(Hardfork.Homestead)) {
if (!allowedCodeSize) {
if (this.DEBUG) {
debug(`Code size exceeds maximum code size (>= SpuriousDragon)`)
debug(
`Code size exceeds maximum code size ${this.common.param('maxCodeSize')}KB (>= ${this.common.isActivatedEIP(7907) ? 'Osaka' : 'SpuriousDragon'})`,
)
}
result = { ...result, ...CodesizeExceedsMaximumError(message.gasLimit) }
} else {
Expand Down
22 changes: 22 additions & 0 deletions packages/evm/src/journal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ export class Journal {

private journalHeight: JournalHeight

private warmedCodeAddresses!: Set<AddressString>

public accessList?: Map<AddressString, Set<SlotString>>
public preimages?: Map<PrefixedHexString, Uint8Array>

Expand Down Expand Up @@ -184,6 +186,7 @@ export class Journal {
this.alwaysWarmJournal = new Map()
this.touched = new Set()
this.journalDiff = [[0, [new Set(), new Map(), new Set()]]]
this.warmedCodeAddresses = new Set()
}

/**
Expand Down Expand Up @@ -242,6 +245,16 @@ export class Journal {
return warm
}

/**
* Returns true if the address's code is warm in the current context
* @param address - The address (as a Uint8Array) to check
*/
isWarmedCodeAddress(address: Uint8Array): boolean {
const addressHex = bytesToUnprefixedHex(address)
const warm = this.warmedCodeAddresses.has(addressHex)
return warm
}

/**
* Add a warm address in the current context
* @param addressArr - The address (as a Uint8Array) to check
Expand All @@ -260,6 +273,15 @@ export class Journal {
}
}

/**
* Add an address to the set of warmed code addresses in the current context
* @param addressArr - The address (as a Uint8Array) to check
*/
addWarmedCodeAddress(addressArr: Uint8Array): void {
const address = bytesToUnprefixedHex(addressArr)
this.warmedCodeAddresses.add(address)
}

/**
* Returns true if the slot of the address is warm
* @param address - The address (as a Uint8Array) to check
Expand Down
43 changes: 43 additions & 0 deletions packages/evm/src/opcodes/EIP7907.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { BIGINT_0 } from '@ethereumjs/util'

import type { Common } from '@ethereumjs/common'
import type { RunState } from '../interpreter.ts'
import { ceil32 } from './util.ts'

/**
* Adds address to warmedCodeAddress set if not already included.
* Adjusts cost incurred for executing opcode based on whether address code
* is warm/cold and whether the address is a large contract. (EIP 7907)
* @param {RunState} runState
* @param {Address} address
* @param {Common} common
* @param {Boolean} chargeGas (default: true)
*/
export function accessAddressCodeEIP7907(
runState: RunState,
address: Uint8Array,
common: Common,
chargeGas = true,
): bigint {
if (!common.isActivatedEIP(7907)) return BIGINT_0

// Cold
if (!runState.interpreter.journal.isWarmedCodeAddress(address)) {
runState.interpreter.journal.addWarmedCodeAddress(address)

if (chargeGas) {
// EIP 7907:
// get the large contract cost gas
const excessContractSize = BigInt(
Math.max(
0,
runState.env.contract.codeSize - Number(common.param('excessCodeSizeThreshold')),
),
)
const largeContractCost = ceil32(excessContractSize) * common.param('initCodeWordGas') // 2
return largeContractCost
}
}
// No Warm case: there is no additional large contract cost for warm code addresses
return BIGINT_0
}
61 changes: 61 additions & 0 deletions packages/evm/src/opcodes/gas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { DELEGATION_7702_FLAG } from '../types.ts'
import { updateSstoreGasEIP1283 } from './EIP1283.ts'
import { updateSstoreGasEIP2200 } from './EIP2200.ts'
import { accessAddressEIP2929, accessStorageEIP2929 } from './EIP2929.ts'
import { accessAddressCodeEIP7907 } from './EIP7907.ts'
import {
createAddressFromStackBigInt,
divCeil,
Expand Down Expand Up @@ -214,6 +215,11 @@ export const dynamicGasHandlers: Map<number, AsyncDynamicGasHandler | SyncDynami
gas += accessAddressEIP2929(runState, address.bytes, common, charge2929Gas)
}

// EXTCODECOPY loads code, so check for large contract cost
if (common.isActivatedEIP(7907)) {
gas += accessAddressCodeEIP7907(runState, address.bytes, common, true)
}

if (dataLength !== BIGINT_0) {
gas += common.param('copyGas') * divCeil(dataLength, BIGINT_32)

Expand Down Expand Up @@ -527,6 +533,16 @@ export const dynamicGasHandlers: Map<number, AsyncDynamicGasHandler | SyncDynami
)
}

// CREATE loads code, add contract to warm code addresses but don't charge gas
if (common.isActivatedEIP(7907)) {
gas += accessAddressCodeEIP7907(
runState,
runState.interpreter.getAddress().bytes,
common,
false,
)
}

if (common.isActivatedEIP(3860)) {
gas += ((length + BIGINT_31) / BIGINT_32) * common.param('initCodeWordGas')
}
Expand Down Expand Up @@ -574,6 +590,11 @@ export const dynamicGasHandlers: Map<number, AsyncDynamicGasHandler | SyncDynami
gas += accessAddressEIP2929(runState, toAddress.bytes, common, charge2929Gas)
}

// CALL loads code, so check for large contract cost
if (common.isActivatedEIP(7907)) {
gas += accessAddressCodeEIP7907(runState, toAddress.bytes, common, true)
}

if (common.isActivatedEIP(7702)) {
gas += await eip7702GasCost(runState, common, toAddress, charge2929Gas)
}
Expand Down Expand Up @@ -652,6 +673,16 @@ export const dynamicGasHandlers: Map<number, AsyncDynamicGasHandler | SyncDynami
)
}

// CALLCODE loads code, so check for large contract cost
if (common.isActivatedEIP(7907)) {
gas += accessAddressCodeEIP7907(
runState,
createAddressFromStackBigInt(toAddr).bytes,
common,
true,
)
}

if (common.isActivatedEIP(7702)) {
gas += await eip7702GasCost(runState, common, toAddress, charge2929Gas)
}
Expand Down Expand Up @@ -716,6 +747,16 @@ export const dynamicGasHandlers: Map<number, AsyncDynamicGasHandler | SyncDynami
)
}

// DELEGATECALL loads code, so check for large contract cost
if (common.isActivatedEIP(7907)) {
gas += accessAddressCodeEIP7907(
runState,
createAddressFromStackBigInt(toAddr).bytes,
common,
true,
)
}

if (common.isActivatedEIP(7702)) {
gas += await eip7702GasCost(runState, common, toAddress, charge2929Gas)
}
Expand Down Expand Up @@ -757,6 +798,16 @@ export const dynamicGasHandlers: Map<number, AsyncDynamicGasHandler | SyncDynami
)
}

// CREATE2 loads code, add contract to warm code addresses but don't charge gas
if (common.isActivatedEIP(7907)) {
gas += accessAddressCodeEIP7907(
runState,
runState.interpreter.getAddress().bytes,
common,
false,
)
}

if (common.isActivatedEIP(3860)) {
gas += ((length + BIGINT_31) / BIGINT_32) * common.param('initCodeWordGas')
}
Expand Down Expand Up @@ -929,6 +980,16 @@ export const dynamicGasHandlers: Map<number, AsyncDynamicGasHandler | SyncDynami
)
}

// STATICCALL loads code, so check for large contract cost
if (common.isActivatedEIP(7907)) {
gas += accessAddressCodeEIP7907(
runState,
createAddressFromStackBigInt(toAddr).bytes,
common,
true,
)
}

if (common.isActivatedEIP(7702)) {
gas += await eip7702GasCost(
runState,
Expand Down
7 changes: 7 additions & 0 deletions packages/evm/src/opcodes/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
BIGINT_0,
BIGINT_1,
BIGINT_2,
BIGINT_31,
BIGINT_32,
BIGINT_64,
BIGINT_160,
Expand Down Expand Up @@ -47,6 +48,12 @@ export function abs(a: bigint) {
return a * BIGINT_NEG1
}

export function ceil32(n: bigint) {
// Division with BigInt in JavaScript always performs
// integer division (i.e., it truncates toward zero)
return (n + BIGINT_31) / BIGINT_32
}

const N = BigInt(115792089237316195423570985008687907853269984665640564039457584007913129639936)
export function exponentiation(bas: bigint, exp: bigint) {
let t = BIGINT_1
Expand Down
11 changes: 11 additions & 0 deletions packages/evm/src/params.ts
Original file line number Diff line number Diff line change
Expand Up @@ -409,4 +409,15 @@ 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: {
// The threshold for the excess contract code size (prev. maxCodeSize EIP-607)
excessCodeSizeThreshold: 24576, // 24 x 1024 = 24576 bytes (0x6000)
// 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)
},
}
Loading