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

Add tracing to t8n #3953

Open
wants to merge 46 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
504a7bd
Add json traces to t8n tool support
acolytec3 Mar 31, 2025
45d3cdc
Update trace output to be one step per line
acolytec3 Mar 31, 2025
d577e67
move tracing to correct spot
acolytec3 Apr 1, 2025
ecbe65d
address feedback
acolytec3 Apr 1, 2025
7263442
Clean up traces
acolytec3 Apr 1, 2025
a357fb6
Cache pre-gas computation memory size for 3155
acolytec3 Apr 1, 2025
fed9ce1
Add jsonifyTrace helper and test
acolytec3 Apr 1, 2025
ab2f22c
more fixes
acolytec3 Apr 1, 2025
d87e42d
Merge remote-tracking branch 'origin/master' into add-tracing-to-t8n
acolytec3 Apr 2, 2025
bc88658
Have t8n use typescript where available
acolytec3 Apr 2, 2025
87afde8
Partially fix gas calculation
acolytec3 Apr 2, 2025
9fc4d27
Implicitly call STOP if end of bytecode is reached
acolytec3 Apr 3, 2025
410e7a0
Merge remote-tracking branch 'origin/master' into add-tracing-to-t8n
acolytec3 Apr 3, 2025
2ee5d97
spellcheck
acolytec3 Apr 3, 2025
91bc624
fix tests and opcode function lookup logic
acolytec3 Apr 3, 2025
571a4f9
more fixes
acolytec3 Apr 3, 2025
8bd5ae6
address feedback
acolytec3 Apr 3, 2025
c52404f
fix tests
acolytec3 Apr 3, 2025
e88bca7
fix test
acolytec3 Apr 3, 2025
2d977d5
Remove unneeded test data
acolytec3 Apr 3, 2025
29e4313
where is the outlog?
acolytec3 Apr 3, 2025
4adfdb5
REmove extra slash
acolytec3 Apr 3, 2025
62b336f
lint
acolytec3 Apr 3, 2025
238f6cc
t8ntool: fix test
jochem-brouwer Apr 3, 2025
db31d14
remove Nify from whitelist
jochem-brouwer Apr 3, 2025
f6572c0
client: remove console.log in test
jochem-brouwer Apr 3, 2025
d797a92
evm/vm: lowercase stepTraceJSON
jochem-brouwer Apr 3, 2025
30923fa
Move helpers to vm. Update tests. Add eip7756 formatted fields
acolytec3 Apr 4, 2025
d09eeff
Add test
acolytec3 Apr 4, 2025
ee28d8c
Comment bytecode
acolytec3 Apr 4, 2025
1e76835
Revert changes related to adding STOP code
acolytec3 Apr 4, 2025
5ae3eff
spellcheck
acolytec3 Apr 4, 2025
5982f97
Add remaining fields for eip 7756
acolytec3 Apr 7, 2025
fb0f72c
spellcheck
acolytec3 Apr 7, 2025
11247a6
fix functionDepth reference
acolytec3 Apr 7, 2025
90c4cba
update comments
acolytec3 Apr 7, 2025
5819a48
Merge remote-tracking branch 'origin/master' into add-tracing-to-t8n
acolytec3 Apr 7, 2025
4e51407
Add logic to track storage in step hook
acolytec3 Apr 8, 2025
1defe8b
Merge remote-tracking branch 'origin/master' into add-tracing-to-t8n
acolytec3 Apr 8, 2025
a3c0b12
memory is not optional
acolytec3 Apr 8, 2025
8c83887
pad keys to 32 bytes for storage
acolytec3 Apr 8, 2025
23010e0
Merge branch 'master' into add-tracing-to-t8n
acolytec3 Apr 8, 2025
fdd6f7a
Merge remote-tracking branch 'origin/master' into add-tracing-to-t8n
acolytec3 Apr 10, 2025
810990f
Merge remote-tracking branch 'origin/master' into add-tracing-to-t8n
acolytec3 Apr 10, 2025
e2ef053
Address feedback
acolytec3 Apr 10, 2025
cc48f0c
Simplify immediates computation
acolytec3 Apr 11, 2025
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
23 changes: 22 additions & 1 deletion packages/evm/src/eof/container.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { EthereumJSErrorWithoutCode } from '@ethereumjs/util'
import { EthereumJSError, EthereumJSErrorWithoutCode } from '@ethereumjs/util'

