-
Notifications
You must be signed in to change notification settings - Fork 0
/
evm.test.ts
115 lines (97 loc) · 3.66 KB
/
evm.test.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
import { Context, evm, AccountState } from "./bytecode-parser";
import { expect, test } from "bun:test";
import { hexStringToUint8Array, uint8ArrayToHexString } from "./opcodes";
//@ts-expect-error The file exists!
import tests from "./tests";
for (const t of tests as any) {
test(t.name, () => {
// Note: as the test cases get more complex, you'll need to modify this
// to pass down more arguments to the evm function (e.g. block, state, etc.)
// and return more data (e.g. state, logs, etc.)
console.log("\n\n-------", t.name, "-------\n");
console.log("Test bytecode: 0x"+t.code.bin);
console.log("Test opcodes");
console.log("\x1b[34m%s\x1b[0m", t.code.asm, "\n");
const worldState = new Map<bigint, AccountState>();
if (t.state) {
const addresses = Object.keys(t.state);
addresses.forEach(address => {
const accountState = t.state[address];
worldState.set(BigInt(address), {
balance: BigInt(accountState.balance || 0n),
code: {
asm: accountState?.code?.asm,
bin: hexStringToUint8Array(accountState?.code?.bin)
} || undefined,
storage: accountState.code ? new Map<bigint, bigint>(): undefined,
nonce: 0n
});
});
}
const CONTRACT_ADDRESS = BigInt(t?.tx?.to || 0xff);
const CALLER_ADDRESS = BigInt(t?.tx?.from || 0x00);
// always initialize the `to` contract's state, the one for whom our evm is executing stuff;
worldState.set(CONTRACT_ADDRESS, {
balance: worldState.get(CONTRACT_ADDRESS)?.balance || 0n,
code: {
asm: t.code.asm || null,
bin: hexStringToUint8Array(t.code.bin)
},
storage: new Map<bigint, bigint>(),
nonce: 0n
});
if (worldState.get(CALLER_ADDRESS)?.code?.bin) {
worldState.set(CALLER_ADDRESS, {
balance: worldState.get(CALLER_ADDRESS)?.balance || 0n,
code: {
asm: worldState.get(CALLER_ADDRESS)?.code?.asm,
bin: worldState.get(CALLER_ADDRESS)!.code!.bin
},
storage: new Map<bigint, bigint>(),
nonce: 0n
});
} else {
worldState.set(CALLER_ADDRESS, {
balance: worldState.get(CALLER_ADDRESS)?.balance || 0n,
storage: new Map<bigint, bigint>(),
nonce: 0n
});
}
const context: Context = {
address: CONTRACT_ADDRESS,
caller: CALLER_ADDRESS,
origin: BigInt(t?.tx?.origin || 0x00),
callValue: BigInt(t?.tx?.value || 0n),
callData: hexStringToUint8Array(t?.tx?.data || ""),
gasPrice: BigInt(t?.tx?.gasprice || 0n),
gasLeft: 15_000_000,
isStatic: false,
bytecode: hexStringToUint8Array(t.code.bin),
block: {
basefee: BigInt(t?.block?.basefee || 0n),
coinbase: BigInt(t?.block?.coinbase || 0n),
timestamp: BigInt(t?.block?.timestamp || 0n),
number: BigInt(t?.block?.number || 0n),
difficulty: BigInt(t?.block?.difficulty || 0n),
gasLimit: BigInt(t?.block?.gaslimit || 0n),
chainId: BigInt(t?.block?.chainid || 1n),
},
state: worldState,
}
const result = evm(context, 3);
expect(result.success).toEqual(t.expect.success);
if (t.expect.stack) {
expect(result.stack).toEqual(t.expect.stack.map((item:any) => BigInt(item)));
}
if (t.expect.logs) {
expect(result.logs).toEqual(t.expect.logs.map((item:any) => ({
address: BigInt(item.address),
data: BigInt("0x" + item.data),
topics: item.topics.map((topic:any) => BigInt(topic))
})));
}
if (t.expect.return) {
expect(BigInt(uint8ArrayToHexString(result.returndata))).toEqual(BigInt("0x" + t.expect.return));
}
});
}