Skip to content

Commit

Permalink
Add handler option to bridge message feature.
Browse files Browse the repository at this point in the history
  • Loading branch information
yarkinwho committed Aug 31, 2023
1 parent c460b54 commit ea2eb46
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 16 deletions.
2 changes: 1 addition & 1 deletion include/evm_runtime/evm_contract.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ class [[eosio::contract]] evm_contract : public contract
[[eosio::action]] void call(eosio::name from, const bytes& to, const bytes& value, const bytes& data, uint64_t gas_limit);
[[eosio::action]] void admincall(const bytes& from, const bytes& to, const bytes& value, const bytes& data, uint64_t gas_limit);

[[eosio::action]] void bridgereg(eosio::name receiver, const eosio::asset& min_fee);
[[eosio::action]] void bridgereg(eosio::name receiver, eosio::name handler, const eosio::asset& min_fee);
[[eosio::action]] void bridgeunreg(eosio::name receiver);

#ifdef WITH_TEST_ACTIONS
Expand Down
3 changes: 2 additions & 1 deletion include/evm_runtime/tables.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ struct [[eosio::table]] [[eosio::contract("evm_contract")]] message_receiver {
};

name account;
name handler;
asset min_fee;
uint32_t flags;

Expand All @@ -197,7 +198,7 @@ struct [[eosio::table]] [[eosio::contract("evm_contract")]] message_receiver {
return (flags & f) != 0;
}

EOSLIB_SERIALIZE(message_receiver, (account)(min_fee)(flags));
EOSLIB_SERIALIZE(message_receiver, (account)(handler)(min_fee)(flags));
};

typedef eosio::multi_index<"msgreceiver"_n, message_receiver> message_receiver_table;
Expand Down
6 changes: 4 additions & 2 deletions tests/basic_evm_tester.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -333,10 +333,12 @@ void basic_evm_tester::admincall(const evmc::bytes& from, const evmc::bytes& to,
push_action(evm_account_name, "admincall"_n, actor, mvo()("from", from_bytes)("to", to_bytes)("value", value_bytes)("data", data_bytes)("gas_limit", gas_limit));
}

transaction_trace_ptr basic_evm_tester::bridgereg(name receiver, asset min_fee, vector<account_name> extra_signers) {
transaction_trace_ptr basic_evm_tester::bridgereg(name receiver, name handler, asset min_fee, vector<account_name> extra_signers) {
extra_signers.push_back(receiver);
if (receiver != handler)
extra_signers.push_back(handler);
return basic_evm_tester::push_action(evm_account_name, "bridgereg"_n, extra_signers,
mvo()("receiver", receiver)("min_fee", min_fee));
mvo()("receiver", receiver)("handler", handler)("min_fee", min_fee));
}