Check failure on line 1 in packages/evm/src/eof/container.ts

View workflow job for this annotation

GitHub Actions / lint / lint

'EthereumJSError' is defined but never used. Allowed unused vars must match /^_/u

import {
CODE_MIN,
Expand Down Expand Up @@ -269,6 +269,27 @@
}
return offset
}

// Returns the code section for a given program counter position
getSectionFromProgramCounter(programCounter: number) {
if (
programCounter < 0 ||
programCounter >
this.codeStartPos[this.codeStartPos.lastIndex] + this.codeSizes[this.codeSizes.lastIndex]
) {
// If code position is outside the beginning or end of the code sections, return 0
throw EthereumJSErrorWithoutCode('program counter out of bounds')
}

for (let i = 0; i < this.codeSizes.length; i++) {
if (programCounter < this.codeStartPos[i] + this.codeSizes[i]) {
// We've found our section if the code position is less than the end of the current code section
return i
}
}
// This shouldn't happen so just error
throw EthereumJSErrorWithoutCode(`Invalid program counter value: ${programCounter}`)
}
}

export interface TypeSection {
Expand Down
86 changes: 74 additions & 12 deletions packages/evm/src/interpreter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@
BIGINT_2,
EthereumJSErrorWithoutCode,
MAX_UINT64,
bigIntToBytes,
bigIntToHex,
bytesToBigInt,
bytesToHex,
equalsBytes,
setLengthLeft,
setLengthRight,
} from '@ethereumjs/util'
import debugDefault from 'debug'
Expand All @@ -32,6 +34,7 @@
VerkleAccessWitnessInterface,
} from '@ethereumjs/common'
import type { Address, PrefixedHexString } from '@ethereumjs/util'
import { stackDelta } from './eof/stackDelta.ts'
import type { EVM } from './evm.ts'
import type { Journal } from './journal.ts'
import type { AsyncOpHandler, Opcode, OpcodeMapEntry } from './opcodes/index.ts'
Expand Down Expand Up @@ -108,6 +111,7 @@
gasRefund: bigint // Tracks the current refund
gasLeft: bigint // Current gas left
returnBytes: Uint8Array /* Current bytes in the return Uint8Array. Cleared each time a CALL/CREATE is made in the current frame. */
accessedStorage: Map<PrefixedHexString, PrefixedHexString>
}

export interface InterpreterResult {
Expand All @@ -127,12 +131,18 @@
fee: number
dynamicFee?: bigint
isAsync: boolean
code: number // The hexadecimal representation of the opcode (e.g. 0x60 for PUSH1)
}
account: Account
address: Address
memory: Uint8Array
memoryWordCount: bigint
codeAddress: Address
eofSection?: number // Current EOF section being executed
immediate?: Uint8Array // Immediate argument of the opcode
eofFunctionDepth?: number // Depth of CALLF return stack
error?: Uint8Array // Error bytes returned if revert occurs
storage?: [PrefixedHexString, PrefixedHexString][]
}

