Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EIP-7702 devnet-3 readiness #3581

Merged
merged 41 commits into from
Sep 11, 2024
Merged
Show file tree
Hide file tree
Changes from 40 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
2f26e9d
tx: implement strict 7702 validation
jochem-brouwer Aug 12, 2024
f59b5a0
vm: update 7702 tx validation
jochem-brouwer Aug 12, 2024
5d8ba39
evm: update 7702 [no ci]
jochem-brouwer Aug 12, 2024
4a20346
tx: add / fix 7702 tests
jochem-brouwer Aug 12, 2024
5e8e756
Merge branch 'master' into 7702-update
jochem-brouwer Aug 12, 2024
ad3effb
vm: fix test encoding of authorization lists [no ci]
jochem-brouwer Aug 12, 2024
f6da2c4
Merge branch 'master' into 7702-update
jochem-brouwer Aug 13, 2024
f8950b8
vm: correctly put authority nonce
jochem-brouwer Aug 14, 2024
842db9d
vm: add test 7702 extcodehash/extcodesize
jochem-brouwer Aug 16, 2024
0b6692f
vm: expand extcode* tests 7702 [no ci]
jochem-brouwer Aug 16, 2024
6136308
Merge branch 'master' into 7702-update
jochem-brouwer Aug 17, 2024
7a01e81
tx/vm: update tests [no ci]
jochem-brouwer Aug 17, 2024
0310175
evm/vm: update opcodes and fix tests 7702
jochem-brouwer Aug 17, 2024
81d2da2
Merge branch 'master' into 7702-update
jochem-brouwer Aug 17, 2024
d773d2b
fix cspell [no ci]
jochem-brouwer Aug 17, 2024
d4d3100
vm: get params from tx for 7702 [no ci]
jochem-brouwer Aug 19, 2024
0fa00d3
vm: 7702 correctly apply the refund [no ci]
jochem-brouwer Aug 19, 2024
1a21f86
vm: 7702: correctly handle self-sponsored txs [no ci]
jochem-brouwer Aug 19, 2024
6162957
tx: throw if authorization list is empty
jochem-brouwer Aug 19, 2024
2043d1e
vm: requests do not throw if code is non-existant
jochem-brouwer Aug 19, 2024
9bd46ad
evm: ensure correct extcodehash reporting if account is delegated to …
jochem-brouwer Aug 19, 2024
a617c3b
Merge branch 'master' into 7702-update
jochem-brouwer Aug 19, 2024
d4f9c60
vm: 7702 ensure delegated accounts are not deleted [no ci]
jochem-brouwer Aug 20, 2024
d26ddb3
Merge branch 'master' into 7702-update
jochem-brouwer Aug 20, 2024
a41ef16
evm: 7702 correctly check for gas on delegated code
jochem-brouwer Aug 23, 2024
30072e2
evm: add verkle gas logic for 7702
jochem-brouwer Aug 23, 2024
33b8523
vm/tx: fix 7702 tests
jochem-brouwer Aug 25, 2024
5b4313a
tx: throw if 7702-tx has no `to` field
jochem-brouwer Aug 25, 2024
07793c7
vm/tx: fix 7702 tests
jochem-brouwer Aug 25, 2024
2f0d1ab
VM: exit early on non-existing system contracts
jochem-brouwer Aug 26, 2024
ec85a45
7702: add delegated account to warm address
jochem-brouwer Aug 27, 2024
8b0cc5c
vm: requests do restore system account
jochem-brouwer Aug 28, 2024
e710e78
7702: continue processing once auth ecrecover is invalid
jochem-brouwer Aug 28, 2024
8cc5755
evm/vm: add 7702 delegation constant
jochem-brouwer Sep 2, 2024
21f1de1
Merge branch 'master' into 7702-update
jochem-brouwer Sep 2, 2024
6325bc3
vm: fix requests
jochem-brouwer Sep 2, 2024
0d77c3f
Merge branch 'master' into 7702-update
jochem-brouwer Sep 2, 2024
4287e95
vm: unduplify 3607 error msg
jochem-brouwer Sep 2, 2024
aed93cd
Merge remote-tracking branch 'origin/master' into 7702-update
acolytec3 Sep 11, 2024
ea9f88d
Merge branch 'master' into 7702-update
acolytec3 Sep 11, 2024
88dfdd0
fix example
acolytec3 Sep 11, 2024
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
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])
7 changes: 7 additions & 0 deletions packages/tx/src/7702/tx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,13 @@ export class EOACode7702Transaction extends BaseTransaction<TransactionType.EOAC
EIP2718.validateYParity(this)
Legacy.validateHighS(this)

holgerd77 marked this conversation as resolved.
Show resolved Hide resolved
if (this.to === undefined) {
const msg = this._errorMsg(
`tx should have a "to" field and cannot be used to create contracts`,
)
throw new Error(msg)
}

const freeze = opts?.freeze ?? true
if (freeze) {
Object.freeze(this)
Expand Down
2 changes: 1 addition & 1 deletion packages/tx/src/capabilities/eip7702.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import type { EIP7702CompatibleTx } from '../types.js'
export function getDataGas(tx: EIP7702CompatibleTx): bigint {
const eip2930Cost = BigInt(AccessLists.getDataGasEIP2930(tx.accessList, tx.common))
const eip7702Cost = BigInt(
tx.authorizationList.length * Number(tx.common.param('perAuthBaseGas')),
tx.authorizationList.length * Number(tx.common.param('perEmptyAccountCost')),
)
return Legacy.getDataGas(tx, eip2930Cost + eip7702Cost)
}
9 changes: 9 additions & 0 deletions packages/tx/src/params.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,13 @@ export const paramsTx: ParamsDict = {
4844: {
blobCommitmentVersionKzg: 1, // The number indicated a versioned hash is a KZG commitment
},
/**
. * Set EOA account code for one transaction
. */
7702: {
// TODO: Set correct minimum hardfork
// gasPrices
perAuthBaseGas: 2500, // Gas cost of each authority item, provided the authority exists in the trie
perEmptyAccountCost: 25000, // Gas cost of each authority item, in case the authority does not exist in the trie
},
}
4 changes: 2 additions & 2 deletions packages/tx/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -600,7 +600,7 @@ export type AccessList = AccessListItem[]
export type AuthorizationListItem = {
chainId: PrefixedHexString
address: PrefixedHexString
nonce: PrefixedHexString[]
nonce: PrefixedHexString
yParity: PrefixedHexString
r: PrefixedHexString
s: PrefixedHexString
Expand All @@ -610,7 +610,7 @@ export type AuthorizationListItem = {
export type AuthorizationListBytesItem = [
Uint8Array,
Uint8Array,
Uint8Array[],
Uint8Array,
Uint8Array,
Uint8Array,
Uint8Array,
Expand Down
Loading
Loading