Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[1.0] integration test for evmtx order #321

Merged
merged 5 commits into from
Nov 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,6 @@ configure_file(defertest.wasm . COPYONLY)
configure_file(defertest.abi . COPYONLY)
configure_file(defertest2.wasm . COPYONLY)
configure_file(defertest2.abi . COPYONLY)
configure_file(evmbridge.wasm . COPYONLY)
configure_file(evmbridge.abi . COPYONLY)

64 changes: 64 additions & 0 deletions tests/evmbridge.abi
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
{
"____comment": "This file was generated with eosio-abigen. DO NOT EDIT ",
"version": "eosio::abi/1.2",
"types": [
{
"new_type_name": "bridge_message",
"type": "variant_bridge_message_v0"
}
],
"structs": [
{
"name": "bridge_message_v0",
"base": "",
"fields": [
{
"name": "receiver",
"type": "name"
},
{
"name": "sender",
"type": "bytes"
},
{
"name": "timestamp",
"type": "time_point"
},
{
"name": "value",
"type": "bytes"
},
{
"name": "data",
"type": "bytes"
}
]
},
{
"name": "onbridgemsg",
"base": "",
"fields": [
{
"name": "message",
"type": "bridge_message"
}
]
}
],
"actions": [
{
"name": "onbridgemsg",
"type": "onbridgemsg",
"ricardian_contract": ""
}
],
"tables": [],
"ricardian_clauses": [],
"variants": [
{
"name": "variant_bridge_message_v0",
"types": ["bridge_message_v0"]
}
],
"action_results": []
}
69 changes: 69 additions & 0 deletions tests/evmbridge.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#include <vector>
#include <variant>
#include <eosio/eosio.hpp>
#include <eosio/contract.hpp>

using namespace eosio;
using namespace std;

extern "C" {
__attribute__((eosio_wasm_import))
void set_action_return_value(void*, size_t);

__attribute__((eosio_wasm_import))
uint64_t get_sender();
}

typedef std::vector<char> bytes;

struct bridge_message_v0 {
name receiver;
bytes sender;
time_point timestamp;
bytes value;
bytes data;

EOSLIB_SERIALIZE(bridge_message_v0, (receiver)(sender)(timestamp)(value)(data));
};

using bridge_message = std::variant<bridge_message_v0>;

class [[eosio::contract("evmbridge")]] evmbridge : public contract {
public:
using contract::contract;
[[eosio::action]] void onbridgemsg(const bridge_message& message);


class contract_actions {
public:
void call(eosio::name from, const bytes &to, const bytes& value, const bytes &data, uint64_t gas_limit);
void assertnonce(eosio::name account, uint64_t next_nonce);
};

using call_action = action_wrapper<"call"_n, &contract_actions::call>;
};

void evmbridge::onbridgemsg(const bridge_message& message) {
const bridge_message_v0 &msg = std::get<bridge_message_v0>(message);
const char method[4] = {'\x00','\x8f','\xcf','\x3e'}; // function assertdata(uint256) payable

uint8_t value_buffer[32] = {};

if (msg.data.size() >= 32) {
std::copy(msg.data.end() - 32, msg.data.end(), value_buffer);
}

bytes call_data;
call_data.reserve(4 + 32);
call_data.insert(call_data.end(), method, method + 4);
call_data.insert(call_data.end(), value_buffer, value_buffer + 32);

call_action call_act("eosio.evm"_n, {{get_self(), "active"_n}});

bytes value;
value.resize(32, 0);
value[31] = 100;

call_act.send(get_self() /*from*/, msg.sender /*to*/, value /*value*/, call_data /*data*/, 100000 /*gas_limit*/);
}

Binary file added tests/evmbridge.wasm
Binary file not shown.
93 changes: 91 additions & 2 deletions tests/nodeos_eos_evm_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,18 @@ def prefix_0x(hexstr):
evmRPCPOpen = None
eosEvmMinerPOpen = None

def assert_contract_exist(contract_addr):
Utils.Print("ensure contract {0} exist".format(contract_addr))
if contract_addr[:2] == '0x':
contract_addr = contract_addr[2:]
rows=prodNode.getTable(evmAcc.name, evmAcc.name, "account")
for row in rows['rows']:
if (str(contract_addr) == str(row['eth_address'])):
assert row['code_id'] is not None, "contract {0} should exist".format(contract_addr)
return True
Utils.Print("evm account table rows: " + json.dumps(rows))
assert False, "contract {0} should exist".format(contract_addr)

def interact_with_storage_contract(dest, nonce):
for i in range(1, 5): # execute a few
Utils.Print("Execute ETH contract")
Expand Down Expand Up @@ -291,7 +303,7 @@ def toDict(dictToParse):
prodNode = cluster.getNode(0)
nonProdNode = cluster.getNode(1)

accounts=createAccountKeys(6)
accounts=createAccountKeys(7)
if accounts is None:
Utils.errorExit("FAILURE - create keys")