/**
Expand Down Expand Up @@ -198,6 +208,7 @@
gasRefund: env.gasRefund,
gasLeft,
returnBytes: new Uint8Array(0),
accessedStorage: new Map(), // Maps accessed storage keys to their values (i.e. SSTOREd and SLOADed values)
}
this.journal = journal
this._env = env
Expand Down Expand Up @@ -283,7 +294,7 @@
while (this._runState.programCounter < this._runState.code.length) {
const programCounter = this._runState.programCounter
let opCode: number
let opCodeObj: OpcodeMapEntry
let opCodeObj: OpcodeMapEntry | undefined
if (doJumpAnalysis) {
opCode = this._runState.code[programCounter]
// Only run the jump destination analysis if `code` actually contains a JUMP/JUMPI/JUMPSUB opcode
Expand Down Expand Up @@ -319,13 +330,13 @@
}
}

this._runState.opCode = opCode!
this._runState.opCode = opCode

try {
if (overheadTimer !== undefined) {
this.performanceLogger.pauseTimer()
}
await this.runStep(opCodeObj!)
await this.runStep(opCodeObj)
if (overheadTimer !== undefined) {
this.performanceLogger.unpauseTimer(overheadTimer)
}
Expand Down Expand Up @@ -374,6 +385,9 @@

let gas = opInfo.feeBigInt

// Cache pre-gas memory size if doing tracing (EIP-7756)
const memorySizeCache = this._runState.memoryWordCount

try {
if (opInfo.dynamicGas) {
// This function updates the gas in-place.
Expand All @@ -384,7 +398,7 @@
if (this._evm.events.listenerCount('step') > 0 || this._evm.DEBUG) {
// Only run this stepHook function if there is an event listener (e.g. test runner)
// or if the vm is running in debug mode (to display opcode debug logs)
await this._runStepHook(gas, this.getGasLeft())
await this._runStepHook(gas, this.getGasLeft(), memorySizeCache)
}

if (
Expand Down Expand Up @@ -441,27 +455,69 @@
return this._evm['_opcodeMap'][op]
}

async _runStepHook(dynamicFee: bigint, gasLeft: bigint): Promise<void> {
const opcodeInfo = this.lookupOpInfo(this._runState.opCode)
const opcode = opcodeInfo.opcodeInfo
async _runStepHook(dynamicFee: bigint, gasLeft: bigint, memorySize: bigint): Promise<void> {
const opcodeInfo = this.lookupOpInfo(this._runState.opCode).opcodeInfo
const section = this._env.eof?.container.header.getSectionFromProgramCounter(
this._runState.programCounter,
)
let error = undefined
let immediate = undefined
if (opcodeInfo.code === 0xfd) {
// If opcode is REVERT, read error data and return in trace
const [offset, length] = this._runState.stack.peek(2)
error = new Uint8Array(0)
if (length !== BIGINT_0) {
error = this._runState.memory.read(Number(offset), Number(length))
}
}

// Add immediate if present (i.e. bytecode parameter for a preceding opcode like (RJUMP 01 - jumps to PC 1))
if (stackDelta[opcodeInfo.code].intermediates > 0) {

Check failure on line 475 in packages/evm/src/interpreter.ts

View workflow job for this annotation

GitHub Actions / browser / test-all-browser

test/customOpcodes.spec.ts > VM: custom opcodes > should add custom opcodes to the EVM

TypeError: Cannot read properties of undefined (reading 'intermediates') ❯ Interpreter._runStepHook src/interpreter.ts:475:36 ❯ Interpreter.runStep src/interpreter.ts:401:19 ❯ Interpreter.run src/interpreter.ts:339:8 ❯ EVM.runInterpreter src/evm.ts:868:27 ❯ test/customOpcodes.spec.ts:36:16

Check failure on line 475 in packages/evm/src/interpreter.ts

View workflow job for this annotation

GitHub Actions / evm / test-evm

test/eips/eip-3860.spec.ts > EIP 3860 tests > CREATE with MAX_INITCODE_SIZE+1, allowUnlimitedContractSize active

TypeError: Cannot read properties of undefined (reading 'intermediates') ❯ Interpreter._runStepHook src/interpreter.ts:475:37 ❯ Interpreter.runStep src/interpreter.ts:401:20 ❯ Interpreter.run src/interpreter.ts:339:9 ❯ EVM.runInterpreter src/evm.ts:868:28 ❯ EVM._executeCall src/evm.ts:470:16 ❯ EVM.runCall src/evm.ts:1001:16 ❯ test/eips/eip-3860.spec.ts:210:19

Check failure on line 475 in packages/evm/src/interpreter.ts

View workflow job for this annotation

GitHub Actions / evm / test-evm

test/eips/eip-3860.spec.ts > EIP 3860 tests > ensure EIP-3860 gas is applied on CREATE2 calls

TypeError: Cannot read properties of undefined (reading 'intermediates') ❯ Interpreter._runStepHook src/interpreter.ts:475:37 ❯ Interpreter.runStep src/interpreter.ts:401:20 ❯ Interpreter.run src/interpreter.ts:339:20 ❯ EVM.runInterpreter src/evm.ts:868:28 ❯ EVM._executeCall src/evm.ts:470:16 ❯ EVM.runCall src/evm.ts:1001:16 ❯ test/eips/eip-3860.spec.ts:131:17

Check failure on line 475 in packages/evm/src/interpreter.ts

View workflow job for this annotation

GitHub Actions / evm / test-evm

test/eips/eip-3860.spec.ts > EIP 3860 tests > ensure EIP-3860 gas is applied on CREATE calls

TypeError: Cannot read properties of undefined (reading 'intermediates') ❯ Interpreter._runStepHook src/interpreter.ts:475:37 ❯ Interpreter.runStep src/interpreter.ts:401:20 ❯ Interpreter.run src/interpreter.ts:339:20 ❯ EVM.runInterpreter src/evm.ts:868:28 ❯ EVM._executeCall src/evm.ts:470:16 ❯ EVM.runCall src/evm.ts:1001:16 ❯ test/eips/eip-3860.spec.ts:87:17

Check failure on line 475 in packages/evm/src/interpreter.ts

View workflow job for this annotation

GitHub Actions / evm / test-evm

test/stack.spec.ts > Stack > stack should return the padded value

TypeError: Cannot read properties of undefined (reading 'intermediates') ❯ Interpreter._runStepHook src/interpreter.ts:475:37 ❯ Interpreter.runStep src/interpreter.ts:401:20 ❯ Interpreter.run src/interpreter.ts:339:20 ❯ EVM.runInterpreter src/evm.ts:868:28 ❯ EVM._executeCreate src/evm.ts:658:18 ❯ EVM.runCall src/evm.ts:1006:16 ❯ test/stack.spec.ts:178:28

Check failure on line 475 in packages/evm/src/interpreter.ts

View workflow job for this annotation

GitHub Actions / evm / test-evm

test/customOpcodes.spec.ts > VM: custom opcodes > should add custom opcodes to the EVM

TypeError: Cannot read properties of undefined (reading 'intermediates') ❯ Interpreter._runStepHook src/interpreter.ts:475:37 ❯ Interpreter.runStep src/interpreter.ts:401:20 ❯ Interpreter.run src/interpreter.ts:339:9 ❯ EVM.runInterpreter src/evm.ts:868:28 ❯ test/customOpcodes.spec.ts:36:17

Check failure on line 475 in packages/evm/src/interpreter.ts

View workflow job for this annotation

GitHub Actions / evm / test-evm

test/blobVersionedHashes.spec.ts > BLOBHASH: access blobVersionedHashes in a CREATE/CREATE2 frame > should work

TypeError: Cannot read properties of undefined (reading 'intermediates') ❯ Interpreter._runStepHook src/interpreter.ts:475:37 ❯ Interpreter.runStep src/interpreter.ts:401:20 ❯ Interpreter.run src/interpreter.ts:339:9 ❯ EVM.runInterpreter src/evm.ts:868:28 ❯ EVM._executeCreate src/evm.ts:658:18 ❯ EVM.runCall src/evm.ts:1006:16 ❯ test/blobVersionedHashes.spec.ts:139:19

Check failure on line 475 in packages/evm/src/interpreter.ts

View workflow job for this annotation

GitHub Actions / evm / test-evm

test/blobVersionedHashes.spec.ts > BLOBHASH: access blobVersionedHashes within contract calls > should work

TypeError: Cannot read properties of undefined (reading 'intermediates') ❯ Interpreter._runStepHook src/interpreter.ts:475:37 ❯ Interpreter.runStep src/interpreter.ts:401:20 ❯ Interpreter.run src/interpreter.ts:339:20 ❯ EVM.runInterpreter src/evm.ts:868:28 ❯ EVM._executeCreate src/evm.ts:658:18 ❯ EVM.runCall src/evm.ts:1006:16 ❯ test/blobVersionedHashes.spec.ts:86:19
immediate = this._runState.code.slice(
this._runState.programCounter,
this._runState.programCounter + stackDelta[opcodeInfo.code].intermediates,
)
}

if (opcodeInfo.name === 'SLOAD') {
// Store SLOADed values for recording in trace
const key = this._runState.stack.peek(1)
const value = await this.storageLoad(setLengthLeft(bigIntToBytes(key[0]), 32))
this._runState.accessedStorage.set(`0x${key[0].toString(16)}`, bytesToHex(value))
}

if (opcodeInfo.name === 'SSTORE') {
// Store SSTOREed values for recording in trace
const [key, value] = this._runState.stack.peek(2)
this._runState.accessedStorage.set(`0x${key.toString(16)}`, `0x${value.toString(16)}`)
}

// Create event object for step
const eventObj: InterpreterStep = {
pc: this._runState.programCounter,
gasLeft,
gasRefund: this._runState.gasRefund,
opcode: {
name: opcode.fullName,
fee: opcode.fee,
name: opcodeInfo.fullName,
fee: opcodeInfo.fee,
dynamicFee,
isAsync: opcode.isAsync,
isAsync: opcodeInfo.isAsync,
code: opcodeInfo.code,
},
stack: this._runState.stack.getStack(),
depth: this._env.depth,
address: this._env.address,
account: this._env.contract,
memory: this._runState.memory._store.subarray(0, Number(this._runState.memoryWordCount) * 32),
memoryWordCount: this._runState.memoryWordCount,
memory: this._runState.memory._store.subarray(0, Number(memorySize) * 32),
memoryWordCount: memorySize,
codeAddress: this._env.codeAddress,
stateManager: this._runState.stateManager,
eofSection: section,
immediate,
error,
eofFunctionDepth:
this._env.eof !== undefined ? this._env.eof?.eofRunState.returnStack.length + 1 : undefined,
storage: Array.from(this._runState.accessedStorage.entries()),
}

if (this._evm.DEBUG) {
Expand Down Expand Up @@ -499,6 +555,7 @@
* @property {fee} opcode.number Base fee of the opcode
* @property {dynamicFee} opcode.dynamicFee Dynamic opcode fee
* @property {boolean} opcode.isAsync opcode is async
* @property {number} opcode.code opcode code
* @property {BigInt} gasLeft amount of gasLeft
* @property {BigInt} gasRefund gas refund
* @property {StateManager} stateManager a {@link StateManager} instance
Expand All @@ -510,6 +567,11 @@
* @property {Uint8Array} memory the memory of the EVM as a `Uint8Array`
* @property {BigInt} memoryWordCount current size of memory in words
* @property {Address} codeAddress the address of the code which is currently being ran (this differs from `address` in a `DELEGATECALL` and `CALLCODE` call)
* @property {number} eofSection the current EOF code section referenced by the PC
* @property {Uint8Array} immediate the immediate argument of the opcode
* @property {Uint8Array} error the error data of the opcode (only present for REVERT)
* @property {number} eofFunctionDepth the depth of the function call (only present for EOF)
* @property {Array} storage an array of tuples, where each tuple contains a storage key and value
*/
await this._evm['_emit']('step', eventObj)
}
Expand Down
1 change: 1 addition & 0 deletions packages/evm/test/runCall.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import { createEVM } from '../src/index.ts'

