Skip to content

Commit 1720af4

Browse files
committed
add EOF V1 code deployment test case
1 parent f83aa02 commit 1720af4

File tree

3 files changed

+371
-1
lines changed

3 files changed

+371
-1
lines changed

tests/all_tests.nim

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,5 @@ cliBuilder:
5050
./test_txpool,
5151
./test_merge,
5252
./test_eip4844,
53-
./test_beacon/test_skeleton
53+
./test_beacon/test_skeleton,
54+
./test_eof

tests/customgenesis/eof.json

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
{
2+
"config": {
3+
"chainId": 7,
4+
"homesteadBlock": 0,
5+
"eip150Block": 0,
6+
"eip150Hash": "0x5de1ee4135274003348e80b788e5afa4b18b18d320a5622218d5c493fedf5689",
7+
"eip155Block": 0,
8+
"eip158Block": 0,
9+
"byzantiumBlock": 0,
10+
"constantinopleBlock": 0,
11+
"petersburgBlock": 0,
12+
"istanbulBlock": 0,
13+
"muirGlacierBlock": 0,
14+
"berlinBlock": 0,
15+
"londonBlock": 0,
16+
"mergeForkBlock": 0,
17+
"shanghaiBlock": 0,
18+
"cancunBlock": 0,
19+
"terminalTotalDifficulty": 0
20+
},
21+
"genesis": {
22+
"coinbase": "0x0000000000000000000000000000000000000000",
23+
"difficulty": "0x30000",
24+
"extraData": "0x0000000000000000000000000000000000000000000000000000000000000000658bdf435d810c91414ec09147daa6db624063790000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
25+
"gasLimit": "0x2fefd8",
26+
"nonce": "0x0000000000000000",
27+
"timestamp": "0x1234",
28+
"alloc": {
29+
}
30+
}
31+
}

tests/test_eof.nim

