Skip to content

Commit

Permalink
Merge branch 'master' into several-evm-precompile-clean-ups
Browse files Browse the repository at this point in the history
  • Loading branch information
jochem-brouwer committed Sep 11, 2024
2 parents 483ae70 + 47051db commit 559b7e5
Show file tree
Hide file tree
Showing 47 changed files with 3,637 additions and 3,371 deletions.
42 changes: 28 additions & 14 deletions packages/evm/src/evm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,27 +30,28 @@ import { getOpcodesForHF } from './opcodes/index.js'
import { paramsEVM } from './params.js'
import { NobleBLS, getActivePrecompiles, getPrecompileName } from './precompiles/index.js'
import { TransientStorage } from './transientStorage.js'
import {
type Block,
type CustomOpcode,
DELEGATION_7702_FLAG,
type EVMBLSInterface,
type EVMBN254Interface,
type EVMEvents,
type EVMInterface,
type EVMMockBlockchainInterface,
type EVMOpts,
type EVMResult,
type EVMRunCallOpts,
type EVMRunCodeOpts,
type ExecResult,
} from './types.js'

import type { InterpreterOpts } from './interpreter.js'
import type { Timer } from './logger.js'
import type { MessageWithTo } from './message.js'
import type { AsyncDynamicGasHandler, SyncDynamicGasHandler } from './opcodes/gas.js'
import type { OpHandler, OpcodeList, OpcodeMap } from './opcodes/index.js'
import type { CustomPrecompile, PrecompileFunc } from './precompiles/index.js'
import type {
Block,
CustomOpcode,
EVMBLSInterface,
EVMBN254Interface,
EVMEvents,
EVMInterface,
EVMMockBlockchainInterface,
EVMOpts,
EVMResult,
EVMRunCallOpts,
EVMRunCodeOpts,
ExecResult,
} from './types.js'
import type { Common, StateManagerInterface } from '@ethereumjs/common'

const debug = debugDefault('evm:evm')
Expand Down Expand Up @@ -1016,6 +1017,19 @@ export class EVM implements EVMInterface {
message.isCompiled = true
} else {
message.code = await this.stateManager.getCode(message.codeAddress)

// EIP-7702 delegation check
if (
this.common.isActivatedEIP(7702) &&
equalsBytes(message.code.slice(0, 3), DELEGATION_7702_FLAG)
) {
const address = new Address(message.code.slice(3, 24))
message.code = await this.stateManager.getCode(address)
if (message.depth === 0) {
this.journal.addAlwaysWarmAddress(address.toString())
}
}

message.isCompiled = false
message.chargeCodeAccesses = true
}
Expand Down
51 changes: 43 additions & 8 deletions packages/evm/src/opcodes/functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,14 @@ import {
getVerkleTreeIndicesForStorageSlot,
setLengthLeft,
} from '@ethereumjs/util'
import { equalBytes } from '@noble/curves/abstract/utils'
import { keccak256 } from 'ethereum-cryptography/keccak.js'

import { EOFContainer, EOFContainerMode } from '../eof/container.js'
import { EOFError } from '../eof/errors.js'
import { EOFBYTES, EOFHASH, isEOF } from '../eof/util.js'
import { ERROR } from '../exceptions.js'
import { DELEGATION_7702_FLAG } from '../types.js'