Expand All @@ -303,10 +315,13 @@ def toDict(dictToParse):
defertest2Acc = accounts[4]
aliceAcc = accounts[5]

accounts[6].name = "evmbridge"
evmbridgeAcc = accounts[6]

testWalletName="test"

Print("Creating wallet \"%s\"." % (testWalletName))
testWallet=walletMgr.create(testWalletName, [cluster.eosioAccount,accounts[0],accounts[1],accounts[2],accounts[3],accounts[4]])
testWallet=walletMgr.create(testWalletName, [cluster.eosioAccount,accounts[0],accounts[1],accounts[2],accounts[3],accounts[4],accounts[5],accounts[6]])

addys = {
"0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266":"0x038318535b54105d4a7aae60c08fc45f9687181b4fdfc625bd1a753fa7397fed75,0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"
Expand Down Expand Up @@ -356,6 +371,15 @@ def toDict(dictToParse):
Utils.Print(f"Publish defertest2 contract {contractDir}/{wasmFile} to account {defertest2Acc}")
prodNode.publishContract(defertest2Acc, contractDir, wasmFile, abiFile, waitForTransBlock=True)

contractDir=eosEvmBuildRoot + "/tests"
wasmFile="evmbridge.wasm"
abiFile="evmbridge.abi"
Utils.Print(f"Publish evmbridge contract {contractDir}/{wasmFile} to account {evmbridgeAcc}")
prodNode.publishContract(evmbridgeAcc, contractDir, wasmFile, abiFile, waitForTransBlock=True)
# add eosio.code evmbridge
cmd="set account permission evmbridge active --add-code -p evmbridge@active"
prodNode.processCleosCmd(cmd, cmd, silentErrors=True, returnType=ReturnType.raw)

# add eosio.code permission to defertest2 account
cmd="set account permission " + defertest2Acc.name + " active --add-code -p " + defertest2Acc.name + "@active"
prodNode.processCleosCmd(cmd, cmd, silentErrors=False, returnType=ReturnType.raw)
Expand Down Expand Up @@ -527,6 +551,7 @@ def toDict(dictToParse):
actData = {"miner":minerAcc.name, "rlptx":Web3.to_hex(get_raw_transaction(signed_trx))[2:]}
retValue = prodNode.pushMessage(evmAcc.name, "pushtx", json.dumps(actData), '-p {0}'.format(minerAcc.name), silentErrors=True)
assert retValue[0], f"push trx should have succeeded: {retValue}"
assert_contract_exist(makeContractAddress(fromAdd, nonce))
nonce = interact_with_storage_contract(makeContractAddress(fromAdd, nonce), nonce)

if genesisJson[0] != '/': genesisJson = os.path.realpath(genesisJson)
Expand Down Expand Up @@ -820,6 +845,7 @@ def toDict(dictToParse):


# Launch eos-evm-node
Utils.Print("===== laucnhing eos-evm-node & eos-evm-rpc =====")
dataDir = Utils.DataDir + "eos_evm"
nodeStdOutDir = dataDir + "/eos-evm-node.stdout"
nodeStdErrDir = dataDir + "/eos-evm-node.stderr"
Expand Down Expand Up @@ -901,6 +927,69 @@ def get_block(num):
nonProdNode.transferFunds(cluster.eosioAccount, evmAcc, "111.0000 EOS", "0xB106D2C286183FFC3D1F0C4A6f0753bB20B407c2", waitForTransBlock=True)
time.sleep(2)

### evmtx event order test
Utils.Print("Test evmtx event order via evmbridge contract")
# // SPDX-License-Identifier: GPL-3.0
# pragma solidity >=0.7.0 <0.9.0;
# contract BridgeTest {
# uint256 number;
# constructor() {
# number = 41;
# }
# function assertdata(uint256 expect) payable public {
# require(number == expect, "assertdata failed");
# number = number + 1;
# }
# function sendbridgemsg() payable public {
# number = number + 1;
# bytes memory receiver_msg = abi.encodeWithSignature("test(uint256)", number);
# address evmaddr = 0xbBBBbBbbbBBBBbbbbbbBBbBB5530EA015b900000;//eosio.evm
# (bool success, ) = evmaddr.call{value: msg.value}(
# abi.encodeWithSignature("bridgeMsgV0(string,bool,bytes)", "evmbridge", true, receiver_msg ));
# if(!success) { revert(); }
# }
# }
#
nonce += 1
evmChainId = 15555
gasP = getGasPrice()
signed_trx = w3.eth.account.sign_transaction(dict(
nonce=nonce,
gas=5000000,
gasPrice=gasP,
data=Web3.to_bytes(hexstr='608060405234801561001057600080fd5b5060296000819055506105b2806100286000396000f3fe6080604052600436106100285760003560e01c80628fcf3e1461002d57806386bf4eff14610049575b600080fd5b610047600480360381019061004291906102b8565b610053565b005b6100516100af565b005b8060005414610097576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161008e90610342565b60405180910390fd5b60016000546100a69190610391565b60008190555050565b60016000546100be9190610391565b600081905550600080546040516024016100d891906103d4565b6040516020818303038152906040527f29e99f07000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050509050600073bbbbbbbbbbbbbbbbbbbbbbbb5530ea015b900000905060008173ffffffffffffffffffffffffffffffffffffffff163460018560405160240161019e9291906104e6565b6040516020818303038152906040527ff781185b000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040516102289190610565565b60006040518083038185875af1925050503d8060008114610265576040519150601f19603f3d011682016040523d82523d6000602084013e61026a565b606091505b505090508061027857600080fd5b505050565b600080fd5b6000819050919050565b61029581610282565b81146102a057600080fd5b50565b6000813590506102b28161028c565b92915050565b6000602082840312156102ce576102cd61027d565b5b60006102dc848285016102a3565b91505092915050565b600082825260208201905092915050565b7f61737365727464617461206661696c6564000000000000000000000000000000600082015250565b600061032c6011836102e5565b9150610337826102f6565b602082019050919050565b6000602082019050818103600083015261035b8161031f565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061039c82610282565b91506103a783610282565b92508282019050808211156103bf576103be610362565b5b92915050565b6103ce81610282565b82525050565b60006020820190506103e960008301846103c5565b92915050565b7f65766d6272696467650000000000000000000000000000000000000000000000600082015250565b60006104256009836102e5565b9150610430826103ef565b602082019050919050565b60008115159050919050565b6104508161043b565b82525050565b600081519050919050565b600082825260208201905092915050565b60005b83811015610490578082015181840152602081019050610475565b60008484015250505050565b6000601f19601f8301169050919050565b60006104b882610456565b6104c28185610461565b93506104d2818560208601610472565b6104db8161049c565b840191505092915050565b600060608201905081810360008301526104ff81610418565b905061050e6020830185610447565b818103604083015261052081846104ad565b90509392505050565b600081905092915050565b600061053f82610456565b6105498185610529565b9350610559818560208601610472565b80840191505092915050565b60006105718284610534565b91508190509291505056fea2646970667358221220ac18e6f72606f415174ea5fa2cf02da58e2ec7af6b59282e166efa50f79aef3164736f6c63430008120033'),
chainId=evmChainId
), evmSendKey)
actData = {"miner":minerAcc.name, "rlptx":Web3.to_hex(get_raw_transaction(signed_trx))[2:]}
retValue = prodNode.pushMessage(evmAcc.name, "pushtx", json.dumps(actData), '-p {0}'.format(minerAcc.name), silentErrors=True)
assert retValue[0], f"push trx should have succeeded: {retValue}"
bridgemsgcontractaddr = makeContractAddress("9E126C57330FA71556628e0aabd6B6B6783d99fA", nonce)
assert_contract_exist(bridgemsgcontractaddr)
Utils.Print("bridge msg contract addr is:" + str(bridgemsgcontractaddr))
row4=prodNode.getTableRow(evmAcc.name, evmAcc.name, "account", 4) # 4th balance of this integration test
Utils.Print("\taccount row4: ", row4)

Utils.Print("call bridgereg in evm runtime contract for account evmbridge")
prodNode.pushMessage(evmAcc.name, "bridgereg", '["evmbridge","evmbridge","1.0000 EOS"]', '-p {0} -p evmbridge'.format(evmAcc.name), silentErrors=False)

Utils.Print("push EVM trx to trigger bridgemsg from EVM to notify evmbridge account")
amount=1.0000
nonce += 1
signed_trx = w3.eth.account.sign_transaction(dict(
nonce=nonce,
gas=100000,
gasPrice=gasP,
to=Web3.to_checksum_address(bridgemsgcontractaddr),
data=Web3.to_bytes(hexstr='86bf4eff'), #function sendbridgemsg()
value=int(amount*10000*szabo*100),
chainId=evmChainId
), evmSendKey)
actData = {"miner":minerAcc.name, "rlptx":Web3.to_hex(get_raw_transaction(signed_trx))[2:]}
retValue = prodNode.pushMessage(evmAcc.name, "pushtx", json.dumps(actData), '-p {0}'.format(minerAcc.name), silentErrors=False)
assert retValue[0], f"push trx to bridge msg contract should have succeeded: {retValue}"
row4=prodNode.getTableRow(evmAcc.name, evmAcc.name, "account", 4) # 4th balance of this integration test
Utils.Print("\taccount row4: ", row4)

# update gas parameter
Utils.Print("Update gas parameter: ram price = 100 EOS per MB, gas price = 900Gwei")
trans = prodNode.pushMessage(evmAcc.name, "updtgasparam", json.dumps({"ram_price_mb":"100.0000 EOS","gas_price":900000000000}), '-p {0}'.format(evmAcc.name), silentErrors=False)
Expand Down
Loading