Skip to content

Commit 8411345

Browse files
committed
Finish constructing test
1 parent 12f5180 commit 8411345

File tree

4 files changed

+175
-26
lines changed

4 files changed

+175
-26
lines changed

packages/evm/src/evm.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ import type { OpHandler, OpcodeList, OpcodeMap } from './opcodes/index.js'
5454
import type { CustomPrecompile, PrecompileFunc } from './precompiles/index.js'
5555
import type { VerkleAccessWitness } from './verkleAccessWitness.js'
5656
import type { Common, StateManagerInterface } from '@ethereumjs/common'
57+
import type { PrefixedHexString } from '@ethereumjs/util'
5758

5859
const debug = debugDefault('evm:evm')
5960
const debugGas = debugDefault('evm:gas')
@@ -150,6 +151,8 @@ export class EVM implements EVMInterface {
150151

151152
private _bn254: EVMBN254Interface
152153

154+
private executionBlobs: Map<PrefixedHexString, Uint8Array> // Map of <sha256 hash of trace, trace bytes>
155+
153156
/**
154157
*
155158
* Creates new EVM object
@@ -245,6 +248,8 @@ export class EVM implements EVMInterface {
245248
// Additional window check is to prevent vite browser bundling (and potentially other) to break
246249
this.DEBUG =
247250
typeof window === 'undefined' ? (process?.env?.DEBUG?.includes('ethjs') ?? false) : false
251+
252+
this.executionBlobs = new Map()
248253
}
249254

250255
/**

packages/evm/src/precompiles/12-execute.ts

Lines changed: 78 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,107 @@
1-
import { StatelessVerkleStateManager } from '@ethereumjs/statemanager'
1+
import { StatefulVerkleStateManager } from '@ethereumjs/statemanager'
2+
import {
3+
bytesToBigInt,
4+
bytesToHex,
5+
createAddressFromString,
6+
equalsBytes,
7+
hexToBytes,
8+
} from '@ethereumjs/util'
9+
import { createVerkleTree } from '@ethereumjs/verkle'
210

311
import { createEVM } from '../constructors.js'
412
import { EvmErrorResult, OOGResult } from '../evm.js'
513
import { ERROR, EvmError } from '../exceptions.js'
14+
import { VerkleAccessWitness } from '../verkleAccessWitness.js'
615

716
import { gasLimitCheck } from './util.js'
817

918
import { getPrecompileName } from './index.js'
1019

20+
import type { EVM } from '../evm.js'
1121
import type { ExecResult } from '../types.js'
1222
import type { PrecompileInput } from './types.js'
23+
import type { VerkleExecutionWitness } from '@ethereumjs/util'
1324

14-
export function precompile12(opts: PrecompileInput): ExecResult {
25+
export async function precompile12(opts: PrecompileInput): Promise<ExecResult> {
1526
const pName = getPrecompileName('12')
1627
const data = opts.data
17-
28+
const evm = opts._EVM as EVM
1829
const gasUsed = opts.common.param('executeGasCost')
1930
if (!gasLimitCheck(opts, gasUsed, pName)) {
2031
return OOGResult(opts.gasLimit)
2132
}
2233

23-
if (data.length < 128) {
34+
if (data.length !== 128) {
2435
return EvmErrorResult(new EvmError(ERROR.INVALID_INPUT_LENGTH), opts.gasLimit)
2536
}
2637

27-
const preStateRoot = data.subarray(0, 32) // prestateroot for L2 state
38+
const _preStateRoot = data.subarray(0, 32) // prestateroot for L2 state
2839
const postStateRoot = data.subarray(32, 64) // post state root for L2 state
29-
const trace = data.subarray(64, 96) // reference to state access and
30-
const executeGasUsed = data.subarray(96)
40+
const traceBlob = evm['executionBlobs'].get(bytesToHex(data.subarray(64, 96))) // reference to state access and transactions
41+
if (traceBlob === undefined) {
42+
opts._debug?.(`${pName} error - trace not found`)
43+
return EvmErrorResult(new EvmError(ERROR.REVERT), opts.gasLimit)
44+
}
3145

46+
const decodedTrace = JSON.parse(new TextDecoder().decode(traceBlob))
47+
48+
if (decodedTrace.txs === undefined || decodedTrace.witness === undefined) {
49+
opts._debug?.(`${pName} error - trace is invalid`)
50+
return EvmErrorResult(new EvmError(ERROR.REVERT), opts.gasLimit)
51+
}
52+
const executeGasUsed = bytesToBigInt(data.subarray(96))
53+
54+
const witness = decodedTrace.witness as VerkleExecutionWitness
55+
const tree = await createVerkleTree({ verkleCrypto: opts.common.customCrypto.verkle })
56+
57+
// Populate the L2 state trie with the prestate
58+
for (const stateDiff of witness.stateDiff) {
59+
const suffixes: number[] = []
60+
const values: Uint8Array[] = []
61+
for (const diff of stateDiff.suffixDiffs) {
62+
if (diff.currentValue !== null) {
63+
suffixes.push(Number(diff.suffix))
64+
values.push(hexToBytes(diff.currentValue))
65+
}
66+
}
67+
const stem = hexToBytes(stateDiff.stem)
68+
await tree.put(stem, suffixes, values)
69+
}
3270
const executionResult = true
3371

34-
const stateManager = new StatelessVerkleStateManager({ common: opts.common })
35-
const evm = createEVM({ stateManager, common: opts.common })
72+
const stateManager = new StatefulVerkleStateManager({ common: opts.common, trie: tree })
73+
const l2EVM = await createEVM({ stateManager, common: opts.common })
74+
75+
l2EVM.verkleAccessWitness = new VerkleAccessWitness({
76+
verkleCrypto: opts.common.customCrypto.verkle!,
77+
})
78+
l2EVM.systemVerkleAccessWitness = new VerkleAccessWitness({
79+
verkleCrypto: opts.common.customCrypto.verkle!,
80+
})
81+
let computedGasUsed = 0n
82+
83+
// Run each transaction in the trace
84+
for (const tx of decodedTrace.txs) {
85+
const res = await l2EVM.runCall({
86+
to: createAddressFromString(tx.to),
87+
caller: createAddressFromString(tx.from),
88+
gasLimit: BigInt(tx.gasLimit),
89+
gasPrice: BigInt(tx.gasPrice),
90+
value: BigInt(tx.value),
91+
data: tx.data !== undefined ? hexToBytes(tx.data) : undefined,
92+
})
93+
computedGasUsed += res.execResult.executionGasUsed
94+
}
95+
96+
if (computedGasUsed !== executeGasUsed) {
97+
opts._debug?.(`${pName} gas used mismatch: ${computedGasUsed} !== ${executeGasUsed}`)
98+
return EvmErrorResult(new EvmError(ERROR.REVERT), opts.gasLimit)
99+
}
100+
101+
if (!equalsBytes(postStateRoot, tree.root())) {
102+
opts._debug?.(`${pName} post state root mismatch`)
103+
return EvmErrorResult(new EvmError(ERROR.REVERT), opts.gasLimit)
104+
}
36105

37106
opts._debug?.(`${pName} trace executed successfully=${executionResult}`)
38107

packages/evm/src/precompiles/index.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { precompile0e } from './0e-bls12-g2msm.js'
1818
import { precompile0f } from './0f-bls12-pairing.js'
1919
import { precompile10 } from './10-bls12-map-fp-to-g1.js'
2020
import { precompile11 } from './11-bls12-map-fp2-to-g2.js'
21+
import { precompile12 } from './12-execute.js'
2122
import { MCLBLS, NobleBLS } from './bls12_381/index.js'
2223
import { NobleBN254, RustBN254 } from './bn254/index.js'
2324

@@ -210,6 +211,15 @@ const precompileEntries: PrecompileEntry[] = [
210211
precompile: precompile11,
211212
name: 'BLS12_MAP_FP_TO_G2 (0x11)',
212213
},
214+
{
215+
address: BYTES_19 + '12',
216+
check: {
217+
type: PrecompileAvailabilityCheck.EIP,
218+
param: 9999,
219+
},
220+
precompile: precompile12,
221+
name: 'EXECUTE (0x12)',
222+
},
213223
]
214224

215225
const precompiles: Precompiles = {
@@ -230,6 +240,7 @@ const precompiles: Precompiles = {
230240
[BYTES_19 + '0f']: precompile0f,
231241
[BYTES_19 + '10']: precompile10,
232242
[BYTES_19 + '11']: precompile11,
243+
[BYTES_19 + '12']: precompile12,
233244
}
234245

235246
type DeletePrecompile = {
Lines changed: 81 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,103 @@
11
import { Common, Hardfork, Mainnet } from '@ethereumjs/common'
2-
import { StatelessVerkleStateManager } from '@ethereumjs/statemanager'
3-
import { createTx, createTxFromRLP } from '@ethereumjs/tx'
4-
import { trustedSetup } from '@paulmillr/trusted-setups/fast.js'
5-
import { hexToBytes } from 'ethereum-cryptography/utils'
6-
import { KZG as microEthKZG } from 'micro-eth-signer/kzg'
2+
import { StatefulVerkleStateManager } from '@ethereumjs/statemanager'
3+
import {
4+
bigIntToBytes,
5+
bytesToHex,
6+
concatBytes,
7+
createAccount,
8+
createAddressFromPrivateKey,
9+
createAddressFromString,
10+
hexToBytes,
11+
setLengthLeft,
12+
} from '@ethereumjs/util'
13+
import { createVerkleTree } from '@ethereumjs/verkle'
14+
import { sha256 } from 'ethereum-cryptography/sha256'
715
import * as verkle from 'micro-eth-signer/verkle'
816
import { assert, describe, it } from 'vitest'
917

10-
import { createEVM, getActivePrecompiles } from '../../src/index.js'
11-
12-
import witness from './executionWitness.json'
18+
import {
19+
VerkleAccessWitness,
20+
createEVM,
21+
generateExecutionWitness,
22+
getActivePrecompiles,
23+
} from '../../src/index.js'
1324

1425
describe('Precompiles: EXECUTE', () => {
1526
it('should execute a trace', async () => {
16-
const kzg = new microEthKZG(trustedSetup)
1727
const common = new Common({
1828
chain: Mainnet,
1929
hardfork: Hardfork.Prague,
2030
eips: [6800, 9999],
2131
customCrypto: {
22-
kzg,
2332
verkle,
2433
},
2534
})
26-
const stateManager = new StatelessVerkleStateManager({ common })
27-
stateManager.initVerkleExecutionWitness(1n, witness.witness)
35+
const account = createAccount({ balance: 0xffffffffffffffffffffffffffffffffffffffffn })
36+
const address = createAddressFromString('0x999aebeac9619be18e0369d9cb8d0393cfb99021')
37+
const receiver = createAddressFromPrivateKey(
38+
hexToBytes('0xaeb51ceb07e4f6761ea6ad9a772d0e4a70367020fd6175b5e271d0d12e37d24d'),
39+
)
40+
const tx = {
41+
to: receiver.toString(),
42+
from: address.toString(),
43+
gasLimit: '0xffffffffff',
44+
gasPrice: '0x1',
45+
value: '0x1',
46+
}
47+
const tree = await createVerkleTree({ verkleCrypto: verkle })
48+
const stateManager = new StatefulVerkleStateManager({ common, trie: tree })
49+
await stateManager.putAccount(address, account)
50+
const preStateRoot = tree.root()
2851
const evm = await createEVM({ stateManager, common })
29-
const txs = witness.txs.map((tx) => createTxFromRLP(hexToBytes(tx), { common }))
30-
for (const tx of txs) {
31-
const res = await evm.runCall(tx)
32-
console.log(res)
52+
53+
evm.verkleAccessWitness = new VerkleAccessWitness({
54+
verkleCrypto: verkle,
55+
})
56+
evm.systemVerkleAccessWitness = new VerkleAccessWitness({
57+
verkleCrypto: verkle,
58+
})
59+
const res = await evm.runCall({
60+
to: receiver,
61+
caller: address,
62+
gasLimit: BigInt(tx.gasLimit),
63+
gasPrice: BigInt(tx.gasPrice),
64+
value: BigInt(tx.value),
65+
})
66+
const executionGasUsed = res.execResult.executionGasUsed
67+
const postStateRoot = tree.root()
68+
const execWitness = await generateExecutionWitness(
69+
stateManager,
70+
evm.verkleAccessWitness,
71+
preStateRoot,
72+
)
73+
74+
// Create a trace
75+
const trace = {
76+
witness: execWitness,
77+
txs: [tx],
3378
}
79+
// TODO: Replace this with proper serialization
80+
const bytes = new TextEncoder().encode(JSON.stringify(trace))
81+
// We use the hash as a reference to the trace. This should be replaced with a proper blob commitment (or versionedHash)
82+
// and the data should be stored as a proper Ethereum blob
83+
const hash = bytesToHex(sha256(bytes))
84+
evm['executionBlobs'].set(hash, bytes)
85+
3486
const addressStr = '0000000000000000000000000000000000000012'
3587
const EXECUTE = getActivePrecompiles(common).get(addressStr)!
88+
const input = concatBytes(
89+
preStateRoot,
90+
postStateRoot,
91+
hexToBytes(hash),
92+
setLengthLeft(bigIntToBytes(executionGasUsed), 32),
93+
)
3694

37-
const result = await EXECUTE({})
95+
const result = await EXECUTE({
96+
data: input,
97+
gasLimit: 1000000000n,
98+
common,
99+
_EVM: evm,
100+
})
101+
assert.equal(result.returnValue[0], 1)
38102
})
39103
})

0 commit comments

Comments
 (0)