import type { EVMRunCallOpts } from '../src/types.ts'
import { eip4844Data } from './testdata/eip4844.ts'

Check failure on line 25 in packages/evm/test/runCall.spec.ts

View workflow job for this annotation

GitHub Actions / lint / lint

'eip4844Data' is defined but never used. Allowed unused vars must match /^_/u

// Non-protected Create2Address generator. Does not check if Uint8Arrays have the right padding.
function create2address(sourceAddress: Address, codeHash: Uint8Array, salt: Uint8Array): Address {
Expand Down
117 changes: 111 additions & 6 deletions packages/vm/test/api/t8ntool/t8ntool.spec.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import { readFileSync } from 'fs'
import { assert, describe, it } from 'vitest'
import { assert, describe, expect, it } from 'vitest'

import { TransitionTool } from '../../t8n/t8ntool.ts'

import { Common, Hardfork, Mainnet } from '@ethereumjs/common'
import { MerkleStateManager } from '@ethereumjs/statemanager'
import { createTx } from '@ethereumjs/tx'
import { Account, createAddressFromPrivateKey, hexToBytes, randomBytes } from '@ethereumjs/util'
import { createVM, runTx } from '../../../src/index.ts'
import { stepTraceJSON, summaryTraceJSON } from '../../t8n/helpers.ts'
import type { T8NOptions } from '../../t8n/types.ts'

const t8nDir = 'test/t8n/testdata/'

const args: T8NOptions = {
Expand All @@ -24,6 +29,7 @@ const args: T8NOptions = {
alloc: `output/allocTEST.json`,
},
log: false,
trace: false,
}

// This test is generated using `execution-spec-tests` commit 88cab2521322191b2ec7ef7d548740c0b0a264fc, running:
Expand All @@ -34,11 +40,110 @@ const args: T8NOptions = {
describe('test runner config tests', () => {
it('should run t8ntool with inputs and report the expected output', async () => {
await TransitionTool.run(args)
const expectedResult = JSON.parse(readFileSync(`${t8nDir}/output/result.json`).toString())
const expectedAlloc = JSON.parse(readFileSync(`${t8nDir}/output/alloc.json`).toString())
const reportedResult = JSON.parse(readFileSync(`${t8nDir}/output/resultTEST.json`).toString())
const reportedAlloc = JSON.parse(readFileSync(`${t8nDir}/output/allocTEST.json`).toString())
const expectedResult = JSON.parse(readFileSync(`${t8nDir}output/result.json`).toString())
const expectedAlloc = JSON.parse(readFileSync(`${t8nDir}output/alloc.json`).toString())
const reportedResult = JSON.parse(readFileSync(`${t8nDir}output/resultTEST.json`).toString())
const reportedAlloc = JSON.parse(readFileSync(`${t8nDir}output/allocTEST.json`).toString())
assert.deepStrictEqual(reportedResult, expectedResult, 'result matches expected result')
assert.deepStrictEqual(reportedAlloc, expectedAlloc, 'alloc matches expected alloc')
})
})
describe('trace tests', async () => {
it('should produce a valid step trace for a legacy contract', async () => {
const common = new Common({ chain: Mainnet, hardfork: Hardfork.Cancun })
const sm = new MerkleStateManager({ common })
const vm = await createVM({ common, stateManager: sm })
const bytecode = hexToBytes('0x604260005260206000F3') // PUSH1 42 PUSH1 00 MSTORE PUSH1 20 PUSH1 00 RETURN
const contractAddress = createAddressFromPrivateKey(randomBytes(32))
await vm.stateManager.putAccount(contractAddress)
await vm.stateManager.putCode(contractAddress, bytecode)
const trace: string[] = []
vm.evm.events!.on('step', (step) => {
trace.push(JSON.stringify(stepTraceJSON(step, true)))
})
vm.events!.on('afterTx', async (event) => {
trace.push(JSON.stringify(await summaryTraceJSON(event, vm)))
})
const tx = await createTx({
to: contractAddress,
data: bytecode,
gasLimit: 0xffffffff,
gasPrice: 0xf,
}).sign(randomBytes(32))
await runTx(vm, { tx, skipBalance: true, skipBlockGasLimitValidation: true, skipNonce: true })
assert.equal(trace.length, 7, 'trace length is 7')
assert.equal(JSON.parse(trace[6]).gasUsed, 21154)
})
it('should produce a trace of the correct length', async () => {
const common = new Common({
hardfork: Hardfork.Prague,
eips: [663, 3540, 3670, 4200, 4750, 5450, 6206, 7069, 7480, 7620, 7692, 7698],
chain: Mainnet,
})
const sm = new MerkleStateManager({ common })
const vm = await createVM({ common, stateManager: sm })
const code = hexToBytes('0xef000101000402000100030400010000800001305000ef')

const pk = randomBytes(32)
const caller = createAddressFromPrivateKey(pk) // caller address
const contractAddress = createAddressFromPrivateKey(randomBytes(32)) // contract address

await vm.stateManager.putCode(contractAddress, code)
await vm.stateManager.putAccount(caller, new Account(BigInt(0), BigInt(0x11111111)))

const tx = await createTx({
gasLimit: BigInt(0xffff),
gasPrice: 0x7,
to: contractAddress,
}).sign(pk)
const trace: string[] = []
vm.evm.events!.on('step', (step) => {
trace.push(JSON.stringify(stepTraceJSON(step, true)))
})
vm.events!.on('afterTx', async (event) => {
trace.push(JSON.stringify(await summaryTraceJSON(event, vm)))
})
const result = await runTx(vm, {
tx,
skipBalance: true,
skipNonce: true,
skipBlockGasLimitValidation: true,
})
assert.equal(result.execResult.executionGasUsed, BigInt(4))
assert.equal(trace.length, 4)
})
it('should produce a trace with storage activates', async () => {
const bytecode = hexToBytes('0x604260005560206000f3') // PUSH1 42 PUSH1 00 MSTORE PUSH1 20 PUSH1 00 RETURN
const common = new Common({ chain: Mainnet, hardfork: Hardfork.Cancun })
const sm = new MerkleStateManager({ common })
const vm = await createVM({ common, stateManager: sm })
const contractAddress = createAddressFromPrivateKey(randomBytes(32))
await vm.stateManager.putAccount(contractAddress)
await vm.stateManager.putCode(contractAddress, bytecode)
const trace: string[] = []
vm.evm.events!.on('step', (step) => {
trace.push(JSON.stringify(stepTraceJSON(step, false, true)))
})
vm.events!.on('afterTx', async (event) => {
trace.push(JSON.stringify(await summaryTraceJSON(event, vm)))
})
const tx = await createTx({
to: contractAddress,
gasLimit: 0xffffffff,
gasPrice: 0xf,
}).sign(randomBytes(32))

await runTx(vm, { tx, skipBalance: true, skipBlockGasLimitValidation: true, skipNonce: true })
assert.equal(trace.length, 7, 'trace length is 7')

// First step trace should have empty storage
const traceStepWithoutStorage = JSON.parse(trace[0])
expect(traceStepWithoutStorage.storage.length).toBe(0)

// Last step trace should have storage with actual value
const traceStepWithStorage = JSON.parse(trace[5])
assert.exists(traceStepWithStorage.storage)
expect(traceStepWithStorage.storage).toMatchObject([['0x0', '0x42']])
assert.equal(JSON.parse(trace[6]).gasUsed, 43115)
})
})
2 changes: 1 addition & 1 deletion packages/vm/test/t8n/ethereumjs-t8ntool.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ if [[ "$1" == "--version" ]]; then
fi
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
export NODE_OPTIONS="--max-old-space-size=4096"
npx tsx "$SCRIPT_DIR/launchT8N.ts" "$@"
npx tsx --conditions=typescript "$SCRIPT_DIR/launchT8N.ts" "$@"
Loading
Loading