transaction_trace_ptr basic_evm_tester::bridgeunreg(name receiver) {
Expand Down
5 changes: 3 additions & 2 deletions tests/basic_evm_tester.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ struct exec_output {

struct message_receiver {
name account;
name handler;
asset min_fee;
uint32_t flags;
};
Expand Down Expand Up @@ -139,7 +140,7 @@ FC_REFLECT(evm_test::exec_input, (context)(from)(to)(data)(value))
FC_REFLECT(evm_test::exec_callback, (contract)(action))
FC_REFLECT(evm_test::exec_output, (status)(data)(context))

FC_REFLECT(evm_test::message_receiver, (account)(min_fee)(flags));
FC_REFLECT(evm_test::message_receiver, (account)(handler)(min_fee)(flags));
FC_REFLECT(evm_test::bridge_message_v0, (receiver)(sender)(timestamp)(value)(data));

namespace evm_test {
Expand Down Expand Up @@ -219,7 +220,7 @@ class basic_evm_tester : public testing::validating_tester
silkworm::Transaction
generate_tx(const evmc::address& to, const intx::uint256& value, uint64_t gas_limit = 21000) const;

transaction_trace_ptr bridgereg(name receiver, asset min_fee, vector<account_name> extra_signers={evm_account_name});
transaction_trace_ptr bridgereg(name receiver, name handler, asset min_fee, vector<account_name> extra_signers={evm_account_name});
transaction_trace_ptr bridgeunreg(name receiver);
transaction_trace_ptr exec(const exec_input& input, const std::optional<exec_callback>& callback);
transaction_trace_ptr pushtx(const silkworm::Transaction& trx, name miner = evm_account_name);
Expand Down
104 changes: 94 additions & 10 deletions tests/bridge_message_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,26 +81,26 @@ BOOST_FIXTURE_TEST_CASE(bridge_register_test, bridge_message_tester) try {
create_accounts({"rec1"_n});

// evm auth is needed
BOOST_REQUIRE_EXCEPTION(bridgereg("rec1"_n, make_asset(-1), {}),
BOOST_REQUIRE_EXCEPTION(bridgereg("rec1"_n, "rec1"_n, make_asset(-1), {}),
missing_auth_exception, [](const missing_auth_exception& e) { return expect_assert_message(e, "missing authority of evm"); });

// min fee only accepts EOS
BOOST_REQUIRE_EXCEPTION(bridgereg("rec1"_n, asset::from_string("1.0000 TST")),
BOOST_REQUIRE_EXCEPTION(bridgereg("rec1"_n, "rec1"_n, asset::from_string("1.0000 TST")),
eosio_assert_message_exception, eosio_assert_message_is("unexpected symbol"));

// min fee must be non-negative
BOOST_REQUIRE_EXCEPTION(bridgereg("rec1"_n, make_asset(-1)),
BOOST_REQUIRE_EXCEPTION(bridgereg("rec1"_n, "rec1"_n, make_asset(-1)),
eosio_assert_message_exception, eosio_assert_message_is("min_fee cannot be negative"));

bridgereg("rec1"_n, make_asset(0));
bridgereg("rec1"_n, "rec1"_n, make_asset(0));

auto row = fc::raw::unpack<message_receiver>(get_row_by_account( evm_account_name, evm_account_name, "msgreceiver"_n, "rec1"_n));
BOOST_REQUIRE(row.account == "rec1"_n);
BOOST_REQUIRE(row.min_fee == make_asset(0));
BOOST_REQUIRE(row.flags == 0x1);

// Register again changing min fee
bridgereg("rec1"_n, make_asset(1));
bridgereg("rec1"_n, "rec1"_n, make_asset(1));

row = fc::raw::unpack<message_receiver>(get_row_by_account( evm_account_name, evm_account_name, "msgreceiver"_n, "rec1"_n));
BOOST_REQUIRE(row.account == "rec1"_n);
Expand All @@ -127,7 +127,7 @@ BOOST_FIXTURE_TEST_CASE(basic_tests, bridge_message_tester) try {
set_abi("rec1"_n, testing::contracts::evm_bridge_receiver_abi().data());

// Register rec1 with 1000.0000 EOS as min_fee
bridgereg("rec1"_n, make_asset(1000'0000));
bridgereg("rec1"_n, "rec1"_n, make_asset(1000'0000));

// Fund evm1 address with 1001 EOS
evm_eoa evm1;
Expand Down Expand Up @@ -235,7 +235,7 @@ BOOST_FIXTURE_TEST_CASE(test_bridge_errors, bridge_message_tester) try {
evm1.next_nonce--;

// Register abcd
bridgereg("abcd"_n, make_asset(1));
bridgereg("abcd"_n, "abcd"_n, make_asset(1));

// min fee not covered
BOOST_REQUIRE_EXCEPTION(send_raw_message(evm1, emv_reserved_address, 0,
Expand All @@ -253,7 +253,7 @@ BOOST_FIXTURE_TEST_CASE(test_bridge_errors, bridge_message_tester) try {
// Close abcd balance
close("abcd"_n);

// receiver account is not open
// handler account is not open
BOOST_REQUIRE_EXCEPTION(send_raw_message(evm1, emv_reserved_address, 1e14,
evmc::from_hex(bridgeMsgV0_method_id).value() +
evmc::from_hex(int_str32(96)).value() +
Expand All @@ -263,7 +263,7 @@ BOOST_FIXTURE_TEST_CASE(test_bridge_errors, bridge_message_tester) try {
evmc::from_hex(data_str32(str_to_hex("abcd"))).value() +
evmc::from_hex(int_str32(4)).value() +
evmc::from_hex(data_str32(str_to_hex("data"))).value()),
eosio_assert_message_exception, eosio_assert_message_is("receiver account is not open"));
eosio_assert_message_exception, eosio_assert_message_is("handler account is not open"));
} FC_LOG_AND_RETHROW()

BOOST_FIXTURE_TEST_CASE(test_send_message_from_solidity, bridge_message_tester) try {
Expand All @@ -287,7 +287,7 @@ BOOST_FIXTURE_TEST_CASE(test_send_message_from_solidity, bridge_message_tester)
set_abi("rec1"_n, testing::contracts::evm_bridge_receiver_abi().data());

// Register rec1 with 0 EOS as min_fee
bridgereg("rec1"_n, make_asset(0));
bridgereg("rec1"_n, "rec1"_n, make_asset(0));

// Fund evm1 address with 100 EOS
evm_eoa evm1;
Expand Down Expand Up @@ -325,4 +325,88 @@ BOOST_FIXTURE_TEST_CASE(test_send_message_from_solidity, bridge_message_tester)
}
} FC_LOG_AND_RETHROW()

BOOST_FIXTURE_TEST_CASE(handler_tests, bridge_message_tester) try {
auto emv_reserved_address = make_reserved_address(evm_account_name);

// Fund evm1 address with 1001 EOS
evm_eoa evm1;
const int64_t to_bridge = 1001'0000;
transfer_token("alice"_n, evm_account_name, make_asset(to_bridge), evm1.address_0x());

BOOST_REQUIRE_EXCEPTION(send_raw_message(evm1, emv_reserved_address, 0,
evmc::from_hex(bridgeMsgV0_method_id).value() +
evmc::from_hex(int_str32(96)).value() +
evmc::from_hex(int_str32(1)).value() +
evmc::from_hex(int_str32(160)).value() +
evmc::from_hex(int_str32(8)).value() +
evmc::from_hex(data_str32(str_to_hex("receiver"))).value() +
evmc::from_hex(int_str32(4)).value() +
evmc::from_hex(data_str32(str_to_hex("data"))).value()),
eosio_assert_message_exception, eosio_assert_message_is("receiver is not account"));
evm1.next_nonce--;

// Create receiver account
create_accounts({"receiver"_n});

// receiver not registered
BOOST_REQUIRE_EXCEPTION(send_raw_message(evm1, emv_reserved_address, 0,
evmc::from_hex(bridgeMsgV0_method_id).value() +
evmc::from_hex(int_str32(96)).value() +
evmc::from_hex(int_str32(1)).value() +
evmc::from_hex(int_str32(160)).value() +
evmc::from_hex(int_str32(8)).value() +
evmc::from_hex(data_str32(str_to_hex("receiver"))).value() +
evmc::from_hex(int_str32(4)).value() +
evmc::from_hex(data_str32(str_to_hex("data"))).value()),
eosio_assert_message_exception, eosio_assert_message_is("receiver not registered"));
evm1.next_nonce--;

// Create handler account
create_accounts({"handler"_n});
set_code("handler"_n, testing::contracts::evm_bridge_receiver_wasm());
set_abi("handler"_n, testing::contracts::evm_bridge_receiver_abi().data());
// Register handler with 1000.0000 EOS as min_fee
bridgereg("receiver"_n, "handler"_n, make_asset(1000'0000));


// Corner case: handler account is not open
// We can only test in this way because bridgereg will open handler.
close("handler"_n);

BOOST_REQUIRE_EXCEPTION(send_raw_message(evm1, emv_reserved_address, 1000_ether,
evmc::from_hex(bridgeMsgV0_method_id).value() +
evmc::from_hex(int_str32(96)).value() +
evmc::from_hex(int_str32(1)).value() +
evmc::from_hex(int_str32(160)).value() +
evmc::from_hex(int_str32(8)).value() +
evmc::from_hex(data_str32(str_to_hex("receiver"))).value() +
evmc::from_hex(int_str32(4)).value() +
evmc::from_hex(data_str32(str_to_hex("data"))).value()),
eosio_assert_message_exception, eosio_assert_message_is("handler account is not open"));
evm1.next_nonce--;
open("handler"_n);

// Check handler balance before sending the message
BOOST_REQUIRE(vault_balance("handler"_n) == (balance_and_dust{make_asset(0), 0}));

// Emit message
auto res = send_bridge_message(evm1, "receiver", 1000_ether, "0102030405060708090a");

// Check handler balance after sending the message
BOOST_REQUIRE(vault_balance("handler"_n) == (balance_and_dust{make_asset(1000'0000), 0}));

// Recover message form the return value of handler contract
// Make sure "handler" received a message sent to "receiver"
BOOST_CHECK(res->action_traces[1].receiver == "handler"_n);
auto msgout = fc::raw::unpack<bridge_message>(res->action_traces[1].return_value);
auto out = std::get<bridge_message_v0>(msgout);

BOOST_CHECK(out.receiver == "receiver"_n);
BOOST_CHECK(out.sender == to_bytes(evm1.address));
BOOST_CHECK(out.timestamp.time_since_epoch() == control->pending_block_time().time_since_epoch());
BOOST_CHECK(out.value == to_bytes(1000_ether));
BOOST_CHECK(out.data == to_bytes(evmc::from_hex("0102030405060708090a").value()));

} FC_LOG_AND_RETHROW()

BOOST_AUTO_TEST_SUITE_END()

0 comments on commit ea2eb46

Please sign in to comment.