import {
createAddressFromStackBigInt,
Expand Down Expand Up @@ -59,6 +61,21 @@ export interface AsyncOpHandler {

export type OpHandler = SyncOpHandler | AsyncOpHandler

function getEIP7702DelegatedAddress(code: Uint8Array) {
if (equalBytes(code.slice(0, 3), DELEGATION_7702_FLAG)) {
return new Address(code.slice(3, 24))
}
}

async function eip7702CodeCheck(runState: RunState, code: Uint8Array) {
const address = getEIP7702DelegatedAddress(code)
if (address !== undefined) {
return runState.stateManager.getCode(address)
}

return code
}

// the opcode functions
export const handlers: Map<number, OpHandler> = new Map([
// 0x00: STOP
Expand Down Expand Up @@ -511,36 +528,39 @@ export const handlers: Map<number, OpHandler> = new Map([
// 0x3b: EXTCODESIZE
[
0x3b,
async function (runState) {
async function (runState, common) {
const addressBigInt = runState.stack.pop()
const address = createAddressFromStackBigInt(addressBigInt)
// EOF check
const code = await runState.stateManager.getCode(address)
let code = await runState.stateManager.getCode(address)
if (isEOF(code)) {
// In legacy code, the target code is treated as to be "EOFBYTES" code
runState.stack.push(BigInt(EOFBYTES.length))
return
} else if (common.isActivatedEIP(7702)) {
code = await eip7702CodeCheck(runState, code)
}

const size = BigInt(
await runState.stateManager.getCodeSize(createAddressFromStackBigInt(addressBigInt)),
)
const size = BigInt(code.length)

runState.stack.push(size)
},
],
// 0x3c: EXTCODECOPY
[
0x3c,
async function (runState) {
async function (runState, common) {
const [addressBigInt, memOffset, codeOffset, dataLength] = runState.stack.popN(4)

if (dataLength !== BIGINT_0) {
let code = await runState.stateManager.getCode(createAddressFromStackBigInt(addressBigInt))
const address = createAddressFromStackBigInt(addressBigInt)
let code = await runState.stateManager.getCode(address)

if (isEOF(code)) {
// 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)
}

const data = getDataSlice(code, codeOffset, dataLength)
Expand All @@ -553,7 +573,7 @@ export const handlers: Map<number, OpHandler> = new Map([
// 0x3f: EXTCODEHASH
[
0x3f,
async function (runState) {
async function (runState, common) {
const addressBigInt = runState.stack.pop()
const address = createAddressFromStackBigInt(addressBigInt)

Expand All @@ -564,6 +584,21 @@ export const handlers: Map<number, OpHandler> = new Map([
// Therefore, push the hash of EOFBYTES to the stack
runState.stack.push(bytesToBigInt(EOFHASH))
return
} 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)))
return
} else {
runState.stack.push(bytesToBigInt(keccak256(code)))
return
}
}

const account = await runState.stateManager.getAccount(address)
Expand Down
49 changes: 49 additions & 0 deletions packages/evm/src/opcodes/gas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@ import {
VERKLE_BASIC_DATA_LEAF_KEY,
VERKLE_CODE_HASH_LEAF_KEY,
bigIntToBytes,
equalsBytes,
getVerkleTreeIndicesForStorageSlot,
setLengthLeft,
} from '@ethereumjs/util'

import { EOFError } from '../eof/errors.js'
import { ERROR } from '../exceptions.js'
import { DELEGATION_7702_FLAG } from '../types.js'

import { updateSstoreGasEIP1283 } from './EIP1283.js'
import { updateSstoreGasEIP2200 } from './EIP2200.js'
Expand All @@ -31,9 +33,23 @@ import {

import type { RunState } from '../interpreter.js'
import type { Common } from '@ethereumjs/common'
import type { Address } from '@ethereumjs/util'

const EXTCALL_TARGET_MAX = BigInt(2) ** BigInt(8 * 20) - BigInt(1)

async function eip7702GasCost(
runState: RunState,
common: Common,
address: Address,
charge2929Gas: boolean,
) {
const code = await runState.stateManager.getCode(address)
if (equalsBytes(code.slice(0, 3), DELEGATION_7702_FLAG)) {
return accessAddressEIP2929(runState, code.slice(3, 24), common, charge2929Gas)
}
return BIGINT_0
}

/**
* This file returns the dynamic parts of opcodes which have dynamic gas
* These are not pure functions: some edit the size of the memory
Expand Down Expand Up @@ -175,6 +191,10 @@ export const dynamicGasHandlers: Map<number, AsyncDynamicGasHandler | SyncDynami
gas += accessAddressEIP2929(runState, address.bytes, common, charge2929Gas)
}

if (common.isActivatedEIP(7702)) {
gas += await eip7702GasCost(runState, common, address, charge2929Gas)
}

return gas
},
],
Expand Down Expand Up @@ -208,6 +228,10 @@ export const dynamicGasHandlers: Map<number, AsyncDynamicGasHandler | SyncDynami
gas += accessAddressEIP2929(runState, address.bytes, common, charge2929Gas)
}

if (common.isActivatedEIP(7702)) {
gas += await eip7702GasCost(runState, common, address, charge2929Gas)
}

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

Expand Down Expand Up @@ -273,6 +297,10 @@ export const dynamicGasHandlers: Map<number, AsyncDynamicGasHandler | SyncDynami
gas += accessAddressEIP2929(runState, address.bytes, common, charge2929Gas)
}

if (common.isActivatedEIP(7702)) {
gas += await eip7702GasCost(runState, common, address, charge2929Gas)
}

return gas
},
],
Expand Down Expand Up @@ -573,6 +601,10 @@ export const dynamicGasHandlers: Map<number, AsyncDynamicGasHandler | SyncDynami
gas += accessAddressEIP2929(runState, toAddress.bytes, common, charge2929Gas)
}

if (common.isActivatedEIP(7702)) {
gas += await eip7702GasCost(runState, common, toAddress, charge2929Gas)
}

if (value !== BIGINT_0 && !common.isActivatedEIP(6800)) {
gas += common.param('callValueTransferGas')
}
Expand Down Expand Up @@ -647,6 +679,10 @@ export const dynamicGasHandlers: Map<number, AsyncDynamicGasHandler | SyncDynami
)
}

if (common.isActivatedEIP(7702)) {
gas += await eip7702GasCost(runState, common, toAddress, charge2929Gas)
}

if (value !== BIGINT_0) {
gas += common.param('callValueTransferGas')
}
Expand Down Expand Up @@ -708,6 +744,10 @@ export const dynamicGasHandlers: Map<number, AsyncDynamicGasHandler | SyncDynami
)
}

if (common.isActivatedEIP(7702)) {
gas += await eip7702GasCost(runState, common, toAddress, charge2929Gas)
}

const gasLimit = maxCallGas(
currentGasLimit,
runState.interpreter.getGasLeft() - gas,
Expand Down Expand Up @@ -907,6 +947,15 @@ export const dynamicGasHandlers: Map<number, AsyncDynamicGasHandler | SyncDynami
)
}

if (common.isActivatedEIP(7702)) {
gas += await eip7702GasCost(
runState,
common,
createAddressFromStackBigInt(toAddr),
charge2929Gas,
)
}

const gasLimit = maxCallGas(
currentGasLimit,
runState.interpreter.getGasLeft() - gas,
Expand Down
8 changes: 0 additions & 8 deletions packages/evm/src/params.ts
Original file line number Diff line number Diff line change
Expand Up @@ -409,12 +409,4 @@ export const paramsEVM: ParamsDict = {
eofcreateGas: 32000, // Base fee of the EOFCREATE opcode (Same as CREATE/CREATE2)
returncontractGas: 0, // Base fee of the RETURNCONTRACT opcode
},
/**
. * Set EOA account code for one transaction
. */
7702: {
// TODO: Set correct minimum hardfork
// gasPrices
perAuthBaseGas: 2500, // Gas cost of each authority item
},
}
3 changes: 3 additions & 0 deletions packages/evm/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -507,3 +507,6 @@ export type EOFEnv = {
returnStack: number[]
}
}

// EIP-7702 flag: if contract code starts with these 3 bytes, it is a 7702-delegated EOA
export const DELEGATION_7702_FLAG = new Uint8Array([0xef, 0x01, 0x00])
Loading

0 comments on commit 559b7e5

Please sign in to comment.