Skip to content

Commit

Permalink
add EOF V1 code deployment test case
Browse files Browse the repository at this point in the history
  • Loading branch information
jangko committed Oct 31, 2023
1 parent f83aa02 commit 1720af4
Show file tree
Hide file tree
Showing 3 changed files with 371 additions and 1 deletion.
3 changes: 2 additions & 1 deletion tests/all_tests.nim
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,5 @@ cliBuilder:
./test_txpool,
./test_merge,
./test_eip4844,
./test_beacon/test_skeleton
./test_beacon/test_skeleton,
./test_eof
31 changes: 31 additions & 0 deletions tests/customgenesis/eof.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"config": {
"chainId": 7,
"homesteadBlock": 0,
"eip150Block": 0,
"eip150Hash": "0x5de1ee4135274003348e80b788e5afa4b18b18d320a5622218d5c493fedf5689",
"eip155Block": 0,
"eip158Block": 0,
"byzantiumBlock": 0,
"constantinopleBlock": 0,
"petersburgBlock": 0,
"istanbulBlock": 0,
"muirGlacierBlock": 0,
"berlinBlock": 0,
"londonBlock": 0,
"mergeForkBlock": 0,
"shanghaiBlock": 0,
"cancunBlock": 0,
"terminalTotalDifficulty": 0
},
"genesis": {
"coinbase": "0x0000000000000000000000000000000000000000",
"difficulty": "0x30000",
"extraData": "0x0000000000000000000000000000000000000000000000000000000000000000658bdf435d810c91414ec09147daa6db624063790000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"gasLimit": "0x2fefd8",
"nonce": "0x0000000000000000",
"timestamp": "0x1234",
"alloc": {
}
}
}
338 changes: 338 additions & 0 deletions tests/test_eof.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,338 @@
import
std/[tables, math, times],
eth/[keys],
stew/byteutils,
unittest2,
../nimbus/core/chain,
../nimbus/core/tx_pool,
../nimbus/core/casper,
../nimbus/db/accounts_cache,
../nimbus/utils/[eof, utils],
../nimbus/evm/interpreter/op_codes,
../nimbus/[common, config, transaction],
./test_txpool/helpers

const
baseDir = [".", "tests"]
repoDir = [".", "customgenesis"]
genesisFile = "eof.json"

type
TestEnv = object
nonce : uint64
chainId : ChainId
vaultKey: PrivateKey
conf : NimbusConf
com : CommonRef
chain : ChainRef
xp : TxPoolRef

proc toAddress(x: string): EthAddress =
hexToByteArray[20](x)

proc privKey(keyHex: string): PrivateKey =
let kRes = PrivateKey.fromHex(keyHex)
if kRes.isErr:
echo kRes.error
quit(QuitFailure)

kRes.get()

proc toAddress(key: PrivateKey): EthAddress =
let pubKey = key.toPublicKey
pubKey.toCanonicalAddress

func eth(n: int): UInt256 =
n.u256 * pow(10.u256, 18)

proc fm(input, output, max: int): FunctionMetadata =
FunctionMetadata(input: input.uint8,
output: output.uint8, maxStackHeight: max.uint16)

const
createDeployer = [
byte(CALLDATASIZE), # size
byte(PUSH1), 0x00, # offset
byte(PUSH1), 0x00, # dst
byte(CALLDATACOPY),
byte(CALLDATASIZE), # len
byte(PUSH1), 0x00, # offset
byte(PUSH1), 0x00, # value
byte(CREATE),
]

create2Deployer = [
byte(CALLDATASIZE), # len
byte(PUSH1), 0x00, # offset
byte(PUSH1), 0x00, # dst
byte(CALLDATACOPY),
byte(PUSH1), 0x00, # salt
byte(CALLDATASIZE), # len
byte(PUSH1), 0x00, # offset
byte(PUSH1), 0x00, # value
byte(CREATE2),
]

aa = toAddress("0x000000000000000000000000000000000000aaaa")
bb = toAddress("0x000000000000000000000000000000000000bbbb")
cc = toAddress("0x000000000000000000000000000000000000cccc")
funds = 1.eth
vaultKeyHex = "b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291"

proc acc(code: openArray[byte]): GenesisAccount =
GenesisAccount(code: @code)

proc acc(balance: UInt256): GenesisAccount =
GenesisAccount(balance: balance)

proc makeCode(): seq[byte] =
var c: Container
c.types = @[
fm(0, 0, 0),
fm(0, 0, 2),
fm(0, 0, 0),
fm(0, 0, 2)
]

c.code = @[@[
byte(CALLF),
byte(0),
byte(1),
byte(CALLF),
byte(0),
byte(2),
byte(STOP),
], @[
byte(PUSH1),
byte(2),
byte(RJUMP), # skip first flag
byte(0),
byte(5),

byte(PUSH1),
byte(1),
byte(PUSH1),
byte(0),
byte(SSTORE), # set first flag

byte(PUSH1),
byte(1),
byte(SWAP1),
byte(SUB),
byte(DUP1),
byte(RJUMPI), # jump to first flag, then don't branch
byte(0xff),
byte(0xF3), # -13

byte(PUSH1),
byte(1),
byte(PUSH1),
byte(1),
byte(SSTORE), # set second flag
byte(RETF),
], @[
byte(PUSH1),
byte(1),
byte(PUSH1),
byte(2),
byte(SSTORE), # set third flag

byte(CALLF),
byte(0),
byte(3),
byte(RETF),
], @[
byte(PUSH1),
byte(0),
byte(RJUMPV), # jump over invalid op
byte(1),
byte(0),
byte(1),

byte(INVALID),

byte(PUSH1),
byte(1),
byte(PUSH1),
byte(3),
byte(SSTORE), # set forth flag
byte(RETF),
]]