Lines changed: 338 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,338 @@
1+
import
2+
std/[tables, math, times],
3+
eth/[keys],
4+
stew/byteutils,
5+
unittest2,
6+
../nimbus/core/chain,
7+
../nimbus/core/tx_pool,
8+
../nimbus/core/casper,
9+
../nimbus/db/accounts_cache,
10+
../nimbus/utils/[eof, utils],
11+
../nimbus/evm/interpreter/op_codes,
12+
../nimbus/[common, config, transaction],
13+
./test_txpool/helpers
14+
15+
const
16+
baseDir = [".", "tests"]
17+
repoDir = [".", "customgenesis"]
18+
genesisFile = "eof.json"
19+
20+
type
21+
TestEnv = object
22+
nonce : uint64
23+
chainId : ChainId
24+
vaultKey: PrivateKey
25+
conf : NimbusConf
26+
com : CommonRef
27+
chain : ChainRef
28+
xp : TxPoolRef
29+
30+
proc toAddress(x: string): EthAddress =
31+
hexToByteArray[20](x)
32+
33+
proc privKey(keyHex: string): PrivateKey =
34+
let kRes = PrivateKey.fromHex(keyHex)
35+
if kRes.isErr:
36+
echo kRes.error
37+
quit(QuitFailure)
38+
39+
kRes.get()
40+
41+
proc toAddress(key: PrivateKey): EthAddress =
42+
let pubKey = key.toPublicKey
43+
pubKey.toCanonicalAddress
44+
45+
func eth(n: int): UInt256 =
46+
n.u256 * pow(10.u256, 18)
47+
48+
proc fm(input, output, max: int): FunctionMetadata =
49+
FunctionMetadata(input: input.uint8,
50+
output: output.uint8, maxStackHeight: max.uint16)
51+
52+
const
53+
createDeployer = [
54+
byte(CALLDATASIZE), # size
55+
byte(PUSH1), 0x00, # offset
56+
byte(PUSH1), 0x00, # dst
57+
byte(CALLDATACOPY),
58+
byte(CALLDATASIZE), # len
59+
byte(PUSH1), 0x00, # offset
60+
byte(PUSH1), 0x00, # value
61+
byte(CREATE),
62+
]
63+
64+
create2Deployer = [
65+
byte(CALLDATASIZE), # len
66+
byte(PUSH1), 0x00, # offset
67+
byte(PUSH1), 0x00, # dst
68+
byte(CALLDATACOPY),
69+
byte(PUSH1), 0x00, # salt
70+
byte(CALLDATASIZE), # len
71+
byte(PUSH1), 0x00, # offset
72+
byte(PUSH1), 0x00, # value
73+
byte(CREATE2),
74+
]
75+
76+
aa = toAddress("0x000000000000000000000000000000000000aaaa")
77+
bb = toAddress("0x000000000000000000000000000000000000bbbb")
78+
cc = toAddress("0x000000000000000000000000000000000000cccc")
79+
funds = 1.eth
80+
vaultKeyHex = "b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291"
81+
82+
proc acc(code: openArray[byte]): GenesisAccount =
83+
GenesisAccount(code: @code)
84+
85+
proc acc(balance: UInt256): GenesisAccount =
86+
GenesisAccount(balance: balance)
87+
88+
proc makeCode(): seq[byte] =
89+
var c: Container
90+
c.types = @[
91+
fm(0, 0, 0),
92+
fm(0, 0, 2),
93+
fm(0, 0, 0),
94+
fm(0, 0, 2)
95+
]
96+
97+
c.code = @[@[
98+
byte(CALLF),
99+
byte(0),
100+
byte(1),
101+
byte(CALLF),
102+
byte(0),
103+
byte(2),
104+
byte(STOP),
105+
], @[
106+
byte(PUSH1),
107+
byte(2),
108+
byte(RJUMP), # skip first flag
109+
byte(0),
110+
byte(5),
111+
112+
byte(PUSH1),
113+
byte(1),
114+
byte(PUSH1),
115+
byte(0),
116+
byte(SSTORE), # set first flag
117+
118+
byte(PUSH1),
119+
byte(1),
120+
byte(SWAP1),
121+
byte(SUB),
122+
byte(DUP1),
123+
byte(RJUMPI), # jump to first flag, then don't branch
124+
byte(0xff),
125+
byte(0xF3), # -13
126+
127+
byte(PUSH1),
128+
byte(1),
129+
byte(PUSH1),
130+
byte(1),
131+
byte(SSTORE), # set second flag
132+
byte(RETF),
133+
], @[
134+
byte(PUSH1),
135+
byte(1),
136+
byte(PUSH1),
137+
byte(2),
138+
byte(SSTORE), # set third flag
139+
140+
byte(CALLF),
141+
byte(0),
142+
byte(3),
143+
byte(RETF),
144+
], @[
145+
byte(PUSH1),
146+
byte(0),
147+
byte(RJUMPV), # jump over invalid op
148+
byte(1),
149+
byte(0),
150+
byte(1),
151+
152+
byte(INVALID),
153+
154+
byte(PUSH1),
155+
byte(1),
156+
byte(PUSH1),
157+
byte(3),
158+
byte(SSTORE), # set forth flag
159+
byte(RETF),
160+
]]
161+
162+
c.encode()
163+
164+
proc preAlloc(address: EthAddress): GenesisAlloc =
165+
result[address] = acc(funds)
166+
result[bb] = acc(createDeployer)
167+
result[cc] = acc(create2Deployer)
168+
result[aa] = acc(makeCode())
169+
170+
proc initDeployCode(): seq[byte] =
171+
let c = Container(
172+
types: @[fm(0, 0, 0)],
173+
code : @[@[byte(STOP)]],
174+
)
175+
c.encode()
176+
177+
proc initInitCode(deployCode: openArray[byte]): seq[byte] =
178+
result = @[
179+
byte(PUSH1), byte(deployCode.len), # len
180+
byte(PUSH1), 0x0c, # offset
181+
byte(PUSH1), 0x00, # dst offset
182+
byte(CODECOPY),
183+
184+
# code in memory
185+
byte(PUSH1), byte(deployCode.len), # size
186+
byte(PUSH1), 0x00, # offset
187+
byte(RETURN),
188+
]
189+
result.add deployCode
190+
191+
func gwei(n: uint64): GasInt {.compileTime.} =
192+
GasInt(n * (10'u64 ^ 9'u64))
193+
194+
proc makeTx*(t: var TestEnv,
195+
recipient: Option[EthAddress],
196+
amount: UInt256,
197+
payload: openArray[byte] = []): Transaction =
198+
const
199+
gasLimit = 500000.GasInt
200+
gasFeeCap = 5.gwei
201+
gasTipCap = 2.GasInt
202+
203+
let tx = Transaction(
204+
txType : TxEip1559,
205+
chainId : t.chainId,
206+
nonce : AccountNonce(t.nonce),
207+
gasLimit: gasLimit,
208+
maxPriorityFee: gasTipCap,
209+
maxFee : gasFeeCap,
210+
to : recipient,
211+
value : amount,
212+
payload : @payload
213+
)
214+
215+
inc t.nonce
216+
signTransaction(tx, t.vaultKey, t.chainId, eip155 = true)
217+
218+
proc initEnv(): TestEnv =
219+
let
220+
signKey = privKey(vaultKeyHex)
221+
address = toAddress(signKey)
222+
223+
var
224+
conf = makeConfig(@[
225+
"--engine-signer:" & address.toHex,
226+
"--custom-network:" & genesisFile.findFilePath(baseDir,repoDir).value
227+
])
228+
229+
conf.networkParams.genesis.alloc = preAlloc(address)
230+
231+
let
232+
com = CommonRef.new(
233+
newMemoryDb(),
234+
conf.pruneMode == PruneMode.Full,
235+
conf.networkId,
236+
conf.networkParams
237+
)
238+
chain = newChain(com)
239+
240+
com.initializeEmptyDb()
241+
242+
result = TestEnv(
243+
conf: conf,
244+
com: com,
245+
chain: chain,
246+
xp: TxPoolRef.new(com, conf.engineSigner),
247+
vaultKey: signKey,
248+
chainId: conf.networkParams.config.chainId,
249+
nonce: 0'u64
250+
)
251+
252+
const
253+
prevRandao = EMPTY_UNCLE_HASH # it can be any valid hash
254+
255+
proc eofMain*() =
256+
var
257+
env = initEnv()
258+
txs: seq[Transaction]
259+
stateRoot: Hash256
260+
261+
let
262+
deployCode = initDeployCode()
263+
initCode = initInitCode(deployCode)
264+
xp = env.xp
265+
com = env.com
266+
chain = env.chain
267+
268+
# execute flag contract
269+
txs.add env.makeTx(some(aa), 0.u256)
270+
271+
# deploy eof contract from eoa
272+
txs.add env.makeTx(none(EthAddress), 0.u256, initCode)
273+
274+
# deploy eof contract from create contract
275+
txs.add env.makeTx(some(bb), 0.u256, initCode)
276+
277+
# deploy eof contract from create2 contract
278+
txs.add env.makeTx(some(cc), 0.u256, initCode)
279+
280+
suite "Test EOF code deployment":
281+
test "add txs to txpool":
282+
for tx in txs:
283+
let res = xp.addLocal(tx, force = true)
284+
check res.isOk
285+
if res.isErr:
286+
debugEcho res.error
287+
return
288+
289+
# all txs accepted in txpool
290+
check xp.nItems.total == 4
291+
292+
test "generate POS block":
293+
com.pos.prevRandao = prevRandao
294+
com.pos.feeRecipient = aa
295+
com.pos.timestamp = getTime()
296+
297+
let blk = xp.ethBlock()
298+
check com.isBlockAfterTtd(blk.header)
299+
300+
let body = BlockBody(
301+
transactions: blk.txs,
302+
uncles: blk.uncles
303+
)
304+
check blk.txs.len == 4
305+
306+
let rr = chain.persistBlocks([blk.header], [body])
307+
check rr == ValidationResult.OK
308+
309+
# save stateRoot for next test
310+
stateRoot = blk.header.stateRoot
311+
312+
test "check flags and various deployment mechanisms":
313+
var state = AccountsCache.init(
314+
com.db.db,
315+
stateRoot,
316+
com.pruneTrie)
317+
318+
# check flags
319+
for i in 0 ..< 4:
320+
let val = state.getStorage(aa, i.u256)
321+
check val == 1.u256
322+
323+
# deploy EOF with EOA
324+
let address = toAddress(env.vaultKey)
325+
var code = state.getCode(generateAddress(address, 1))
326+
check code == deployCode
327+
328+
# deploy EOF with CREATE
329+
code = state.getCode(generateAddress(bb, 0))
330+
check code == deployCode
331+
332+
# deploy EOF with CREATE2
333+
let xx = generateSafeAddress(cc, ZERO_CONTRACTSALT, initCode)
334+
code = state.getCode(xx)
335+
check code == deployCode
336+
337+
when isMainModule:
338+
eofMain()

0 commit comments

Comments
 (0)