From ea2eb4641247b1bfa0cddbe2fa0465298d3a2f92 Mon Sep 17 00:00:00 2001 From: yarkin Date: Thu, 31 Aug 2023 15:03:17 +0800 Subject: [PATCH] Add handler option to bridge message feature. --- include/evm_runtime/evm_contract.hpp | 2 +- include/evm_runtime/tables.hpp | 3 +- tests/basic_evm_tester.cpp | 6 +- tests/basic_evm_tester.hpp | 5 +- tests/bridge_message_tests.cpp | 104 ++++++++++++++++++++++++--- 5 files changed, 104 insertions(+), 16 deletions(-) diff --git a/include/evm_runtime/evm_contract.hpp b/include/evm_runtime/evm_contract.hpp index 5ae8b204..979817b7 100644 --- a/include/evm_runtime/evm_contract.hpp +++ b/include/evm_runtime/evm_contract.hpp @@ -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 diff --git a/include/evm_runtime/tables.hpp b/include/evm_runtime/tables.hpp index c44b6768..5f6e99e1 100644 --- a/include/evm_runtime/tables.hpp +++ b/include/evm_runtime/tables.hpp @@ -189,6 +189,7 @@ struct [[eosio::table]] [[eosio::contract("evm_contract")]] message_receiver { }; name account; + name handler; asset min_fee; uint32_t flags; @@ -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; diff --git a/tests/basic_evm_tester.cpp b/tests/basic_evm_tester.cpp index 2e3e9352..6ec77c7c 100644 --- a/tests/basic_evm_tester.cpp +++ b/tests/basic_evm_tester.cpp @@ -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 extra_signers) { +transaction_trace_ptr basic_evm_tester::bridgereg(name receiver, name handler, asset min_fee, vector 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) { diff --git a/tests/basic_evm_tester.hpp b/tests/basic_evm_tester.hpp index b6e5d1f0..088e8d7a 100644 --- a/tests/basic_evm_tester.hpp +++ b/tests/basic_evm_tester.hpp @@ -111,6 +111,7 @@ struct exec_output { struct message_receiver { name account; + name handler; asset min_fee; uint32_t flags; }; @@ -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 { @@ -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 extra_signers={evm_account_name}); + transaction_trace_ptr bridgereg(name receiver, name handler, asset min_fee, vector extra_signers={evm_account_name}); transaction_trace_ptr bridgeunreg(name receiver); transaction_trace_ptr exec(const exec_input& input, const std::optional& callback); transaction_trace_ptr pushtx(const silkworm::Transaction& trx, name miner = evm_account_name); diff --git a/tests/bridge_message_tests.cpp b/tests/bridge_message_tests.cpp index c1868dd0..10f83fc3 100644 --- a/tests/bridge_message_tests.cpp +++ b/tests/bridge_message_tests.cpp @@ -81,18 +81,18 @@ 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(get_row_by_account( evm_account_name, evm_account_name, "msgreceiver"_n, "rec1"_n)); BOOST_REQUIRE(row.account == "rec1"_n); @@ -100,7 +100,7 @@ BOOST_FIXTURE_TEST_CASE(bridge_register_test, bridge_message_tester) try { 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(get_row_by_account( evm_account_name, evm_account_name, "msgreceiver"_n, "rec1"_n)); BOOST_REQUIRE(row.account == "rec1"_n); @@ -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; @@ -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, @@ -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() + @@ -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 { @@ -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; @@ -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(res->action_traces[1].return_value); + auto out = std::get(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() \ No newline at end of file