c.encode()

proc preAlloc(address: EthAddress): GenesisAlloc =
result[address] = acc(funds)
result[bb] = acc(createDeployer)
result[cc] = acc(create2Deployer)
result[aa] = acc(makeCode())

proc initDeployCode(): seq[byte] =
let c = Container(
types: @[fm(0, 0, 0)],
code : @[@[byte(STOP)]],
)
c.encode()

proc initInitCode(deployCode: openArray[byte]): seq[byte] =
result = @[
byte(PUSH1), byte(deployCode.len), # len
byte(PUSH1), 0x0c, # offset
byte(PUSH1), 0x00, # dst offset
byte(CODECOPY),

# code in memory
byte(PUSH1), byte(deployCode.len), # size
byte(PUSH1), 0x00, # offset
byte(RETURN),
]
result.add deployCode

func gwei(n: uint64): GasInt {.compileTime.} =
GasInt(n * (10'u64 ^ 9'u64))

proc makeTx*(t: var TestEnv,
recipient: Option[EthAddress],
amount: UInt256,
payload: openArray[byte] = []): Transaction =
const
gasLimit = 500000.GasInt
gasFeeCap = 5.gwei
gasTipCap = 2.GasInt

let tx = Transaction(
txType : TxEip1559,
chainId : t.chainId,
nonce : AccountNonce(t.nonce),
gasLimit: gasLimit,
maxPriorityFee: gasTipCap,
maxFee : gasFeeCap,
to : recipient,
value : amount,
payload : @payload
)

inc t.nonce
signTransaction(tx, t.vaultKey, t.chainId, eip155 = true)

proc initEnv(): TestEnv =
let
signKey = privKey(vaultKeyHex)
address = toAddress(signKey)

var
conf = makeConfig(@[
"--engine-signer:" & address.toHex,
"--custom-network:" & genesisFile.findFilePath(baseDir,repoDir).value
])

conf.networkParams.genesis.alloc = preAlloc(address)

let
com = CommonRef.new(
newMemoryDb(),
conf.pruneMode == PruneMode.Full,
conf.networkId,
conf.networkParams
)
chain = newChain(com)

com.initializeEmptyDb()

result = TestEnv(
conf: conf,
com: com,
chain: chain,
xp: TxPoolRef.new(com, conf.engineSigner),
vaultKey: signKey,
chainId: conf.networkParams.config.chainId,
nonce: 0'u64
)

const
prevRandao = EMPTY_UNCLE_HASH # it can be any valid hash

proc eofMain*() =
var
env = initEnv()
txs: seq[Transaction]
stateRoot: Hash256

let
deployCode = initDeployCode()
initCode = initInitCode(deployCode)
xp = env.xp
com = env.com
chain = env.chain

# execute flag contract
txs.add env.makeTx(some(aa), 0.u256)

# deploy eof contract from eoa
txs.add env.makeTx(none(EthAddress), 0.u256, initCode)

# deploy eof contract from create contract
txs.add env.makeTx(some(bb), 0.u256, initCode)

# deploy eof contract from create2 contract
txs.add env.makeTx(some(cc), 0.u256, initCode)

suite "Test EOF code deployment":
test "add txs to txpool":
for tx in txs:
let res = xp.addLocal(tx, force = true)
check res.isOk
if res.isErr:
debugEcho res.error
return

# all txs accepted in txpool
check xp.nItems.total == 4

test "generate POS block":
com.pos.prevRandao = prevRandao
com.pos.feeRecipient = aa
com.pos.timestamp = getTime()

let blk = xp.ethBlock()
check com.isBlockAfterTtd(blk.header)

let body = BlockBody(
transactions: blk.txs,
uncles: blk.uncles
)
check blk.txs.len == 4

let rr = chain.persistBlocks([blk.header], [body])
check rr == ValidationResult.OK

# save stateRoot for next test
stateRoot = blk.header.stateRoot

test "check flags and various deployment mechanisms":
var state = AccountsCache.init(
com.db.db,
stateRoot,
com.pruneTrie)

# check flags
for i in 0 ..< 4:
let val = state.getStorage(aa, i.u256)
check val == 1.u256

# deploy EOF with EOA
let address = toAddress(env.vaultKey)
var code = state.getCode(generateAddress(address, 1))
check code == deployCode

# deploy EOF with CREATE
code = state.getCode(generateAddress(bb, 0))
check code == deployCode

# deploy EOF with CREATE2
let xx = generateSafeAddress(cc, ZERO_CONTRACTSALT, initCode)
code = state.getCode(xx)
check code == deployCode

when isMainModule:
eofMain()

0 comments on commit 1720af4

Please sign in to comment.