diff --git a/tests/all_tests.nim b/tests/all_tests.nim index 8be661288f..23301f77ed 100644 --- a/tests/all_tests.nim +++ b/tests/all_tests.nim @@ -46,4 +46,5 @@ cliBuilder: ./test_configuration, ./test_keyed_queue_rlp, # ./test_txpool, - ./test_merge + ./test_merge, + ./test_eof diff --git a/tests/customgenesis/eof.json b/tests/customgenesis/eof.json new file mode 100644 index 0000000000..29257bd5d9 --- /dev/null +++ b/tests/customgenesis/eof.json @@ -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": { + } + } +} diff --git a/tests/test_eof.nim b/tests/test_eof.nim new file mode 100644 index 0000000000..6f278ca6fb --- /dev/null +++ b/tests/test_eof.nim @@ -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() diff --git a/tests/test_txpool/helpers.nim b/tests/test_txpool/helpers.nim index 1a56b3f21f..752dd0c420 100644 --- a/tests/test_txpool/helpers.nim +++ b/tests/test_txpool/helpers.nim @@ -39,7 +39,6 @@ export tx_packer.packerVmExec, tx_recover.recoverItem, tx_tabs.TxTabsRef, - tx_tabs.any, tx_tabs.decAccount, tx_tabs.dispose, tx_tabs.eq,