Skip to content

Commit bfb14c5

Browse files
committed
Convert trace to ssz container
1 parent 44a447a commit bfb14c5

File tree

2 files changed

+73
-20
lines changed

2 files changed

+73
-20
lines changed

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

Lines changed: 58 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
hexToBytes,
88
} from '@ethereumjs/util'
99
import { createVerkleTree } from '@ethereumjs/verkle'
10+
import * as ssz from 'micro-eth-signer/ssz'
1011

1112
import { createEVM } from '../constructors.js'
1213
import { EvmErrorResult, OOGResult } from '../evm.js'
@@ -22,6 +23,56 @@ import type { ExecResult } from '../types.js'
2223
import type { PrecompileInput } from './types.js'
2324
import type { VerkleExecutionWitness } from '@ethereumjs/util'
2425

26+
const MAX_CALL_DATA_SIZE = 7500000 // Assuming a transaction with all zero bytes fills up an entire block worth of gas
27+
export const traceContainer = ssz.container({
28+
txs: ssz.list(
29+
256,
30+
ssz.container({
31+
to: ssz.bytevector(20),
32+
from: ssz.bytevector(20),
33+
gasLimit: ssz.uint64,
34+
gasPrice: ssz.uint64,
35+
value: ssz.uint64,
36+
data: ssz.bytelist(MAX_CALL_DATA_SIZE),
37+
}),
38+
),
39+
witness: ssz.container({
40+
stateDiff: ssz.list(
41+
256,
42+
ssz.container({
43+
stem: ssz.bytevector(31),
44+
suffixDiffs: ssz.list(
45+
256,
46+
ssz.container({
47+
suffix: ssz.uint64,
48+
currentValue: ssz.bytevector(32),
49+
newValue: ssz.bytevector(32),
50+
}),
51+
),
52+
}),
53+
),
54+
parentStateRoot: ssz.bytevector(32),
55+
}),
56+
})
57+
58+
export const executionWitnessJSONToSSZ = (witness: VerkleExecutionWitness) => {
59+
return {
60+
stateDiff: witness.stateDiff.map((diff) => ({
61+
stem: hexToBytes(diff.stem),
62+
suffixDiffs: diff.suffixDiffs.map((suffixDiff) => ({
63+
suffix: BigInt(suffixDiff.suffix),
64+
currentValue:
65+
suffixDiff.currentValue !== null
66+
? hexToBytes(suffixDiff.currentValue)
67+
: new Uint8Array(32),
68+
newValue:
69+
suffixDiff.newValue !== null ? hexToBytes(suffixDiff.newValue) : new Uint8Array(32),
70+
})),
71+
})),
72+
parentStateRoot: hexToBytes(witness.parentStateRoot),
73+
}
74+
}
75+
2576
export async function precompile12(opts: PrecompileInput): Promise<ExecResult> {
2677
const pName = getPrecompileName('12')
2778
const data = opts.data
@@ -43,15 +94,15 @@ export async function precompile12(opts: PrecompileInput): Promise<ExecResult> {
4394
return EvmErrorResult(new EvmError(ERROR.REVERT), opts.gasLimit)
4495
}
4596

46-
const decodedTrace = JSON.parse(new TextDecoder().decode(traceBlob))
97+
const decodedTrace = traceContainer.decode(traceBlob)
4798

4899
if (decodedTrace.txs === undefined || decodedTrace.witness === undefined) {
49100
opts._debug?.(`${pName} error - trace is invalid`)
50101
return EvmErrorResult(new EvmError(ERROR.REVERT), opts.gasLimit)
51102
}
52103
const executeGasUsed = bytesToBigInt(data.subarray(96))
53104

54-
const witness = decodedTrace.witness as VerkleExecutionWitness
105+
const witness = decodedTrace.witness
55106
const tree = await createVerkleTree({ verkleCrypto: opts.common.customCrypto.verkle })
56107

57108
// Populate the L2 state trie with the prestate
@@ -61,10 +112,10 @@ export async function precompile12(opts: PrecompileInput): Promise<ExecResult> {
61112
for (const diff of stateDiff.suffixDiffs) {
62113
if (diff.currentValue !== null) {
63114
suffixes.push(Number(diff.suffix))
64-
values.push(hexToBytes(diff.currentValue))
115+
values.push(diff.currentValue)
65116
}
66117
}
67-
const stem = hexToBytes(stateDiff.stem)
118+
const stem = stateDiff.stem
68119
await tree.put(stem, suffixes, values)
69120
}
70121
const executionResult = true
@@ -83,12 +134,12 @@ export async function precompile12(opts: PrecompileInput): Promise<ExecResult> {
83134
// Run each transaction in the trace
84135
for (const tx of decodedTrace.txs) {
85136
const res = await l2EVM.runCall({
86-
to: createAddressFromString(tx.to),
87-
caller: createAddressFromString(tx.from),
137+
to: createAddressFromString(bytesToHex(tx.to)),
138+
caller: createAddressFromString(bytesToHex(tx.from)),
88139
gasLimit: BigInt(tx.gasLimit),
89140
gasPrice: BigInt(tx.gasPrice),
90141
value: BigInt(tx.value),
91-
data: tx.data !== undefined ? hexToBytes(tx.data) : undefined,
142+
data: tx.data !== undefined ? tx.data : undefined,
92143
})
93144
computedGasUsed += res.execResult.executionGasUsed
94145
}

packages/evm/test/precompiles/12-execute.spec.ts

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
generateExecutionWitness,
2222
getActivePrecompiles,
2323
} from '../../src/index.js'
24+
import { executionWitnessJSONToSSZ, traceContainer } from '../../src/precompiles/12-execute.js'
2425

2526
describe('Precompiles: EXECUTE', () => {
2627
it('should execute a trace', async () => {
@@ -39,11 +40,12 @@ describe('Precompiles: EXECUTE', () => {
3940
hexToBytes('0xaeb51ceb07e4f6761ea6ad9a772d0e4a70367020fd6175b5e271d0d12e37d24d'),
4041
)
4142
const tx = {
42-
to: receiver.toString(),
43-
from: address.toString(),
44-
gasLimit: '0xffffffffff',
45-
gasPrice: '0x1',
46-
value: '0x1',
43+
to: receiver.toBytes(),
44+
from: address.toBytes(),
45+
gasLimit: BigInt('0xffffffffff'),
46+
gasPrice: BigInt('0x1'),
47+
value: BigInt('0x1'),
48+
data: new Uint8Array(),
4749
}
4850
const tree = await createVerkleTree({ verkleCrypto: verkle })
4951
const stateManager = new StatefulVerkleStateManager({ common, trie: tree })
@@ -60,9 +62,9 @@ describe('Precompiles: EXECUTE', () => {
6062
const res = await evm.runCall({
6163
to: receiver,
6264
caller: address,
63-
gasLimit: BigInt(tx.gasLimit),
64-
gasPrice: BigInt(tx.gasPrice),
65-
value: BigInt(tx.value),
65+
gasLimit: tx.gasLimit,
66+
gasPrice: tx.gasPrice,
67+
value: tx.value,
6668
})
6769
const executionGasUsed = res.execResult.executionGasUsed
6870
const postStateRoot = tree.root()
@@ -76,15 +78,15 @@ describe('Precompiles: EXECUTE', () => {
7678

7779
// Create a trace
7880
const trace = {
79-
witness: execWitness,
81+
witness: executionWitnessJSONToSSZ(execWitness),
8082
txs: [tx],
8183
}
82-
// TODO: Replace this with proper serialization
83-
const bytes = new TextEncoder().encode(JSON.stringify(trace))
84+
const traceBytes = traceContainer.encode(trace)
85+
8486
// We use the hash as a reference to the trace. This should be replaced with a proper blob commitment (or versionedHash)
8587
// and the data should be stored as a proper Ethereum blob
86-
const hash = bytesToHex(sha256(bytes))
87-
evm['executionBlobs'].set(hash, bytes)
88+
const hash = bytesToHex(sha256(traceBytes))
89+
evm['executionBlobs'].set(hash, traceBytes)
8890

8991
const addressStr = '0000000000000000000000000000000000000012'
9092
const EXECUTE = getActivePrecompiles(common).get(addressStr)!

0 commit comments

Comments
 (0)