diff --git a/include/evm_runtime/evm_contract.hpp b/include/evm_runtime/evm_contract.hpp index 5ae8b204..b1e695f3 100644 --- a/include/evm_runtime/evm_contract.hpp +++ b/include/evm_runtime/evm_contract.hpp @@ -91,6 +91,8 @@ class [[eosio::contract]] evm_contract : public contract [[eosio::action]] void bridgereg(eosio::name receiver, const eosio::asset& min_fee); [[eosio::action]] void bridgeunreg(eosio::name receiver); + [[eosio::action]] void assertnonce(eosio::name account, uint64_t next_nonce); + #ifdef WITH_TEST_ACTIONS [[eosio::action]] void testtx(const std::optional& orlptx, const evm_runtime::test::block_info& bi); [[eosio::action]] void diff --git a/src/actions.cpp b/src/actions.cpp index 8f325b7f..a0c1cf63 100644 --- a/src/actions.cpp +++ b/src/actions.cpp @@ -735,6 +735,19 @@ void evm_contract::bridgeunreg(eosio::name receiver) { message_receivers.erase(*it); } + +void evm_contract::assertnonce(eosio::name account, uint64_t next_nonce) { + nextnonces nextnonce_table(get_self(), get_self().value); + + auto next_nonce_iter = nextnonce_table.find(account.value); + if (next_nonce_iter == nextnonce_table.end()) { + eosio::check(0 == next_nonce, "wrong nonce"); + } + else { + eosio::check(next_nonce_iter->next_nonce == next_nonce, "wrong nonce"); + } +} + #ifdef WITH_TEST_ACTIONS [[eosio::action]] void evm_contract::testtx( const std::optional& orlptx, const evm_runtime::test::block_info& bi ) { assert_unfrozen(); diff --git a/tests/basic_evm_tester.cpp b/tests/basic_evm_tester.cpp index 2e3e9352..fc1c5d2a 100644 --- a/tests/basic_evm_tester.cpp +++ b/tests/basic_evm_tester.cpp @@ -344,6 +344,11 @@ transaction_trace_ptr basic_evm_tester::bridgeunreg(name receiver) { mvo()("receiver", receiver)); } +transaction_trace_ptr basic_evm_tester::assertnonce(name account, uint64_t next_nonce) { + return basic_evm_tester::push_action(evm_account_name, "assertnonce"_n, account, + mvo()("account", account)("next_nonce", next_nonce)); +} + transaction_trace_ptr basic_evm_tester::pushtx(const silkworm::Transaction& trx, name miner) { silkworm::Bytes rlp; diff --git a/tests/basic_evm_tester.hpp b/tests/basic_evm_tester.hpp index b6e5d1f0..9adc416e 100644 --- a/tests/basic_evm_tester.hpp +++ b/tests/basic_evm_tester.hpp @@ -222,6 +222,7 @@ class basic_evm_tester : public testing::validating_tester transaction_trace_ptr bridgereg(name receiver, 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 assertnonce(name account, uint64_t next_nonce); transaction_trace_ptr pushtx(const silkworm::Transaction& trx, name miner = evm_account_name); void call(name from, const evmc::bytes& to, const evmc::bytes& value, evmc::bytes& data, uint64_t gas_limit, name actor); void admincall(const evmc::bytes& from, const evmc::bytes& to, const evmc::bytes& value, evmc::bytes& data, uint64_t gas_limit, name actor); diff --git a/tests/call_tests.cpp b/tests/call_tests.cpp index 085ea3f2..9fcad062 100644 --- a/tests/call_tests.cpp +++ b/tests/call_tests.cpp @@ -471,12 +471,13 @@ BOOST_FIXTURE_TEST_CASE(deploy_contract_function, call_evm_tester) try { auto to = evmc::bytes(); auto data = evmc::from_hex(contract_bytecode); - + assertnonce("alice"_n, 0); call("alice"_n, to, silkworm::Bytes(v), *data, 1000000, "alice"_n); // nonce 0->1 auto addr = silkworm::create_address(alice_addr, 0); - + assertnonce("alice"_n, 1); call_test(addr, 1234, "alice"_n, "alice"_n); // nonce 1->2 + auto count = get_count(addr); BOOST_REQUIRE(count == 1234); @@ -484,15 +485,72 @@ BOOST_FIXTURE_TEST_CASE(deploy_contract_function, call_evm_tester) try { produce_block(); auto from = evmc::bytes{std::begin(alice_addr.bytes), std::end(alice_addr.bytes)}; - + assertnonce("alice"_n, 2); admincall(from, to, silkworm::Bytes(v), *data, 1000000, evm_account_name); // nonce 2->3 - + addr = silkworm::create_address(alice_addr, 2); + assertnonce("alice"_n, 3); call_test(addr, 2222, "alice"_n, "alice"_n); // nonce 3->4 + assertnonce("alice"_n, 4); count = get_count(addr); BOOST_REQUIRE(count == 2222); } FC_LOG_AND_RETHROW() +BOOST_FIXTURE_TEST_CASE(assetnonce_test, call_evm_tester) try { + auto alice_addr = make_reserved_address("alice"_n.to_uint64_t()); + + // nonce for not opened account is zero. + assertnonce("alice"_n, 0); + BOOST_REQUIRE_EXCEPTION(assertnonce("alice"_n, 1), + eosio_assert_message_exception, eosio_assert_message_is("wrong nonce")); + + // Advance block so we do not generate same transaction. + produce_block(); + + open("alice"_n); + transfer_token("alice"_n, evm_account_name, make_asset(1000000), "alice"); + + evm_eoa evm1; + evmc::bytes32 v; + + auto to = evmc::bytes(); + + auto data = evmc::from_hex(contract_bytecode); + + // Fail when nonce is 0 but tested with non-zero value + BOOST_REQUIRE_EXCEPTION(assertnonce("alice"_n, 1), + eosio_assert_message_exception, eosio_assert_message_is("wrong nonce")); + + assertnonce("alice"_n, 0); + call("alice"_n, to, silkworm::Bytes(v), *data, 1000000, "alice"_n); // nonce 0->1 + + // Advance block so we do not generate same transaction. + produce_block(); + + assertnonce("alice"_n, 1); + assertnonce(evm_account_name, 0); + // Fund evm1 address with 100 EOS, should NOT increase alice nonce, but increase evm nonce. + transfer_token("alice"_n, evm_account_name, make_asset(1000000), evm1.address_0x()); + + // Advance block so we do not generate same transaction. + produce_block(); + assertnonce("alice"_n, 1); + assertnonce(evm_account_name, 1); + + // Advance block so we do not generate same transaction. + produce_block(); + + // Fail when nonce is non-zero but tested with another value + BOOST_REQUIRE_EXCEPTION(assertnonce("alice"_n, 2), + eosio_assert_message_exception, eosio_assert_message_is("wrong nonce")); + // Fail when nonce is non-zero but tested with 0 + BOOST_REQUIRE_EXCEPTION(assertnonce("alice"_n, 0), + eosio_assert_message_exception, eosio_assert_message_is("wrong nonce")); + + + +} FC_LOG_AND_RETHROW() + BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file