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

Add handler option to bridge message feature. #647

Merged
merged 5 commits into from
Sep 1, 2023
Merged
Show file tree
Hide file tree
Changes from 4 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
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);

[[eosio::action]] void assertnonce(eosio::name account, uint64_t next_nonce);
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
7 changes: 4 additions & 3 deletions src/actions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -411,9 +411,9 @@ void evm_contract::process_filtered_messages(const std::vector<silkworm::Filtere
eosio::check(value >= min_fee, "min_fee not covered");

balances balance_table(get_self(), get_self().value);
const balance& receiver_account = balance_table.get(msg_v0.get_account_as_name().value, "receiver account is not open");
const balance& receiver_account = balance_table.get(receiver.value, "receiver account is not open");

action(std::vector<permission_level>{}, msg_v0.get_account_as_name(), "onbridgemsg"_n,
action(std::vector<permission_level>{}, it->handler, "onbridgemsg"_n,
bridge_message{ bridge_message_v0 {
.receiver = msg_v0.get_account_as_name(),
.sender = to_bytes(rawmsg.sender),
Expand Down Expand Up @@ -699,7 +699,7 @@ void evm_contract::admincall(const bytes& from, const bytes& to, const bytes& va
call_(s, to, v, data, gas_limit, nonce);
}

void evm_contract::bridgereg(eosio::name receiver, const eosio::asset& min_fee) {
void evm_contract::bridgereg(eosio::name receiver, eosio::name handler, const eosio::asset& min_fee) {
assert_unfrozen();
require_auth(receiver);
require_auth(get_self()); // to temporarily prevent registration of unauthorized accounts
Expand All @@ -709,6 +709,7 @@ void evm_contract::bridgereg(eosio::name receiver, const eosio::asset& min_fee)

auto update_row = [&](auto& row) {
row.account = receiver;
row.handler = handler;
row.min_fee = min_fee;
row.flags = message_receiver::FORCE_ATOMIC;
};
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 assertnonce(name account, uint64_t next_nonce);
Expand Down
102 changes: 93 additions & 9 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
arhag marked this conversation as resolved.
Show resolved Hide resolved
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 Down Expand Up @@ -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() +
arhag marked this conversation as resolved.
Show resolved Hide resolved
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: receiver account is not open
// We can only test in this way because bridgereg will open receiver.
close("receiver"_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("receiver account is not open"));
evm1.next_nonce--;
open("receiver"_n);

// Check handler balance before sending the message
BOOST_REQUIRE(vault_balance("receiver"_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("receiver"_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()