From 1e300f2e959c1fd34928fec53bfb549df0f8b4ef Mon Sep 17 00:00:00 2001 From: yarkin Date: Thu, 27 Jul 2023 23:08:14 +0800 Subject: [PATCH 01/14] Add call actions. Pending submodule silkworm updates. --- include/evm_runtime/evm_contract.hpp | 6 + src/actions.cpp | 92 ++++++++-- tests/CMakeLists.txt | 1 + tests/basic_evm_tester.cpp | 28 +++ tests/basic_evm_tester.hpp | 2 + tests/call_tests.cpp | 252 +++++++++++++++++++++++++++ 6 files changed, 366 insertions(+), 15 deletions(-) create mode 100644 tests/call_tests.cpp diff --git a/include/evm_runtime/evm_contract.hpp b/include/evm_runtime/evm_contract.hpp index 81b47620..668fd439 100644 --- a/include/evm_runtime/evm_contract.hpp +++ b/include/evm_runtime/evm_contract.hpp @@ -84,6 +84,10 @@ class [[eosio::contract]] evm_contract : public contract /// @return true if all garbage has been collected [[eosio::action]] bool gc(uint32_t max); + + [[eosio::action]] void call(eosio::name from, const bytes& to, uint128_t value, bytes& data, uint64_t gas_limit); + [[eosio::action]] void admincall(const bytes& from, const bytes& to, uint128_t value, bytes& data, uint64_t gas_limit); + #ifdef WITH_TEST_ACTIONS [[eosio::action]] void testtx(const std::optional& orlptx, const evm_runtime::test::block_info& bi); [[eosio::action]] void @@ -140,6 +144,8 @@ class [[eosio::contract]] evm_contract : public contract void handle_account_transfer(const eosio::asset& quantity, const std::string& memo); void handle_evm_transfer(eosio::asset quantity, const std::string& memo); + void call_(intx::uint256 s, const evmc::address& to, intx::uint256 value, bytes& data, uint64_t gas_limit, uint64_t nonce); + // to allow sending through a Bytes (basic_string) w/o copying over to a std::vector void pushtx_bytes(eosio::name miner, const std::basic_string& rlptx); using pushtx_action = eosio::action_wrapper<"pushtx"_n, &evm_contract::pushtx_bytes>; diff --git a/src/actions.cpp b/src/actions.cpp index 893aa4ee..ff8e0d48 100644 --- a/src/actions.cpp +++ b/src/actions.cpp @@ -40,6 +40,7 @@ namespace silkworm { namespace evm_runtime { static constexpr uint32_t hundred_percent = 100'000; +static constexpr char err_msg_invalid_addr[] = "invalid address"; using namespace silkworm; @@ -219,30 +220,37 @@ Receipt evm_contract::execute_tx( eosio::name miner, Block& block, Transaction& inevm.emplace(get_self(), get_self().value); }; + bool is_special_signature = (tx.r == intx::uint256()); + tx.from.reset(); tx.recover_sender(); eosio::check(tx.from.has_value(), "unable to recover sender"); LOGTIME("EVM RECOVER SENDER"); + // type 1: normal signature (normal address recovered from normal signature), required !from_self + // type 2: special signature (r == 0), reserved address (stored in s), required from_self + reduce special balance + // type 3: special signature (r == 0), normal address (stored in s), required from_self if(from_self) { - check(is_reserved_address(*tx.from), "actions from self without a reserved from address are unexpected"); - const name ingress_account(*extract_reserved_address(*tx.from)); + check(is_special_signature, "actions from self without a special signature are unexpected"); + if (is_reserved_address(*tx.from)) { + const name ingress_account(*extract_reserved_address(*tx.from)); - const intx::uint512 max_gas_cost = intx::uint256(tx.gas_limit) * tx.max_fee_per_gas; - check(max_gas_cost + tx.value < std::numeric_limits::max(), "too much gas"); - const intx::uint256 value_with_max_gas = tx.value + (intx::uint256)max_gas_cost; + const intx::uint512 max_gas_cost = intx::uint256(tx.gas_limit) * tx.max_fee_per_gas; + check(max_gas_cost + tx.value < std::numeric_limits::max(), "too much gas"); + const intx::uint256 value_with_max_gas = tx.value + (intx::uint256)max_gas_cost; - populate_bridge_accessors(); - balance_table.modify(balance_table.get(ingress_account.value), eosio::same_payer, [&](balance& b){ - b.balance -= value_with_max_gas; - }); - inevm->set(inevm->get() += value_with_max_gas, eosio::same_payer); + populate_bridge_accessors(); + balance_table.modify(balance_table.get(ingress_account.value), eosio::same_payer, [&](balance& b){ + b.balance -= value_with_max_gas; + }); + inevm->set(inevm->get() += value_with_max_gas, eosio::same_payer); - ep.state().set_balance(*tx.from, value_with_max_gas); - ep.state().set_nonce(*tx.from, tx.nonce); + ep.state().set_balance(*tx.from, value_with_max_gas); + ep.state().set_nonce(*tx.from, tx.nonce); + } } - else if(is_reserved_address(*tx.from)) - check(from_self, "bridge signature used outside of bridge transaction"); + else if(is_special_signature) + check(false, "bridge signature used outside of bridge transaction"); if(enforce_chain_id && !from_self) { check(tx.chain_id.has_value(), "tx without chain-id"); @@ -452,7 +460,7 @@ void evm_contract::close(eosio::name owner) { uint64_t evm_contract::get_and_increment_nonce(const name owner) { nextnonces nextnonce_table(get_self(), get_self().value); - const nextnonce& nonce = nextnonce_table.get(owner.value); + const nextnonce& nonce = nextnonce_table.get(owner.value, "caller account has not been opened"); uint64_t ret = nonce.next_nonce; nextnonce_table.modify(nonce, eosio::same_payer, [](nextnonce& n){ ++n.next_nonce; @@ -555,6 +563,60 @@ bool evm_contract::gc(uint32_t max) { return state.gc(max); } +void evm_contract::call_(intx::uint256 s, const evmc::address& to, intx::uint256 value, bytes& data, uint64_t gas_limit, uint64_t nonce) { + const auto& current_config = _config.get(); + + Transaction txn; + + txn.type = TransactionType::kLegacy, + txn.nonce = nonce, + txn.max_priority_fee_per_gas = current_config.gas_price, + txn.max_fee_per_gas = current_config.gas_price, + txn.gas_limit = gas_limit, + txn.to = to, + txn.value = value, + txn.data = Bytes{(const uint8_t*)data.data(), data.size()}, + txn.r = 0u, // r == 0 is pseudo signature that resolves to reserved address range + txn.s = s; + + Bytes rlp; + rlp::encode(rlp, txn); + pushtx_action pushtx_act(get_self(), {{get_self(), "active"_n}}); + pushtx_act.send(get_self(), rlp); +} + +void evm_contract::call(eosio::name from, const bytes& to, uint128_t value, bytes& data, uint64_t gas_limit) { + assert_unfrozen(); + require_auth(from); + + ByteView bv_to{(const uint8_t*)to.data(), to.size()}; + + call_(from.value, to_evmc_address(bv_to), intx::uint256(value), data, gas_limit, get_and_increment_nonce(from)); +} + +void evm_contract::admincall(const bytes& from, const bytes& to, uint128_t value, bytes& data, uint64_t gas_limit) { + assert_unfrozen(); + require_auth(get_self()); + + // Prepare s + eosio::check(from.size() == kAddressLength, err_msg_invalid_addr); + intx::uint256 s = intx::be::unsafe::load((const uint8_t *)from.data()); + // load will put the data in higher bytes, shift them donw. + s >>= 256 - kAddressLength * 8; + // pad with '1's + s |= ((~intx::uint256(0)) << (kAddressLength * 8)); + + // Prepare to + ByteView bv_to{(const uint8_t*)to.data(), to.size()}; + + // Prepare nonce + evm_runtime::state state{get_self(), get_self(), true}; + auto account = state.read_account(to_address(from)); + check(!!account, err_msg_invalid_addr); + + call_(s, to_evmc_address(bv_to), intx::uint256(value), data, gas_limit, account->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/CMakeLists.txt b/tests/CMakeLists.txt index dcb168fd..d0c91558 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -30,6 +30,7 @@ add_eosio_test_executable( unit_test ${CMAKE_SOURCE_DIR}/gas_fee_tests.cpp ${CMAKE_SOURCE_DIR}/blockhash_tests.cpp ${CMAKE_SOURCE_DIR}/exec_tests.cpp + ${CMAKE_SOURCE_DIR}/call_tests.cpp ${CMAKE_SOURCE_DIR}/chainid_tests.cpp ${CMAKE_SOURCE_DIR}/main.cpp ${CMAKE_SOURCE_DIR}/silkworm/core/silkworm/rlp/encode.cpp diff --git a/tests/basic_evm_tester.cpp b/tests/basic_evm_tester.cpp index a9d7ece5..2e337e33 100644 --- a/tests/basic_evm_tester.cpp +++ b/tests/basic_evm_tester.cpp @@ -291,6 +291,34 @@ transaction_trace_ptr basic_evm_tester::exec(const exec_input& input, const std: return basic_evm_tester::push_action(evm_account_name, "exec"_n, evm_account_name, bytes{binary_data.begin(), binary_data.end()}); } +void basic_evm_tester::call(name from, const evmc::bytes& to, uint128_t value, evmc::bytes& data, uint64_t gas_limit, name actor) +{ + bytes to_bytes; + to_bytes.resize(to.size()); + memcpy(to_bytes.data(), to.data(), to.size()); + bytes data_bytes; + data_bytes.resize(data.size()); + memcpy(data_bytes.data(), data.data(), data.size()); + + push_action(evm_account_name, "call"_n, actor, mvo()("from", from)("to", to_bytes)("value", value)("data", data_bytes)("gas_limit", gas_limit)); +} + +void basic_evm_tester::admincall(const evmc::bytes& from, const evmc::bytes& to, uint128_t value, evmc::bytes& data, uint64_t gas_limit, name actor) +{ + bytes to_bytes; + to_bytes.resize(to.size()); + memcpy(to_bytes.data(), to.data(), to.size()); + bytes data_bytes; + data_bytes.resize(data.size()); + memcpy(data_bytes.data(), data.data(), data.size()); + + bytes from_bytes; + from_bytes.resize(to.size()); + memcpy(from_bytes.data(), from.data(), from.size()); + + push_action(evm_account_name, "admincall"_n, actor, mvo()("from", from_bytes)("to", to_bytes)("value", value)("data", data_bytes)("gas_limit", gas_limit)); +} + void 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 56b79940..d2c3fa16 100644 --- a/tests/basic_evm_tester.hpp +++ b/tests/basic_evm_tester.hpp @@ -200,6 +200,8 @@ class basic_evm_tester : public testing::validating_tester transaction_trace_ptr exec(const exec_input& input, const std::optional& callback); void pushtx(const silkworm::Transaction& trx, name miner = evm_account_name); + void call(name from, const evmc::bytes& to, uint128_t value, evmc::bytes& data, uint64_t gas_limit, name actor); + void admincall(const evmc::bytes& from, const evmc::bytes& to, uint128_t value, evmc::bytes& data, uint64_t gas_limit, name actor); evmc::address deploy_contract(evm_eoa& eoa, evmc::bytes bytecode); void addegress(const std::vector& accounts); diff --git a/tests/call_tests.cpp b/tests/call_tests.cpp new file mode 100644 index 00000000..e93e7ca2 --- /dev/null +++ b/tests/call_tests.cpp @@ -0,0 +1,252 @@ +#include "basic_evm_tester.hpp" +#include + +using intx::operator""_u256; + +using namespace evm_test; +using eosio::testing::eosio_assert_message_is; +struct exec_output_row { + uint64_t id; + exec_output output; +}; +FC_REFLECT(exec_output_row, (id)(output)) + +struct call_evm_tester : basic_evm_tester { + call_evm_tester() { + create_accounts({"alice"_n}); + transfer_token(faucet_account_name, "alice"_n, make_asset(10000'0000)); + create_accounts({"bob"_n}); + transfer_token(faucet_account_name, "bob"_n, make_asset(10000'0000)); + init(); + } + + evmc::address deploy_test_contract(evm_eoa& eoa) { + + /* + //SPDX-License-Identifier: lgplv3 + pragma solidity ^0.8.0; + + contract Test { + uint256 public count; + address public lastcaller; + + function test(uint256 input) public { + count += input; + lastcaller = msg.sender; + } + } + */ + const std::string token_bytecode = + "608060405234801561001057600080fd5b506102ad806100206000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c806306661abd1461004657806329e99f0714610064578063d097e7a614610080575b600080fd5b61004e61009e565b60405161005b919061013f565b60405180910390f35b61007e6004803603810190610079919061018b565b6100a4565b005b610088610100565b60405161009591906101f9565b60405180910390f35b60005481565b806000808282546100b59190610243565b9250508190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000819050919050565b61013981610126565b82525050565b60006020820190506101546000830184610130565b92915050565b600080fd5b61016881610126565b811461017357600080fd5b50565b6000813590506101858161015f565b92915050565b6000602082840312156101a1576101a061015a565b5b60006101af84828501610176565b91505092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006101e3826101b8565b9050919050565b6101f3816101d8565b82525050565b600060208201905061020e60008301846101ea565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061024e82610126565b915061025983610126565b925082820190508082111561027157610270610214565b5b9291505056fea2646970667358221220f1474068cf9aac836f8c5a31a9307e9bf225c1fccdedaf90eeaba493de1a9c9564736f6c63430008120033"; + // Deploy token contract + return deploy_contract(eoa, evmc::from_hex(token_bytecode).value()); + } + + void call_test(const evmc::address& contract_addr, uint64_t amount, name eos, name actor) { + + auto to = evmc::bytes{std::begin(contract_addr.bytes), std::end(contract_addr.bytes)}; + + silkworm::Bytes data; + data += evmc::from_hex("29e99f07").value(); // sha3(test(uint256))[:4] + data += evmc::bytes32{amount}; // value + + call(eos, to, 0, data, 500000, actor); + } + + void admincall_test(const evmc::address& contract_addr, uint64_t amount, evm_eoa& eoa, name actor) { + auto to = evmc::bytes{std::begin(contract_addr.bytes), std::end(contract_addr.bytes)}; + auto from = evmc::bytes{std::begin(eoa.address.bytes), std::end(eoa.address.bytes)}; + silkworm::Bytes data; + data += evmc::from_hex("29e99f07").value(); // sha3(test(uint256))[:4] + data += evmc::bytes32{amount}; // value + + admincall(from, to, 0, data, 500000, actor); + } + + intx::uint256 get_count(const evmc::address& contract_addr, std::optional callback={}, std::optional context={}) { + exec_input input; + input.context = context; + input.to = bytes{std::begin(contract_addr.bytes), std::end(contract_addr.bytes)}; + + silkworm::Bytes data; + data += evmc::from_hex("06661abd").value(); // sha3(count())[:4] + input.data = bytes{data.begin(), data.end()}; + + auto res = exec(input, callback); + + BOOST_REQUIRE(res); + BOOST_REQUIRE(res->action_traces.size() == 1); + + // Since callback information was not provided the result of the + // execution is returned in the action return_value + auto out = fc::raw::unpack(res->action_traces[0].return_value); + BOOST_REQUIRE(out.status == 0); + BOOST_REQUIRE(out.data.size() == 32); + + auto result = intx::be::unsafe::load(reinterpret_cast(out.data.data())); + return result; + } + + evmc::address get_lastcaller(const evmc::address& contract_addr, std::optional callback={}, std::optional context={}) { + exec_input input; + input.context = context; + input.to = bytes{std::begin(contract_addr.bytes), std::end(contract_addr.bytes)}; + + silkworm::Bytes data; + data += evmc::from_hex("d097e7a6").value(); // sha3(lastcaller())[:4] + input.data = bytes{data.begin(), data.end()}; + + auto res = exec(input, callback); + + BOOST_REQUIRE(res); + BOOST_REQUIRE(res->action_traces.size() == 1); + + // Since callback information was not provided the result of the + // execution is returned in the action return_value + auto out = fc::raw::unpack(res->action_traces[0].return_value); + BOOST_REQUIRE(out.status == 0); + + BOOST_REQUIRE(out.data.size() >= silkworm::kAddressLength); + + evmc::address result; + memcpy(result.bytes, out.data.data()+out.data.size() - silkworm::kAddressLength, silkworm::kAddressLength); + return result; + } + +}; + +BOOST_AUTO_TEST_SUITE(call_evm_tests) +BOOST_FIXTURE_TEST_CASE(call_test_function, call_evm_tester) try { + evm_eoa evm1; + auto total_fund = intx::uint256(vault_balance("evm"_n)); + // Fund evm1 address with 100 EOS + transfer_token("alice"_n, "evm"_n, make_asset(1000000), evm1.address_0x()); + auto evm1_balance = evm_balance(evm1); + BOOST_REQUIRE(!!evm1_balance); + BOOST_REQUIRE(*evm1_balance == intx::exp<256>(10, 18) * 100); + total_fund += *evm1_balance; + + // Deploy contract + auto token_addr = deploy_test_contract(evm1); + + // Deployment gas fee go to evm vault + BOOST_REQUIRE(*evm_balance(evm1) + intx::uint256(vault_balance("evm"_n)) == total_fund); + + // Missing authority + BOOST_REQUIRE_EXCEPTION(call_test(token_addr, 1234, "alice"_n, "bob"_n), + missing_auth_exception, eosio::testing::fc_exception_message_starts_with("missing authority")); + + // Account not opened + BOOST_REQUIRE_EXCEPTION(call_test(token_addr, 1234, "alice"_n, "alice"_n), + eosio_assert_message_exception, eosio_assert_message_is("caller account has not been opened")); + + // Open + open("alice"_n); + + // No sufficient funds in the account so decrementing of balance failed. + BOOST_REQUIRE_EXCEPTION(call_test(token_addr, 1234, "alice"_n, "alice"_n), + eosio_assert_message_exception, eosio_assert_message_is("decrementing more than available")); + + // Transfer enough funds + transfer_token("alice"_n, "evm"_n, make_asset(1000000), "alice"); + + BOOST_REQUIRE(intx::uint256(vault_balance("alice"_n)) == intx::exp<256>(10, 18) * 100); + auto total_fund2 = intx::uint256(vault_balance("alice"_n)) + intx::uint256(vault_balance("evm"_n)); + + // Call and check results + call_test(token_addr, 1234, "alice"_n, "alice"_n); + auto count = get_count(token_addr); + BOOST_REQUIRE(count == 1234); + + // Gas go from alice's vault to evm's vault + BOOST_REQUIRE(total_fund2 == intx::uint256(vault_balance("alice"_n)) + intx::uint256(vault_balance("evm"_n))); + + + // Advance block so we do not generate same transaction. + produce_block(); + + call_test(token_addr, 4321, "alice"_n, "alice"_n); + count = get_count(token_addr); + BOOST_REQUIRE(count == 5555); + + // Gas go from alice's vault to evm's vault + BOOST_REQUIRE(total_fund2 == intx::uint256(vault_balance("alice"_n)) + intx::uint256(vault_balance("evm"_n))); + + // Function being called on behalf of reserved address of eos account "alice" + auto caller = get_lastcaller(token_addr); + BOOST_REQUIRE(caller == make_reserved_address("alice"_n.to_uint64_t())); + +} FC_LOG_AND_RETHROW() + +BOOST_FIXTURE_TEST_CASE(admincall_test_function, call_evm_tester) try { + evm_eoa evm1; + evm_eoa evm2; + + auto total_fund = intx::uint256(vault_balance("evm"_n)); + + // Fund evm1 address with 100 EOS + transfer_token("alice"_n, "evm"_n, make_asset(1000000), evm1.address_0x()); + + auto evm1_balance = evm_balance(evm1); + BOOST_REQUIRE(!!evm1_balance); + BOOST_REQUIRE(*evm1_balance == intx::exp<256>(10, 18) * 100); + total_fund += *evm1_balance; + + // Deploy contract + auto token_addr = deploy_test_contract(evm1); + + // Deployment gas fee go to evm vault + BOOST_REQUIRE(*evm_balance(evm1) + intx::uint256(vault_balance("evm"_n)) == total_fund); + + // Missing authority + BOOST_REQUIRE_EXCEPTION(admincall_test(token_addr, 1234, evm2, "alice"_n), + missing_auth_exception, eosio::testing::fc_exception_message_starts_with("missing authority")); + + // Account not created + BOOST_REQUIRE_EXCEPTION( admincall_test(token_addr, 1234, evm2, "evm"_n), + eosio_assert_message_exception, eosio_assert_message_is("invalid address")); + + // Transfer small amount to create account + transfer_token("alice"_n, "evm"_n, make_asset(100), evm2.address_0x()); + + auto evm2_balance = evm_balance(evm2); + BOOST_REQUIRE(!!evm2_balance); + BOOST_REQUIRE(*evm2_balance == intx::exp<256>(10, 18 - 4) * 100); + + // Insufficient funds + BOOST_REQUIRE_EXCEPTION( admincall_test(token_addr, 1234, evm2, "evm"_n), + eosio_assert_message_exception, eosio_assert_message_is("validate_transaction error: 23 Insufficient funds")); + + // Transfer enough funds + transfer_token("alice"_n, "evm"_n, make_asset(10000), evm2.address_0x()); + + BOOST_REQUIRE(evm_balance(evm2) == intx::exp<256>(10, 18 - 4) * 10100); + auto total_fund2 = intx::uint256(vault_balance("evm"_n)) + *evm_balance(evm2); + + // Call and check results + admincall_test(token_addr, 1234, evm2, "evm"_n); + + auto count = get_count(token_addr); + BOOST_REQUIRE(count == 1234); + + // Gas go from evm2 to evm vault + BOOST_REQUIRE(total_fund2 == *evm_balance(evm2) + intx::uint256(vault_balance("evm"_n))); + + // Advance block so we do not generate same transaction. + produce_block(); + + admincall_test(token_addr, 4321, evm2, "evm"_n); + count = get_count(token_addr); + BOOST_REQUIRE(count == 5555); + + // Gas go from evm2 to evm vault + BOOST_REQUIRE(total_fund2 == *evm_balance(evm2) + intx::uint256(vault_balance("evm"_n))); + + // Function being called on behalf of evm address "evm2" + auto caller = get_lastcaller(token_addr); + BOOST_REQUIRE(caller== evm2.address); + + +} FC_LOG_AND_RETHROW() + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file From 6e8750e3563a730bca00881d7a42e14255e05d01 Mon Sep 17 00:00:00 2001 From: yarkin Date: Fri, 28 Jul 2023 22:22:28 +0800 Subject: [PATCH 02/14] Allow call to create contracts. Allow admin call to call as reserved address. --- include/evm_runtime/evm_contract.hpp | 2 +- src/actions.cpp | 52 ++++++++++++++++------------ tests/basic_evm_tester.cpp | 4 ++- tests/call_tests.cpp | 42 ++++++++++++++++++++-- 4 files changed, 73 insertions(+), 27 deletions(-) diff --git a/include/evm_runtime/evm_contract.hpp b/include/evm_runtime/evm_contract.hpp index 668fd439..c3a96583 100644 --- a/include/evm_runtime/evm_contract.hpp +++ b/include/evm_runtime/evm_contract.hpp @@ -144,7 +144,7 @@ class [[eosio::contract]] evm_contract : public contract void handle_account_transfer(const eosio::asset& quantity, const std::string& memo); void handle_evm_transfer(eosio::asset quantity, const std::string& memo); - void call_(intx::uint256 s, const evmc::address& to, intx::uint256 value, bytes& data, uint64_t gas_limit, uint64_t nonce); + void call_(intx::uint256 s, const bytes& to, intx::uint256 value, bytes& data, uint64_t gas_limit, uint64_t nonce); // to allow sending through a Bytes (basic_string) w/o copying over to a std::vector void pushtx_bytes(eosio::name miner, const std::basic_string& rlptx); diff --git a/src/actions.cpp b/src/actions.cpp index ff8e0d48..5ce4240d 100644 --- a/src/actions.cpp +++ b/src/actions.cpp @@ -563,22 +563,26 @@ bool evm_contract::gc(uint32_t max) { return state.gc(max); } -void evm_contract::call_(intx::uint256 s, const evmc::address& to, intx::uint256 value, bytes& data, uint64_t gas_limit, uint64_t nonce) { +void evm_contract::call_(intx::uint256 s, const bytes& to, intx::uint256 value, bytes& data, uint64_t gas_limit, uint64_t nonce) { const auto& current_config = _config.get(); Transaction txn; - - txn.type = TransactionType::kLegacy, - txn.nonce = nonce, - txn.max_priority_fee_per_gas = current_config.gas_price, - txn.max_fee_per_gas = current_config.gas_price, - txn.gas_limit = gas_limit, - txn.to = to, - txn.value = value, - txn.data = Bytes{(const uint8_t*)data.data(), data.size()}, - txn.r = 0u, // r == 0 is pseudo signature that resolves to reserved address range + txn.type = TransactionType::kLegacy; + txn.nonce = nonce; + txn.max_priority_fee_per_gas = current_config.gas_price; + txn.max_fee_per_gas = current_config.gas_price; + txn.gas_limit = gas_limit; + txn.value = value; + txn.data = Bytes{(const uint8_t*)data.data(), data.size()}; + txn.r = 0u; // r == 0 is pseudo signature that resolves to reserved address range txn.s = s; + // Allow empty to so that it can support contract creation calls. + if (!to.empty()) { + ByteView bv_to{(const uint8_t*)to.data(), to.size()}; + txn.to = to_evmc_address(bv_to); + } + Bytes rlp; rlp::encode(rlp, txn); pushtx_action pushtx_act(get_self(), {{get_self(), "active"_n}}); @@ -589,9 +593,7 @@ void evm_contract::call(eosio::name from, const bytes& to, uint128_t value, byte assert_unfrozen(); require_auth(from); - ByteView bv_to{(const uint8_t*)to.data(), to.size()}; - - call_(from.value, to_evmc_address(bv_to), intx::uint256(value), data, gas_limit, get_and_increment_nonce(from)); + call_(from.value, to, intx::uint256(value), data, gas_limit, get_and_increment_nonce(from)); } void evm_contract::admincall(const bytes& from, const bytes& to, uint128_t value, bytes& data, uint64_t gas_limit) { @@ -606,15 +608,21 @@ void evm_contract::admincall(const bytes& from, const bytes& to, uint128_t value // pad with '1's s |= ((~intx::uint256(0)) << (kAddressLength * 8)); - // Prepare to - ByteView bv_to{(const uint8_t*)to.data(), to.size()}; - // Prepare nonce - evm_runtime::state state{get_self(), get_self(), true}; - auto account = state.read_account(to_address(from)); - check(!!account, err_msg_invalid_addr); - - call_(s, to_evmc_address(bv_to), intx::uint256(value), data, gas_limit, account->nonce); + auto from_addr = to_address(from); + auto eos_acct = extract_reserved_address(from_addr); + uint64_t nonce = 0; + if (eos_acct) { + nonce = get_and_increment_nonce(eosio::name(*eos_acct)); + } + else { + evm_runtime::state state{get_self(), get_self(), true}; + auto account = state.read_account(from_addr); + check(!!account, err_msg_invalid_addr); + nonce = account->nonce; + } + + call_(s, to, intx::uint256(value), data, gas_limit, nonce); } #ifdef WITH_TEST_ACTIONS diff --git a/tests/basic_evm_tester.cpp b/tests/basic_evm_tester.cpp index 2e337e33..649486c3 100644 --- a/tests/basic_evm_tester.cpp +++ b/tests/basic_evm_tester.cpp @@ -296,6 +296,7 @@ void basic_evm_tester::call(name from, const evmc::bytes& to, uint128_t value, e bytes to_bytes; to_bytes.resize(to.size()); memcpy(to_bytes.data(), to.data(), to.size()); + bytes data_bytes; data_bytes.resize(data.size()); memcpy(data_bytes.data(), data.data(), data.size()); @@ -308,12 +309,13 @@ void basic_evm_tester::admincall(const evmc::bytes& from, const evmc::bytes& to, bytes to_bytes; to_bytes.resize(to.size()); memcpy(to_bytes.data(), to.data(), to.size()); + bytes data_bytes; data_bytes.resize(data.size()); memcpy(data_bytes.data(), data.data(), data.size()); bytes from_bytes; - from_bytes.resize(to.size()); + from_bytes.resize(from.size()); memcpy(from_bytes.data(), from.data(), from.size()); push_action(evm_account_name, "admincall"_n, actor, mvo()("from", from_bytes)("to", to_bytes)("value", value)("data", data_bytes)("gas_limit", gas_limit)); diff --git a/tests/call_tests.cpp b/tests/call_tests.cpp index e93e7ca2..45dc9c2f 100644 --- a/tests/call_tests.cpp +++ b/tests/call_tests.cpp @@ -12,6 +12,9 @@ struct exec_output_row { FC_REFLECT(exec_output_row, (id)(output)) struct call_evm_tester : basic_evm_tester { + const std::string contract_bytecode = + "608060405234801561001057600080fd5b506102ad806100206000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c806306661abd1461004657806329e99f0714610064578063d097e7a614610080575b600080fd5b61004e61009e565b60405161005b919061013f565b60405180910390f35b61007e6004803603810190610079919061018b565b6100a4565b005b610088610100565b60405161009591906101f9565b60405180910390f35b60005481565b806000808282546100b59190610243565b9250508190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000819050919050565b61013981610126565b82525050565b60006020820190506101546000830184610130565b92915050565b600080fd5b61016881610126565b811461017357600080fd5b50565b6000813590506101858161015f565b92915050565b6000602082840312156101a1576101a061015a565b5b60006101af84828501610176565b91505092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006101e3826101b8565b9050919050565b6101f3816101d8565b82525050565b600060208201905061020e60008301846101ea565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061024e82610126565b915061025983610126565b925082820190508082111561027157610270610214565b5b9291505056fea2646970667358221220f1474068cf9aac836f8c5a31a9307e9bf225c1fccdedaf90eeaba493de1a9c9564736f6c63430008120033"; + call_evm_tester() { create_accounts({"alice"_n}); transfer_token(faucet_account_name, "alice"_n, make_asset(10000'0000)); @@ -36,10 +39,10 @@ struct call_evm_tester : basic_evm_tester { } } */ - const std::string token_bytecode = + const std::string contract_bytecode = "608060405234801561001057600080fd5b506102ad806100206000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c806306661abd1461004657806329e99f0714610064578063d097e7a614610080575b600080fd5b61004e61009e565b60405161005b919061013f565b60405180910390f35b61007e6004803603810190610079919061018b565b6100a4565b005b610088610100565b60405161009591906101f9565b60405180910390f35b60005481565b806000808282546100b59190610243565b9250508190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000819050919050565b61013981610126565b82525050565b60006020820190506101546000830184610130565b92915050565b600080fd5b61016881610126565b811461017357600080fd5b50565b6000813590506101858161015f565b92915050565b6000602082840312156101a1576101a061015a565b5b60006101af84828501610176565b91505092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006101e3826101b8565b9050919050565b6101f3816101d8565b82525050565b600060208201905061020e60008301846101ea565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061024e82610126565b915061025983610126565b925082820190508082111561027157610270610214565b5b9291505056fea2646970667358221220f1474068cf9aac836f8c5a31a9307e9bf225c1fccdedaf90eeaba493de1a9c9564736f6c63430008120033"; // Deploy token contract - return deploy_contract(eoa, evmc::from_hex(token_bytecode).value()); + return deploy_contract(eoa, evmc::from_hex(contract_bytecode).value()); } void call_test(const evmc::address& contract_addr, uint64_t amount, name eos, name actor) { @@ -112,7 +115,6 @@ struct call_evm_tester : basic_evm_tester { memcpy(result.bytes, out.data.data()+out.data.size() - silkworm::kAddressLength, silkworm::kAddressLength); return result; } - }; BOOST_AUTO_TEST_SUITE(call_evm_tests) @@ -249,4 +251,38 @@ BOOST_FIXTURE_TEST_CASE(admincall_test_function, call_evm_tester) try { } FC_LOG_AND_RETHROW() + +BOOST_FIXTURE_TEST_CASE(deploy_contract_function, call_evm_tester) try { + auto alice_addr = make_reserved_address("alice"_n.to_uint64_t()); + open("alice"_n); + transfer_token("alice"_n, "evm"_n, make_asset(1000000), "alice"); + + + auto to = evmc::bytes(); + + auto data = evmc::from_hex(contract_bytecode); + + call("alice"_n, to, 0, *data, 1000000, "alice"_n); // nonce 0->1 + + auto addr = silkworm::create_address(alice_addr, 0); + + call_test(addr, 1234, "alice"_n, "alice"_n); // nonce 1->2 + auto count = get_count(addr); + BOOST_REQUIRE(count == 1234); + + // Advance block so we do not generate same transaction. + produce_block(); + + auto from = evmc::bytes{std::begin(alice_addr.bytes), std::end(alice_addr.bytes)}; + + admincall(from, to, 0, *data, 1000000, "evm"_n); // nonce 2->3 + + addr = silkworm::create_address(alice_addr, 2); + call_test(addr, 2222, "alice"_n, "alice"_n); // nonce 3->4 + count = get_count(addr); + BOOST_REQUIRE(count == 2222); +} FC_LOG_AND_RETHROW() + + + BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file From a6bd60689b908b693c10e0647689b64c80a60623 Mon Sep 17 00:00:00 2001 From: yarkin Date: Fri, 4 Aug 2023 13:41:09 +0800 Subject: [PATCH 03/14] Change error message when inline pushtx actions fails and add tests for reverted call/admincall action. --- src/actions.cpp | 2 +- tests/call_tests.cpp | 39 +++++++++++++++++++++++---------------- 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/src/actions.cpp b/src/actions.cpp index 5ce4240d..d939034b 100644 --- a/src/actions.cpp +++ b/src/actions.cpp @@ -277,7 +277,7 @@ Receipt evm_contract::execute_tx( eosio::name miner, Block& block, Transaction& } if(from_self) - eosio::check(receipt.success, "ingress bridge actions must succeed"); + eosio::check(receipt.success, "inline actions must succeed"); if(!ep.state().reserved_objects().empty()) { bool non_open_account_sent = false; diff --git a/tests/call_tests.cpp b/tests/call_tests.cpp index 45dc9c2f..f1fd36e2 100644 --- a/tests/call_tests.cpp +++ b/tests/call_tests.cpp @@ -12,20 +12,7 @@ struct exec_output_row { FC_REFLECT(exec_output_row, (id)(output)) struct call_evm_tester : basic_evm_tester { - const std::string contract_bytecode = - "608060405234801561001057600080fd5b506102ad806100206000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c806306661abd1461004657806329e99f0714610064578063d097e7a614610080575b600080fd5b61004e61009e565b60405161005b919061013f565b60405180910390f35b61007e6004803603810190610079919061018b565b6100a4565b005b610088610100565b60405161009591906101f9565b60405180910390f35b60005481565b806000808282546100b59190610243565b9250508190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000819050919050565b61013981610126565b82525050565b60006020820190506101546000830184610130565b92915050565b600080fd5b61016881610126565b811461017357600080fd5b50565b6000813590506101858161015f565b92915050565b6000602082840312156101a1576101a061015a565b5b60006101af84828501610176565b91505092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006101e3826101b8565b9050919050565b6101f3816101d8565b82525050565b600060208201905061020e60008301846101ea565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061024e82610126565b915061025983610126565b925082820190508082111561027157610270610214565b5b9291505056fea2646970667358221220f1474068cf9aac836f8c5a31a9307e9bf225c1fccdedaf90eeaba493de1a9c9564736f6c63430008120033"; - - call_evm_tester() { - create_accounts({"alice"_n}); - transfer_token(faucet_account_name, "alice"_n, make_asset(10000'0000)); - create_accounts({"bob"_n}); - transfer_token(faucet_account_name, "bob"_n, make_asset(10000'0000)); - init(); - } - - evmc::address deploy_test_contract(evm_eoa& eoa) { - - /* + /* //SPDX-License-Identifier: lgplv3 pragma solidity ^0.8.0; @@ -34,13 +21,25 @@ struct call_evm_tester : basic_evm_tester { address public lastcaller; function test(uint256 input) public { + require(input != 0); + count += input; lastcaller = msg.sender; } } */ - const std::string contract_bytecode = - "608060405234801561001057600080fd5b506102ad806100206000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c806306661abd1461004657806329e99f0714610064578063d097e7a614610080575b600080fd5b61004e61009e565b60405161005b919061013f565b60405180910390f35b61007e6004803603810190610079919061018b565b6100a4565b005b610088610100565b60405161009591906101f9565b60405180910390f35b60005481565b806000808282546100b59190610243565b9250508190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000819050919050565b61013981610126565b82525050565b60006020820190506101546000830184610130565b92915050565b600080fd5b61016881610126565b811461017357600080fd5b50565b6000813590506101858161015f565b92915050565b6000602082840312156101a1576101a061015a565b5b60006101af84828501610176565b91505092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006101e3826101b8565b9050919050565b6101f3816101d8565b82525050565b600060208201905061020e60008301846101ea565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061024e82610126565b915061025983610126565b925082820190508082111561027157610270610214565b5b9291505056fea2646970667358221220f1474068cf9aac836f8c5a31a9307e9bf225c1fccdedaf90eeaba493de1a9c9564736f6c63430008120033"; + const std::string contract_bytecode = + "608060405234801561001057600080fd5b506102ba806100206000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c806306661abd1461004657806329e99f0714610064578063d097e7a614610080575b600080fd5b61004e61009e565b60405161005b919061014c565b60405180910390f35b61007e60048036038101906100799190610198565b6100a4565b005b61008861010d565b6040516100959190610206565b60405180910390f35b60005481565b600081036100b157600080fd5b806000808282546100c29190610250565b9250508190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000819050919050565b61014681610133565b82525050565b6000602082019050610161600083018461013d565b92915050565b600080fd5b61017581610133565b811461018057600080fd5b50565b6000813590506101928161016c565b92915050565b6000602082840312156101ae576101ad610167565b5b60006101bc84828501610183565b91505092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006101f0826101c5565b9050919050565b610200816101e5565b82525050565b600060208201905061021b60008301846101f7565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061025b82610133565b915061026683610133565b925082820190508082111561027e5761027d610221565b5b9291505056fea2646970667358221220edc009a00fe897643f99e8327c7fb355c96d7b91fb8a7da507513c6b2341acc564736f6c63430008120033"; + + call_evm_tester() { + create_accounts({"alice"_n}); + transfer_token(faucet_account_name, "alice"_n, make_asset(10000'0000)); + create_accounts({"bob"_n}); + transfer_token(faucet_account_name, "bob"_n, make_asset(10000'0000)); + init(); + } + + evmc::address deploy_test_contract(evm_eoa& eoa) { // Deploy token contract return deploy_contract(eoa, evmc::from_hex(contract_bytecode).value()); } @@ -155,6 +154,10 @@ BOOST_FIXTURE_TEST_CASE(call_test_function, call_evm_tester) try { BOOST_REQUIRE(intx::uint256(vault_balance("alice"_n)) == intx::exp<256>(10, 18) * 100); auto total_fund2 = intx::uint256(vault_balance("alice"_n)) + intx::uint256(vault_balance("evm"_n)); + BOOST_REQUIRE_EXCEPTION(call_test(token_addr, 0, "alice"_n, "alice"_n), + eosio_assert_message_exception, eosio_assert_message_is("inline actions must succeed")); + + // Call and check results call_test(token_addr, 1234, "alice"_n, "alice"_n); auto count = get_count(token_addr); @@ -225,6 +228,10 @@ BOOST_FIXTURE_TEST_CASE(admincall_test_function, call_evm_tester) try { BOOST_REQUIRE(evm_balance(evm2) == intx::exp<256>(10, 18 - 4) * 10100); auto total_fund2 = intx::uint256(vault_balance("evm"_n)) + *evm_balance(evm2); + + BOOST_REQUIRE_EXCEPTION(admincall_test(token_addr, 0, evm2, "evm"_n), + eosio_assert_message_exception, eosio_assert_message_is("inline actions must succeed")); + // Call and check results admincall_test(token_addr, 1234, evm2, "evm"_n); From 48513d540df7a8510c4bae8bce7ebfd361b24553 Mon Sep 17 00:00:00 2001 From: yarkin Date: Fri, 4 Aug 2023 13:43:29 +0800 Subject: [PATCH 04/14] update submodule silkworm --- silkworm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/silkworm b/silkworm index 2f000a5c..55a46c32 160000 --- a/silkworm +++ b/silkworm @@ -1 +1 @@ -Subproject commit 2f000a5c5ae74da14e97333ef37bae8b059c63ae +Subproject commit 55a46c321b9cf321729248831956be65067e2fd2 From 867fa2cc19052f641c17ac42e3d11e71b563bea7 Mon Sep 17 00:00:00 2001 From: yarkin Date: Mon, 7 Aug 2023 15:02:38 +0800 Subject: [PATCH 05/14] Add missing const. --- include/evm_runtime/evm_contract.hpp | 6 +++--- src/actions.cpp | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/evm_runtime/evm_contract.hpp b/include/evm_runtime/evm_contract.hpp index c3a96583..cd737d10 100644 --- a/include/evm_runtime/evm_contract.hpp +++ b/include/evm_runtime/evm_contract.hpp @@ -85,8 +85,8 @@ class [[eosio::contract]] evm_contract : public contract [[eosio::action]] bool gc(uint32_t max); - [[eosio::action]] void call(eosio::name from, const bytes& to, uint128_t value, bytes& data, uint64_t gas_limit); - [[eosio::action]] void admincall(const bytes& from, const bytes& to, uint128_t value, bytes& data, uint64_t gas_limit); + [[eosio::action]] void call(eosio::name from, const bytes& to, uint128_t value, const bytes& data, uint64_t gas_limit); + [[eosio::action]] void admincall(const bytes& from, const bytes& to, uint128_t value, const bytes& data, uint64_t gas_limit); #ifdef WITH_TEST_ACTIONS [[eosio::action]] void testtx(const std::optional& orlptx, const evm_runtime::test::block_info& bi); @@ -144,7 +144,7 @@ class [[eosio::contract]] evm_contract : public contract void handle_account_transfer(const eosio::asset& quantity, const std::string& memo); void handle_evm_transfer(eosio::asset quantity, const std::string& memo); - void call_(intx::uint256 s, const bytes& to, intx::uint256 value, bytes& data, uint64_t gas_limit, uint64_t nonce); + void call_(intx::uint256 s, const bytes& to, intx::uint256 value, const bytes& data, uint64_t gas_limit, uint64_t nonce); // to allow sending through a Bytes (basic_string) w/o copying over to a std::vector void pushtx_bytes(eosio::name miner, const std::basic_string& rlptx); diff --git a/src/actions.cpp b/src/actions.cpp index d939034b..898d0602 100644 --- a/src/actions.cpp +++ b/src/actions.cpp @@ -563,7 +563,7 @@ bool evm_contract::gc(uint32_t max) { return state.gc(max); } -void evm_contract::call_(intx::uint256 s, const bytes& to, intx::uint256 value, bytes& data, uint64_t gas_limit, uint64_t nonce) { +void evm_contract::call_(intx::uint256 s, const bytes& to, intx::uint256 value, const bytes& data, uint64_t gas_limit, uint64_t nonce) { const auto& current_config = _config.get(); Transaction txn; @@ -589,14 +589,14 @@ void evm_contract::call_(intx::uint256 s, const bytes& to, intx::uint256 value, pushtx_act.send(get_self(), rlp); } -void evm_contract::call(eosio::name from, const bytes& to, uint128_t value, bytes& data, uint64_t gas_limit) { +void evm_contract::call(eosio::name from, const bytes& to, uint128_t value, const bytes& data, uint64_t gas_limit) { assert_unfrozen(); require_auth(from); call_(from.value, to, intx::uint256(value), data, gas_limit, get_and_increment_nonce(from)); } -void evm_contract::admincall(const bytes& from, const bytes& to, uint128_t value, bytes& data, uint64_t gas_limit) { +void evm_contract::admincall(const bytes& from, const bytes& to, uint128_t value, const bytes& data, uint64_t gas_limit) { assert_unfrozen(); require_auth(get_self()); From bc173c30ea824a7dc13f07a288c84f58d562dbcb Mon Sep 17 00:00:00 2001 From: yarkin Date: Wed, 9 Aug 2023 16:48:37 +0800 Subject: [PATCH 06/14] Optimze logic when processing tx with special signature. --- src/actions.cpp | 49 ++++++++++++++++++++++---------------------- tests/call_tests.cpp | 4 ++-- 2 files changed, 26 insertions(+), 27 deletions(-) diff --git a/src/actions.cpp b/src/actions.cpp index 898d0602..91b792c2 100644 --- a/src/actions.cpp +++ b/src/actions.cpp @@ -220,39 +220,38 @@ Receipt evm_contract::execute_tx( eosio::name miner, Block& block, Transaction& inevm.emplace(get_self(), get_self().value); }; - bool is_special_signature = (tx.r == intx::uint256()); + bool is_special_signature = silkworm::is_special_signature(tx.r, tx.s); tx.from.reset(); tx.recover_sender(); eosio::check(tx.from.has_value(), "unable to recover sender"); LOGTIME("EVM RECOVER SENDER"); - // type 1: normal signature (normal address recovered from normal signature), required !from_self - // type 2: special signature (r == 0), reserved address (stored in s), required from_self + reduce special balance - // type 3: special signature (r == 0), normal address (stored in s), required from_self - if(from_self) { - check(is_special_signature, "actions from self without a special signature are unexpected"); - if (is_reserved_address(*tx.from)) { - const name ingress_account(*extract_reserved_address(*tx.from)); - - const intx::uint512 max_gas_cost = intx::uint256(tx.gas_limit) * tx.max_fee_per_gas; - check(max_gas_cost + tx.value < std::numeric_limits::max(), "too much gas"); - const intx::uint256 value_with_max_gas = tx.value + (intx::uint256)max_gas_cost; - - populate_bridge_accessors(); - balance_table.modify(balance_table.get(ingress_account.value), eosio::same_payer, [&](balance& b){ - b.balance -= value_with_max_gas; - }); - inevm->set(inevm->get() += value_with_max_gas, eosio::same_payer); + // 1 Reject special signature NOT from self. + // 2 All other cases (normal signature or special signature from self) will be accepted. + // 3 If the from address is reserved address, the special balance needs some process. + check(from_self || !is_special_signature, "bridge signature used outside of bridge transaction"); + + if (is_reserved_address(*tx.from)) { + const name ingress_account(*extract_reserved_address(*tx.from)); - ep.state().set_balance(*tx.from, value_with_max_gas); - ep.state().set_nonce(*tx.from, tx.nonce); - } + const intx::uint512 max_gas_cost = intx::uint256(tx.gas_limit) * tx.max_fee_per_gas; + check(max_gas_cost + tx.value < std::numeric_limits::max(), "too much gas"); + const intx::uint256 value_with_max_gas = tx.value + (intx::uint256)max_gas_cost; + + populate_bridge_accessors(); + balance_table.modify(balance_table.get(ingress_account.value), eosio::same_payer, [&](balance& b){ + b.balance -= value_with_max_gas; + }); + inevm->set(inevm->get() += value_with_max_gas, eosio::same_payer); + + ep.state().set_balance(*tx.from, value_with_max_gas); + ep.state().set_nonce(*tx.from, tx.nonce); } - else if(is_special_signature) - check(false, "bridge signature used outside of bridge transaction"); - if(enforce_chain_id && !from_self) { + // A tx from self with regular signature can potentially from external source. + // Therefore, only tx with special signature can waive the chain_id check. + if(enforce_chain_id && !is_special_signature) { check(tx.chain_id.has_value(), "tx without chain-id"); } @@ -277,7 +276,7 @@ Receipt evm_contract::execute_tx( eosio::name miner, Block& block, Transaction& } if(from_self) - eosio::check(receipt.success, "inline actions must succeed"); + eosio::check(receipt.success, "tx executed inline by contract must succeed"); if(!ep.state().reserved_objects().empty()) { bool non_open_account_sent = false; diff --git a/tests/call_tests.cpp b/tests/call_tests.cpp index f1fd36e2..b01f8306 100644 --- a/tests/call_tests.cpp +++ b/tests/call_tests.cpp @@ -155,7 +155,7 @@ BOOST_FIXTURE_TEST_CASE(call_test_function, call_evm_tester) try { auto total_fund2 = intx::uint256(vault_balance("alice"_n)) + intx::uint256(vault_balance("evm"_n)); BOOST_REQUIRE_EXCEPTION(call_test(token_addr, 0, "alice"_n, "alice"_n), - eosio_assert_message_exception, eosio_assert_message_is("inline actions must succeed")); + eosio_assert_message_exception, eosio_assert_message_is("tx executed inline by contract must succeed")); // Call and check results @@ -230,7 +230,7 @@ BOOST_FIXTURE_TEST_CASE(admincall_test_function, call_evm_tester) try { BOOST_REQUIRE_EXCEPTION(admincall_test(token_addr, 0, evm2, "evm"_n), - eosio_assert_message_exception, eosio_assert_message_is("inline actions must succeed")); + eosio_assert_message_exception, eosio_assert_message_is("tx executed inline by contract must succeed")); // Call and check results admincall_test(token_addr, 1234, evm2, "evm"_n); From 4a22a2279b91a2277f9db162441cbb1ca507bb9d Mon Sep 17 00:00:00 2001 From: yarkin Date: Wed, 9 Aug 2023 16:49:18 +0800 Subject: [PATCH 07/14] Fix test styles. --- tests/call_tests.cpp | 54 ++++++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/tests/call_tests.cpp b/tests/call_tests.cpp index b01f8306..b83db0e3 100644 --- a/tests/call_tests.cpp +++ b/tests/call_tests.cpp @@ -119,19 +119,19 @@ struct call_evm_tester : basic_evm_tester { BOOST_AUTO_TEST_SUITE(call_evm_tests) BOOST_FIXTURE_TEST_CASE(call_test_function, call_evm_tester) try { evm_eoa evm1; - auto total_fund = intx::uint256(vault_balance("evm"_n)); + auto total_fund = intx::uint256(vault_balance(evm_account_name)); // Fund evm1 address with 100 EOS - transfer_token("alice"_n, "evm"_n, make_asset(1000000), evm1.address_0x()); + transfer_token("alice"_n, evm_account_name, make_asset(1000000), evm1.address_0x()); auto evm1_balance = evm_balance(evm1); BOOST_REQUIRE(!!evm1_balance); - BOOST_REQUIRE(*evm1_balance == intx::exp<256>(10, 18) * 100); + BOOST_REQUIRE(*evm1_balance == 100_ether); total_fund += *evm1_balance; // Deploy contract auto token_addr = deploy_test_contract(evm1); // Deployment gas fee go to evm vault - BOOST_REQUIRE(*evm_balance(evm1) + intx::uint256(vault_balance("evm"_n)) == total_fund); + BOOST_REQUIRE(*evm_balance(evm1) + intx::uint256(vault_balance(evm_account_name)) == total_fund); // Missing authority BOOST_REQUIRE_EXCEPTION(call_test(token_addr, 1234, "alice"_n, "bob"_n), @@ -149,10 +149,10 @@ BOOST_FIXTURE_TEST_CASE(call_test_function, call_evm_tester) try { eosio_assert_message_exception, eosio_assert_message_is("decrementing more than available")); // Transfer enough funds - transfer_token("alice"_n, "evm"_n, make_asset(1000000), "alice"); + transfer_token("alice"_n, evm_account_name, make_asset(1000000), "alice"); - BOOST_REQUIRE(intx::uint256(vault_balance("alice"_n)) == intx::exp<256>(10, 18) * 100); - auto total_fund2 = intx::uint256(vault_balance("alice"_n)) + intx::uint256(vault_balance("evm"_n)); + BOOST_REQUIRE(intx::uint256(vault_balance("alice"_n)) == 100_ether); + auto total_fund2 = intx::uint256(vault_balance("alice"_n)) + intx::uint256(vault_balance(evm_account_name)); BOOST_REQUIRE_EXCEPTION(call_test(token_addr, 0, "alice"_n, "alice"_n), eosio_assert_message_exception, eosio_assert_message_is("tx executed inline by contract must succeed")); @@ -164,7 +164,7 @@ BOOST_FIXTURE_TEST_CASE(call_test_function, call_evm_tester) try { BOOST_REQUIRE(count == 1234); // Gas go from alice's vault to evm's vault - BOOST_REQUIRE(total_fund2 == intx::uint256(vault_balance("alice"_n)) + intx::uint256(vault_balance("evm"_n))); + BOOST_REQUIRE(total_fund2 == intx::uint256(vault_balance("alice"_n)) + intx::uint256(vault_balance(evm_account_name))); // Advance block so we do not generate same transaction. @@ -175,7 +175,7 @@ BOOST_FIXTURE_TEST_CASE(call_test_function, call_evm_tester) try { BOOST_REQUIRE(count == 5555); // Gas go from alice's vault to evm's vault - BOOST_REQUIRE(total_fund2 == intx::uint256(vault_balance("alice"_n)) + intx::uint256(vault_balance("evm"_n))); + BOOST_REQUIRE(total_fund2 == intx::uint256(vault_balance("alice"_n)) + intx::uint256(vault_balance(evm_account_name))); // Function being called on behalf of reserved address of eos account "alice" auto caller = get_lastcaller(token_addr); @@ -187,69 +187,69 @@ BOOST_FIXTURE_TEST_CASE(admincall_test_function, call_evm_tester) try { evm_eoa evm1; evm_eoa evm2; - auto total_fund = intx::uint256(vault_balance("evm"_n)); + auto total_fund = intx::uint256(vault_balance(evm_account_name)); // Fund evm1 address with 100 EOS - transfer_token("alice"_n, "evm"_n, make_asset(1000000), evm1.address_0x()); + transfer_token("alice"_n, evm_account_name, make_asset(1000000), evm1.address_0x()); auto evm1_balance = evm_balance(evm1); BOOST_REQUIRE(!!evm1_balance); - BOOST_REQUIRE(*evm1_balance == intx::exp<256>(10, 18) * 100); + BOOST_REQUIRE(*evm1_balance == 100_ether); total_fund += *evm1_balance; // Deploy contract auto token_addr = deploy_test_contract(evm1); // Deployment gas fee go to evm vault - BOOST_REQUIRE(*evm_balance(evm1) + intx::uint256(vault_balance("evm"_n)) == total_fund); + BOOST_REQUIRE(*evm_balance(evm1) + intx::uint256(vault_balance(evm_account_name)) == total_fund); // Missing authority BOOST_REQUIRE_EXCEPTION(admincall_test(token_addr, 1234, evm2, "alice"_n), missing_auth_exception, eosio::testing::fc_exception_message_starts_with("missing authority")); // Account not created - BOOST_REQUIRE_EXCEPTION( admincall_test(token_addr, 1234, evm2, "evm"_n), + BOOST_REQUIRE_EXCEPTION( admincall_test(token_addr, 1234, evm2, evm_account_name), eosio_assert_message_exception, eosio_assert_message_is("invalid address")); // Transfer small amount to create account - transfer_token("alice"_n, "evm"_n, make_asset(100), evm2.address_0x()); + transfer_token("alice"_n, evm_account_name, make_asset(100), evm2.address_0x()); auto evm2_balance = evm_balance(evm2); BOOST_REQUIRE(!!evm2_balance); - BOOST_REQUIRE(*evm2_balance == intx::exp<256>(10, 18 - 4) * 100); + BOOST_REQUIRE(*evm2_balance == 10_finney); // Insufficient funds - BOOST_REQUIRE_EXCEPTION( admincall_test(token_addr, 1234, evm2, "evm"_n), + BOOST_REQUIRE_EXCEPTION( admincall_test(token_addr, 1234, evm2, evm_account_name), eosio_assert_message_exception, eosio_assert_message_is("validate_transaction error: 23 Insufficient funds")); // Transfer enough funds - transfer_token("alice"_n, "evm"_n, make_asset(10000), evm2.address_0x()); + transfer_token("alice"_n, evm_account_name, make_asset(10000), evm2.address_0x()); - BOOST_REQUIRE(evm_balance(evm2) == intx::exp<256>(10, 18 - 4) * 10100); - auto total_fund2 = intx::uint256(vault_balance("evm"_n)) + *evm_balance(evm2); + BOOST_REQUIRE(evm_balance(evm2) == 1010_finney); + auto total_fund2 = intx::uint256(vault_balance(evm_account_name)) + *evm_balance(evm2); - BOOST_REQUIRE_EXCEPTION(admincall_test(token_addr, 0, evm2, "evm"_n), + BOOST_REQUIRE_EXCEPTION(admincall_test(token_addr, 0, evm2, evm_account_name), eosio_assert_message_exception, eosio_assert_message_is("tx executed inline by contract must succeed")); // Call and check results - admincall_test(token_addr, 1234, evm2, "evm"_n); + admincall_test(token_addr, 1234, evm2, evm_account_name); auto count = get_count(token_addr); BOOST_REQUIRE(count == 1234); // Gas go from evm2 to evm vault - BOOST_REQUIRE(total_fund2 == *evm_balance(evm2) + intx::uint256(vault_balance("evm"_n))); + BOOST_REQUIRE(total_fund2 == *evm_balance(evm2) + intx::uint256(vault_balance(evm_account_name))); // Advance block so we do not generate same transaction. produce_block(); - admincall_test(token_addr, 4321, evm2, "evm"_n); + admincall_test(token_addr, 4321, evm2, evm_account_name); count = get_count(token_addr); BOOST_REQUIRE(count == 5555); // Gas go from evm2 to evm vault - BOOST_REQUIRE(total_fund2 == *evm_balance(evm2) + intx::uint256(vault_balance("evm"_n))); + BOOST_REQUIRE(total_fund2 == *evm_balance(evm2) + intx::uint256(vault_balance(evm_account_name))); // Function being called on behalf of evm address "evm2" auto caller = get_lastcaller(token_addr); @@ -262,7 +262,7 @@ BOOST_FIXTURE_TEST_CASE(admincall_test_function, call_evm_tester) try { BOOST_FIXTURE_TEST_CASE(deploy_contract_function, call_evm_tester) try { auto alice_addr = make_reserved_address("alice"_n.to_uint64_t()); open("alice"_n); - transfer_token("alice"_n, "evm"_n, make_asset(1000000), "alice"); + transfer_token("alice"_n, evm_account_name, make_asset(1000000), "alice"); auto to = evmc::bytes(); @@ -282,7 +282,7 @@ BOOST_FIXTURE_TEST_CASE(deploy_contract_function, call_evm_tester) try { auto from = evmc::bytes{std::begin(alice_addr.bytes), std::end(alice_addr.bytes)}; - admincall(from, to, 0, *data, 1000000, "evm"_n); // nonce 2->3 + admincall(from, to, 0, *data, 1000000, evm_account_name); // nonce 2->3 addr = silkworm::create_address(alice_addr, 2); call_test(addr, 2222, "alice"_n, "alice"_n); // nonce 3->4 From 8156407204a722c094cddba364d1abd6cc70c1a2 Mon Sep 17 00:00:00 2001 From: yarkin Date: Wed, 9 Aug 2023 19:03:01 +0800 Subject: [PATCH 08/14] More detailed tests for gas fee. --- tests/call_tests.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/tests/call_tests.cpp b/tests/call_tests.cpp index b83db0e3..9870f810 100644 --- a/tests/call_tests.cpp +++ b/tests/call_tests.cpp @@ -28,6 +28,10 @@ struct call_evm_tester : basic_evm_tester { } } */ + // Cost for first time call to test(), extra cost is needed for the lastcaller storage. + const intx::uint256 gas_fee = suggested_gas_price * 63526; + // Cost for other calls to test() + const intx::uint256 gas_fee2 = suggested_gas_price * 29326; const std::string contract_bytecode = "608060405234801561001057600080fd5b506102ba806100206000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c806306661abd1461004657806329e99f0714610064578063d097e7a614610080575b600080fd5b61004e61009e565b60405161005b919061014c565b60405180910390f35b61007e60048036038101906100799190610198565b6100a4565b005b61008861010d565b6040516100959190610206565b60405180910390f35b60005481565b600081036100b157600080fd5b806000808282546100c29190610250565b9250508190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000819050919050565b61014681610133565b82525050565b6000602082019050610161600083018461013d565b92915050565b600080fd5b61017581610133565b811461018057600080fd5b50565b6000813590506101928161016c565b92915050565b6000602082840312156101ae576101ad610167565b5b60006101bc84828501610183565b91505092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006101f0826101c5565b9050919050565b610200816101e5565b82525050565b600060208201905061021b60008301846101f7565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061025b82610133565b915061026683610133565b925082820190508082111561027e5761027d610221565b5b9291505056fea2646970667358221220edc009a00fe897643f99e8327c7fb355c96d7b91fb8a7da507513c6b2341acc564736f6c63430008120033"; @@ -162,7 +166,7 @@ BOOST_FIXTURE_TEST_CASE(call_test_function, call_evm_tester) try { call_test(token_addr, 1234, "alice"_n, "alice"_n); auto count = get_count(token_addr); BOOST_REQUIRE(count == 1234); - + BOOST_REQUIRE(intx::uint256(vault_balance("alice"_n)) == 100_ether - gas_fee); // Gas go from alice's vault to evm's vault BOOST_REQUIRE(total_fund2 == intx::uint256(vault_balance("alice"_n)) + intx::uint256(vault_balance(evm_account_name))); @@ -173,7 +177,7 @@ BOOST_FIXTURE_TEST_CASE(call_test_function, call_evm_tester) try { call_test(token_addr, 4321, "alice"_n, "alice"_n); count = get_count(token_addr); BOOST_REQUIRE(count == 5555); - + BOOST_REQUIRE(intx::uint256(vault_balance("alice"_n)) == 100_ether - gas_fee - gas_fee2); // Gas go from alice's vault to evm's vault BOOST_REQUIRE(total_fund2 == intx::uint256(vault_balance("alice"_n)) + intx::uint256(vault_balance(evm_account_name))); @@ -232,12 +236,13 @@ BOOST_FIXTURE_TEST_CASE(admincall_test_function, call_evm_tester) try { BOOST_REQUIRE_EXCEPTION(admincall_test(token_addr, 0, evm2, evm_account_name), eosio_assert_message_exception, eosio_assert_message_is("tx executed inline by contract must succeed")); + // Call and check results admincall_test(token_addr, 1234, evm2, evm_account_name); auto count = get_count(token_addr); BOOST_REQUIRE(count == 1234); - + BOOST_REQUIRE(evm_balance(evm2) == 1010_finney - gas_fee); // Gas go from evm2 to evm vault BOOST_REQUIRE(total_fund2 == *evm_balance(evm2) + intx::uint256(vault_balance(evm_account_name))); @@ -247,7 +252,7 @@ BOOST_FIXTURE_TEST_CASE(admincall_test_function, call_evm_tester) try { admincall_test(token_addr, 4321, evm2, evm_account_name); count = get_count(token_addr); BOOST_REQUIRE(count == 5555); - + BOOST_REQUIRE(evm_balance(evm2) == 1010_finney - gas_fee - gas_fee2); // Gas go from evm2 to evm vault BOOST_REQUIRE(total_fund2 == *evm_balance(evm2) + intx::uint256(vault_balance(evm_account_name))); From 4b35e5e499221f4d21dccb70d3fe5813f8231f3c Mon Sep 17 00:00:00 2001 From: yarkin Date: Wed, 9 Aug 2023 19:15:38 +0800 Subject: [PATCH 09/14] Update submodule --- silkworm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/silkworm b/silkworm index 55a46c32..3f4e257f 160000 --- a/silkworm +++ b/silkworm @@ -1 +1 @@ -Subproject commit 55a46c321b9cf321729248831956be65067e2fd2 +Subproject commit 3f4e257f582542e1de45e486af33e0f1d48088a0 From 33c066bd1122425184077b74b0b445a36a732d45 Mon Sep 17 00:00:00 2001 From: yarkin Date: Thu, 10 Aug 2023 10:34:46 +0800 Subject: [PATCH 10/14] improve bridge tx handling logic. --- src/actions.cpp | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/src/actions.cpp b/src/actions.cpp index 91b792c2..60e1f461 100644 --- a/src/actions.cpp +++ b/src/actions.cpp @@ -227,26 +227,28 @@ Receipt evm_contract::execute_tx( eosio::name miner, Block& block, Transaction& eosio::check(tx.from.has_value(), "unable to recover sender"); LOGTIME("EVM RECOVER SENDER"); - // 1 Reject special signature NOT from self. - // 2 All other cases (normal signature or special signature from self) will be accepted. - // 3 If the from address is reserved address, the special balance needs some process. - check(from_self || !is_special_signature, "bridge signature used outside of bridge transaction"); - - if (is_reserved_address(*tx.from)) { - const name ingress_account(*extract_reserved_address(*tx.from)); - - const intx::uint512 max_gas_cost = intx::uint256(tx.gas_limit) * tx.max_fee_per_gas; - check(max_gas_cost + tx.value < std::numeric_limits::max(), "too much gas"); - const intx::uint256 value_with_max_gas = tx.value + (intx::uint256)max_gas_cost; - - populate_bridge_accessors(); - balance_table.modify(balance_table.get(ingress_account.value), eosio::same_payer, [&](balance& b){ - b.balance -= value_with_max_gas; - }); - inevm->set(inevm->get() += value_with_max_gas, eosio::same_payer); + // 1 For regular signature, it's impossible to from reserved address, + // and now we accpet them regardless from self or not, so no special treatment. + // 2 For special signature, we will reject calls not from self + // and process the special balance if the tx is from reserved address. + if (is_special_signature) { + check(from_self, "bridge signature used outside of bridge transaction"); + if (is_reserved_address(*tx.from)) { + const name ingress_account(*extract_reserved_address(*tx.from)); + + const intx::uint512 max_gas_cost = intx::uint256(tx.gas_limit) * tx.max_fee_per_gas; + check(max_gas_cost + tx.value < std::numeric_limits::max(), "too much gas"); + const intx::uint256 value_with_max_gas = tx.value + (intx::uint256)max_gas_cost; + + populate_bridge_accessors(); + balance_table.modify(balance_table.get(ingress_account.value), eosio::same_payer, [&](balance& b){ + b.balance -= value_with_max_gas; + }); + inevm->set(inevm->get() += value_with_max_gas, eosio::same_payer); - ep.state().set_balance(*tx.from, value_with_max_gas); - ep.state().set_nonce(*tx.from, tx.nonce); + ep.state().set_balance(*tx.from, value_with_max_gas); + ep.state().set_nonce(*tx.from, tx.nonce); + } } // A tx from self with regular signature can potentially from external source. From 5d9afcd340fb5afd371ad453fec3b1378f2ddfcf Mon Sep 17 00:00:00 2001 From: yarkin Date: Thu, 10 Aug 2023 22:24:21 +0800 Subject: [PATCH 11/14] Only intx::uint128's ctor can work properly with uint128_t. We may consider fix that in intx. --- src/actions.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/actions.cpp b/src/actions.cpp index 60e1f461..2da82a09 100644 --- a/src/actions.cpp +++ b/src/actions.cpp @@ -594,7 +594,7 @@ void evm_contract::call(eosio::name from, const bytes& to, uint128_t value, cons assert_unfrozen(); require_auth(from); - call_(from.value, to, intx::uint256(value), data, gas_limit, get_and_increment_nonce(from)); + call_(from.value, to, intx::uint256(intx::uint128(value)), data, gas_limit, get_and_increment_nonce(from)); } void evm_contract::admincall(const bytes& from, const bytes& to, uint128_t value, const bytes& data, uint64_t gas_limit) { @@ -623,7 +623,7 @@ void evm_contract::admincall(const bytes& from, const bytes& to, uint128_t value nonce = account->nonce; } - call_(s, to, intx::uint256(value), data, gas_limit, nonce); + call_(s, to, intx::uint256(intx::uint128(value)), data, gas_limit, nonce); } #ifdef WITH_TEST_ACTIONS From 8101a513d048c376cea6d28c04993580be09cef7 Mon Sep 17 00:00:00 2001 From: yarkin Date: Thu, 10 Aug 2023 22:26:27 +0800 Subject: [PATCH 12/14] Add tests for calls with payments. --- tests/call_tests.cpp | 244 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 211 insertions(+), 33 deletions(-) diff --git a/tests/call_tests.cpp b/tests/call_tests.cpp index 9870f810..0384f12d 100644 --- a/tests/call_tests.cpp +++ b/tests/call_tests.cpp @@ -17,23 +17,37 @@ struct call_evm_tester : basic_evm_tester { pragma solidity ^0.8.0; contract Test { - uint256 public count; - address public lastcaller; + uint256 public count; + address public lastcaller; - function test(uint256 input) public { - require(input != 0); + function test(uint256 input) public { + require(input != 0); + + count += input; + lastcaller = msg.sender; + } + + function testpay() payable public { + + } + + function notpayable() public { + + } - count += input; - lastcaller = msg.sender; - } } */ // Cost for first time call to test(), extra cost is needed for the lastcaller storage. const intx::uint256 gas_fee = suggested_gas_price * 63526; // Cost for other calls to test() const intx::uint256 gas_fee2 = suggested_gas_price * 29326; + // Cost for calls to testpay() + const intx::uint256 gas_fee_testpay = suggested_gas_price * 21206; + // Cost for calls to notpayable with 0 value + const intx::uint256 gas_fee_notpayable = suggested_gas_price * 21274; + const std::string contract_bytecode = - "608060405234801561001057600080fd5b506102ba806100206000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c806306661abd1461004657806329e99f0714610064578063d097e7a614610080575b600080fd5b61004e61009e565b60405161005b919061014c565b60405180910390f35b61007e60048036038101906100799190610198565b6100a4565b005b61008861010d565b6040516100959190610206565b60405180910390f35b60005481565b600081036100b157600080fd5b806000808282546100c29190610250565b9250508190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000819050919050565b61014681610133565b82525050565b6000602082019050610161600083018461013d565b92915050565b600080fd5b61017581610133565b811461018057600080fd5b50565b6000813590506101928161016c565b92915050565b6000602082840312156101ae576101ad610167565b5b60006101bc84828501610183565b91505092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006101f0826101c5565b9050919050565b610200816101e5565b82525050565b600060208201905061021b60008301846101f7565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061025b82610133565b915061026683610133565b925082820190508082111561027e5761027d610221565b5b9291505056fea2646970667358221220edc009a00fe897643f99e8327c7fb355c96d7b91fb8a7da507513c6b2341acc564736f6c63430008120033"; + "608060405234801561001057600080fd5b5061030f806100206000396000f3fe60806040526004361061004a5760003560e01c806306661abd1461004f57806329e99f071461007a578063a1a7d817146100a3578063d097e7a6146100ad578063d79e1b6a146100d8575b600080fd5b34801561005b57600080fd5b506100646100ef565b60405161007191906101a1565b60405180910390f35b34801561008657600080fd5b506100a1600480360381019061009c91906101ed565b6100f5565b005b6100ab61015e565b005b3480156100b957600080fd5b506100c2610160565b6040516100cf919061025b565b60405180910390f35b3480156100e457600080fd5b506100ed610186565b005b60005481565b6000810361010257600080fd5b8060008082825461011391906102a5565b9250508190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b565b6000819050919050565b61019b81610188565b82525050565b60006020820190506101b66000830184610192565b92915050565b600080fd5b6101ca81610188565b81146101d557600080fd5b50565b6000813590506101e7816101c1565b92915050565b600060208284031215610203576102026101bc565b5b6000610211848285016101d8565b91505092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006102458261021a565b9050919050565b6102558161023a565b82525050565b6000602082019050610270600083018461024c565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60006102b082610188565b91506102bb83610188565b92508282019050808211156102d3576102d2610276565b5b9291505056fea2646970667358221220ed95d8f74110a8eb6307b7ae52b8623fd3e959169b208830a960c99a9ba1dbf564736f6c63430008120033"; call_evm_tester() { create_accounts({"alice"_n}); @@ -59,6 +73,48 @@ struct call_evm_tester : basic_evm_tester { call(eos, to, 0, data, 500000, actor); } + void call_testpay(const evmc::address& contract_addr, uint128_t amount, name eos, name actor) { + + auto to = evmc::bytes{std::begin(contract_addr.bytes), std::end(contract_addr.bytes)}; + + silkworm::Bytes data; + data += evmc::from_hex("a1a7d817").value(); // sha3(testpay())[:4] + + call(eos, to, amount, data, 500000, actor); + } + + void call_notpayable(const evmc::address& contract_addr, uint128_t amount, name eos, name actor) { + + auto to = evmc::bytes{std::begin(contract_addr.bytes), std::end(contract_addr.bytes)}; + + silkworm::Bytes data; + data += evmc::from_hex("d79e1b6a").value(); // sha3(notpayable())[:4] + + call(eos, to, amount, data, 500000, actor); + } + + void admincall_testpay(const evmc::address& contract_addr, uint128_t amount, evm_eoa& eoa, name actor) { + + auto to = evmc::bytes{std::begin(contract_addr.bytes), std::end(contract_addr.bytes)}; + auto from = evmc::bytes{std::begin(eoa.address.bytes), std::end(eoa.address.bytes)}; + + silkworm::Bytes data; + data += evmc::from_hex("a1a7d817").value(); // sha3(testpay())[:4] + + admincall(from, to, amount, data, 500000, actor); + } + + void admincall_notpayable(const evmc::address& contract_addr, uint128_t amount, evm_eoa& eoa, name actor) { + + auto to = evmc::bytes{std::begin(contract_addr.bytes), std::end(contract_addr.bytes)}; + auto from = evmc::bytes{std::begin(eoa.address.bytes), std::end(eoa.address.bytes)}; + + silkworm::Bytes data; + data += evmc::from_hex("d79e1b6a").value(); // sha3(notpayable())[:4] + + admincall(from, to, amount, data, 500000, actor); + } + void admincall_test(const evmc::address& contract_addr, uint64_t amount, evm_eoa& eoa, name actor) { auto to = evmc::bytes{std::begin(contract_addr.bytes), std::end(contract_addr.bytes)}; auto from = evmc::bytes{std::begin(eoa.address.bytes), std::end(eoa.address.bytes)}; @@ -152,24 +208,28 @@ BOOST_FIXTURE_TEST_CASE(call_test_function, call_evm_tester) try { BOOST_REQUIRE_EXCEPTION(call_test(token_addr, 1234, "alice"_n, "alice"_n), eosio_assert_message_exception, eosio_assert_message_is("decrementing more than available")); - // Transfer enough funds + // Transfer enough funds, save initial balance value. transfer_token("alice"_n, evm_account_name, make_asset(1000000), "alice"); - - BOOST_REQUIRE(intx::uint256(vault_balance("alice"_n)) == 100_ether); - auto total_fund2 = intx::uint256(vault_balance("alice"_n)) + intx::uint256(vault_balance(evm_account_name)); + auto alice_balance = 100_ether; + BOOST_REQUIRE(intx::uint256(vault_balance("alice"_n)) == alice_balance); + auto evm_account_balance = intx::uint256(vault_balance(evm_account_name)); BOOST_REQUIRE_EXCEPTION(call_test(token_addr, 0, "alice"_n, "alice"_n), eosio_assert_message_exception, eosio_assert_message_is("tx executed inline by contract must succeed")); + BOOST_REQUIRE(intx::uint256(vault_balance("alice"_n)) == alice_balance); + BOOST_REQUIRE(intx::uint256(vault_balance(evm_account_name)) == evm_account_balance); // Call and check results call_test(token_addr, 1234, "alice"_n, "alice"_n); auto count = get_count(token_addr); BOOST_REQUIRE(count == 1234); - BOOST_REQUIRE(intx::uint256(vault_balance("alice"_n)) == 100_ether - gas_fee); - // Gas go from alice's vault to evm's vault - BOOST_REQUIRE(total_fund2 == intx::uint256(vault_balance("alice"_n)) + intx::uint256(vault_balance(evm_account_name))); + alice_balance -= gas_fee; + evm_account_balance += gas_fee; + + BOOST_REQUIRE(intx::uint256(vault_balance("alice"_n)) == alice_balance); + BOOST_REQUIRE(intx::uint256(vault_balance(evm_account_name)) == evm_account_balance); // Advance block so we do not generate same transaction. produce_block(); @@ -177,14 +237,73 @@ BOOST_FIXTURE_TEST_CASE(call_test_function, call_evm_tester) try { call_test(token_addr, 4321, "alice"_n, "alice"_n); count = get_count(token_addr); BOOST_REQUIRE(count == 5555); - BOOST_REQUIRE(intx::uint256(vault_balance("alice"_n)) == 100_ether - gas_fee - gas_fee2); - // Gas go from alice's vault to evm's vault - BOOST_REQUIRE(total_fund2 == intx::uint256(vault_balance("alice"_n)) + intx::uint256(vault_balance(evm_account_name))); + + alice_balance -= gas_fee2; + evm_account_balance += gas_fee2; + + BOOST_REQUIRE(intx::uint256(vault_balance("alice"_n)) == alice_balance); + BOOST_REQUIRE(intx::uint256(vault_balance(evm_account_name)) == evm_account_balance); + BOOST_REQUIRE(*evm_balance(token_addr) == 0); // Function being called on behalf of reserved address of eos account "alice" auto caller = get_lastcaller(token_addr); BOOST_REQUIRE(caller == make_reserved_address("alice"_n.to_uint64_t())); + + BOOST_REQUIRE_EXCEPTION(call_notpayable(token_addr, 100, "alice"_n, "alice"_n), + eosio_assert_message_exception, eosio_assert_message_is("tx executed inline by contract must succeed")); + + BOOST_REQUIRE(intx::uint256(vault_balance("alice"_n)) == alice_balance); + BOOST_REQUIRE(intx::uint256(vault_balance(evm_account_name)) == evm_account_balance); + BOOST_REQUIRE(*evm_balance(token_addr) == 0); + + call_notpayable(token_addr, 0, "alice"_n, "alice"_n); + + alice_balance -= gas_fee_notpayable; + evm_account_balance += gas_fee_notpayable; + + BOOST_REQUIRE(intx::uint256(vault_balance("alice"_n)) == alice_balance); + BOOST_REQUIRE(intx::uint256(vault_balance(evm_account_name)) == evm_account_balance); + BOOST_REQUIRE(*evm_balance(token_addr) == 0); + + call_testpay(token_addr, 0, "alice"_n, "alice"_n); + + alice_balance -= gas_fee_testpay; + evm_account_balance += gas_fee_testpay; + + BOOST_REQUIRE(intx::uint256(vault_balance("alice"_n)) == alice_balance); + BOOST_REQUIRE(intx::uint256(vault_balance(evm_account_name)) == evm_account_balance); + BOOST_REQUIRE(*evm_balance(token_addr) == 0); + + call_testpay(token_addr, *((uint128_t*)intx::as_words(50_ether)), "alice"_n, "alice"_n); + + alice_balance -= gas_fee_testpay; + alice_balance -= 50_ether; + evm_account_balance += gas_fee_testpay; + + BOOST_REQUIRE(intx::uint256(vault_balance("alice"_n)) == alice_balance); + BOOST_REQUIRE(intx::uint256(vault_balance(evm_account_name)) == evm_account_balance); + BOOST_REQUIRE(*evm_balance(token_addr) == 50_ether); + + // Advance block so we do not generate same transaction. + produce_block(); + + // No enough gas + BOOST_REQUIRE_EXCEPTION(call_testpay(token_addr, *((uint128_t*)intx::as_words(50_ether)), "alice"_n, "alice"_n), + eosio_assert_message_exception, eosio_assert_message_is("decrementing more than available")); + BOOST_REQUIRE(intx::uint256(vault_balance("alice"_n)) == alice_balance); + BOOST_REQUIRE(intx::uint256(vault_balance(evm_account_name)) == evm_account_balance); + BOOST_REQUIRE(*evm_balance(token_addr) == 50_ether); + + call_testpay(token_addr, *((uint128_t*)intx::as_words(10_ether)), "alice"_n, "alice"_n); + + alice_balance -= gas_fee_testpay; + alice_balance -= 10_ether; + evm_account_balance += gas_fee_testpay; + + BOOST_REQUIRE(intx::uint256(vault_balance("alice"_n)) == alice_balance); + BOOST_REQUIRE(intx::uint256(vault_balance(evm_account_name)) == evm_account_balance); + BOOST_REQUIRE(*evm_balance(token_addr) == 60_ether); } FC_LOG_AND_RETHROW() BOOST_FIXTURE_TEST_CASE(admincall_test_function, call_evm_tester) try { @@ -218,20 +337,19 @@ BOOST_FIXTURE_TEST_CASE(admincall_test_function, call_evm_tester) try { // Transfer small amount to create account transfer_token("alice"_n, evm_account_name, make_asset(100), evm2.address_0x()); - auto evm2_balance = evm_balance(evm2); - BOOST_REQUIRE(!!evm2_balance); - BOOST_REQUIRE(*evm2_balance == 10_finney); + auto tb = evm_balance(evm2); + BOOST_REQUIRE(!!tb); + BOOST_REQUIRE(*tb == 10_finney); // Insufficient funds BOOST_REQUIRE_EXCEPTION( admincall_test(token_addr, 1234, evm2, evm_account_name), eosio_assert_message_exception, eosio_assert_message_is("validate_transaction error: 23 Insufficient funds")); - // Transfer enough funds - transfer_token("alice"_n, evm_account_name, make_asset(10000), evm2.address_0x()); - - BOOST_REQUIRE(evm_balance(evm2) == 1010_finney); - auto total_fund2 = intx::uint256(vault_balance(evm_account_name)) + *evm_balance(evm2); - + // Transfer enough funds, save initial balance + transfer_token("alice"_n, evm_account_name, make_asset(999900), evm2.address_0x()); + auto evm2_balance = 100_ether; + BOOST_REQUIRE(evm_balance(evm2) == evm2_balance); + auto evm_account_balance = intx::uint256(vault_balance(evm_account_name)); BOOST_REQUIRE_EXCEPTION(admincall_test(token_addr, 0, evm2, evm_account_name), eosio_assert_message_exception, eosio_assert_message_is("tx executed inline by contract must succeed")); @@ -242,9 +360,12 @@ BOOST_FIXTURE_TEST_CASE(admincall_test_function, call_evm_tester) try { auto count = get_count(token_addr); BOOST_REQUIRE(count == 1234); - BOOST_REQUIRE(evm_balance(evm2) == 1010_finney - gas_fee); - // Gas go from evm2 to evm vault - BOOST_REQUIRE(total_fund2 == *evm_balance(evm2) + intx::uint256(vault_balance(evm_account_name))); + + evm2_balance -= gas_fee; + evm_account_balance += gas_fee; + + BOOST_REQUIRE(evm_balance(evm2) == evm2_balance); + BOOST_REQUIRE(intx::uint256(vault_balance(evm_account_name)) == evm_account_balance); // Advance block so we do not generate same transaction. produce_block(); @@ -252,14 +373,71 @@ BOOST_FIXTURE_TEST_CASE(admincall_test_function, call_evm_tester) try { admincall_test(token_addr, 4321, evm2, evm_account_name); count = get_count(token_addr); BOOST_REQUIRE(count == 5555); - BOOST_REQUIRE(evm_balance(evm2) == 1010_finney - gas_fee - gas_fee2); - // Gas go from evm2 to evm vault - BOOST_REQUIRE(total_fund2 == *evm_balance(evm2) + intx::uint256(vault_balance(evm_account_name))); + + evm2_balance -= gas_fee2; + evm_account_balance += gas_fee2; + + BOOST_REQUIRE(evm_balance(evm2) == evm2_balance); + BOOST_REQUIRE(intx::uint256(vault_balance(evm_account_name)) == evm_account_balance); // Function being called on behalf of evm address "evm2" auto caller = get_lastcaller(token_addr); BOOST_REQUIRE(caller== evm2.address); + + BOOST_REQUIRE_EXCEPTION(admincall_notpayable(token_addr, 100, evm2, evm_account_name), + eosio_assert_message_exception, eosio_assert_message_is("tx executed inline by contract must succeed")); + + BOOST_REQUIRE(evm_balance(evm2)== evm2_balance); + BOOST_REQUIRE(intx::uint256(vault_balance(evm_account_name)) == evm_account_balance); + BOOST_REQUIRE(*evm_balance(token_addr) == 0); + + admincall_notpayable(token_addr, 0, evm2, evm_account_name); + + evm2_balance -= gas_fee_notpayable; + evm_account_balance += gas_fee_notpayable; + + BOOST_REQUIRE(evm_balance(evm2) == evm2_balance); + BOOST_REQUIRE(intx::uint256(vault_balance(evm_account_name)) == evm_account_balance); + BOOST_REQUIRE(*evm_balance(token_addr) == 0); + + admincall_testpay(token_addr, 0, evm2, evm_account_name); + + evm2_balance -= gas_fee_testpay; + evm_account_balance += gas_fee_testpay; + + BOOST_REQUIRE(evm_balance(evm2) == evm2_balance); + BOOST_REQUIRE(intx::uint256(vault_balance(evm_account_name)) == evm_account_balance); + BOOST_REQUIRE(*evm_balance(token_addr) == 0); + + admincall_testpay(token_addr, *((uint128_t*)intx::as_words(50_ether)), evm2, evm_account_name); + + evm2_balance -= gas_fee_testpay; + evm2_balance -= 50_ether; + evm_account_balance += gas_fee_testpay; + + BOOST_REQUIRE(evm_balance(evm2)== evm2_balance); + BOOST_REQUIRE(intx::uint256(vault_balance(evm_account_name)) == evm_account_balance); + BOOST_REQUIRE(*evm_balance(token_addr) == 50_ether); + + // Advance block so we do not generate same transaction. + produce_block(); + + // No enough gas + BOOST_REQUIRE_EXCEPTION(admincall_testpay(token_addr, *((uint128_t*)intx::as_words(50_ether)), evm2, evm_account_name), + eosio_assert_message_exception, eosio_assert_message_is("validate_transaction error: 23 Insufficient funds")); + BOOST_REQUIRE(evm_balance(evm2) == evm2_balance); + BOOST_REQUIRE(intx::uint256(vault_balance(evm_account_name)) == evm_account_balance); + BOOST_REQUIRE(*evm_balance(token_addr) == 50_ether); + + admincall_testpay(token_addr, *((uint128_t*)intx::as_words(10_ether)), evm2, evm_account_name); + + evm2_balance -= gas_fee_testpay; + evm2_balance -= 10_ether; + evm_account_balance += gas_fee_testpay; + BOOST_REQUIRE(evm_balance(evm2) == evm2_balance); + BOOST_REQUIRE(intx::uint256(vault_balance(evm_account_name)) == evm_account_balance); + BOOST_REQUIRE(*evm_balance(token_addr) == 60_ether); } FC_LOG_AND_RETHROW() From 9759072f6233f67e04474ca469160fbb5b8ad18a Mon Sep 17 00:00:00 2001 From: yarkin Date: Fri, 11 Aug 2023 12:55:55 +0800 Subject: [PATCH 13/14] Change data type of value to const byte& --- include/evm_runtime/evm_contract.hpp | 4 +-- src/actions.cpp | 16 +++++++++--- tests/basic_evm_tester.cpp | 16 +++++++++--- tests/basic_evm_tester.hpp | 4 +-- tests/call_tests.cpp | 38 +++++++++++++++++++++------- 5 files changed, 57 insertions(+), 21 deletions(-) diff --git a/include/evm_runtime/evm_contract.hpp b/include/evm_runtime/evm_contract.hpp index cd737d10..e52669fa 100644 --- a/include/evm_runtime/evm_contract.hpp +++ b/include/evm_runtime/evm_contract.hpp @@ -85,8 +85,8 @@ class [[eosio::contract]] evm_contract : public contract [[eosio::action]] bool gc(uint32_t max); - [[eosio::action]] void call(eosio::name from, const bytes& to, uint128_t value, const bytes& data, uint64_t gas_limit); - [[eosio::action]] void admincall(const bytes& from, const bytes& to, uint128_t value, const bytes& data, uint64_t gas_limit); + [[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); #ifdef WITH_TEST_ACTIONS [[eosio::action]] void testtx(const std::optional& orlptx, const evm_runtime::test::block_info& bi); diff --git a/src/actions.cpp b/src/actions.cpp index 2da82a09..11ee59d6 100644 --- a/src/actions.cpp +++ b/src/actions.cpp @@ -590,16 +590,24 @@ void evm_contract::call_(intx::uint256 s, const bytes& to, intx::uint256 value, pushtx_act.send(get_self(), rlp); } -void evm_contract::call(eosio::name from, const bytes& to, uint128_t value, const bytes& data, uint64_t gas_limit) { +void evm_contract::call(eosio::name from, const bytes& to, const bytes& value, const bytes& data, uint64_t gas_limit) { assert_unfrozen(); require_auth(from); - call_(from.value, to, intx::uint256(intx::uint128(value)), data, gas_limit, get_and_increment_nonce(from)); + // Prepare v + eosio::check(value.size() == sizeof(intx::uint256), "invalid value"); + intx::uint256 v = intx::be::unsafe::load((const uint8_t *)value.data()); + + call_(from.value, to, v, data, gas_limit, get_and_increment_nonce(from)); } -void evm_contract::admincall(const bytes& from, const bytes& to, uint128_t value, const bytes& data, uint64_t gas_limit) { +void evm_contract::admincall(const bytes& from, const bytes& to, const bytes& value, const bytes& data, uint64_t gas_limit) { assert_unfrozen(); require_auth(get_self()); + + // Prepare v + eosio::check(value.size() == sizeof(intx::uint256), "invalid value"); + intx::uint256 v = intx::be::unsafe::load((const uint8_t *)value.data()); // Prepare s eosio::check(from.size() == kAddressLength, err_msg_invalid_addr); @@ -623,7 +631,7 @@ void evm_contract::admincall(const bytes& from, const bytes& to, uint128_t value nonce = account->nonce; } - call_(s, to, intx::uint256(intx::uint128(value)), data, gas_limit, nonce); + call_(s, to, v, data, gas_limit, nonce); } #ifdef WITH_TEST_ACTIONS diff --git a/tests/basic_evm_tester.cpp b/tests/basic_evm_tester.cpp index 649486c3..5f7121ca 100644 --- a/tests/basic_evm_tester.cpp +++ b/tests/basic_evm_tester.cpp @@ -291,7 +291,7 @@ transaction_trace_ptr basic_evm_tester::exec(const exec_input& input, const std: return basic_evm_tester::push_action(evm_account_name, "exec"_n, evm_account_name, bytes{binary_data.begin(), binary_data.end()}); } -void basic_evm_tester::call(name from, const evmc::bytes& to, uint128_t value, evmc::bytes& data, uint64_t gas_limit, name actor) +void basic_evm_tester::call(name from, const evmc::bytes& to, const evmc::bytes& value, evmc::bytes& data, uint64_t gas_limit, name actor) { bytes to_bytes; to_bytes.resize(to.size()); @@ -301,10 +301,14 @@ void basic_evm_tester::call(name from, const evmc::bytes& to, uint128_t value, e data_bytes.resize(data.size()); memcpy(data_bytes.data(), data.data(), data.size()); - push_action(evm_account_name, "call"_n, actor, mvo()("from", from)("to", to_bytes)("value", value)("data", data_bytes)("gas_limit", gas_limit)); + bytes value_bytes; + value_bytes.resize(value.size()); + memcpy(value_bytes.data(), value.data(), value.size()); + + push_action(evm_account_name, "call"_n, actor, mvo()("from", from)("to", to_bytes)("value", value_bytes)("data", data_bytes)("gas_limit", gas_limit)); } -void basic_evm_tester::admincall(const evmc::bytes& from, const evmc::bytes& to, uint128_t value, evmc::bytes& data, uint64_t gas_limit, name actor) +void basic_evm_tester::admincall(const evmc::bytes& from, const evmc::bytes& to, const evmc::bytes& value, evmc::bytes& data, uint64_t gas_limit, name actor) { bytes to_bytes; to_bytes.resize(to.size()); @@ -318,7 +322,11 @@ void basic_evm_tester::admincall(const evmc::bytes& from, const evmc::bytes& to, from_bytes.resize(from.size()); memcpy(from_bytes.data(), from.data(), from.size()); - push_action(evm_account_name, "admincall"_n, actor, mvo()("from", from_bytes)("to", to_bytes)("value", value)("data", data_bytes)("gas_limit", gas_limit)); + bytes value_bytes; + value_bytes.resize(value.size()); + memcpy(value_bytes.data(), value.data(), value.size()); + + 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)); } void basic_evm_tester::pushtx(const silkworm::Transaction& trx, name miner) diff --git a/tests/basic_evm_tester.hpp b/tests/basic_evm_tester.hpp index d2c3fa16..64303da7 100644 --- a/tests/basic_evm_tester.hpp +++ b/tests/basic_evm_tester.hpp @@ -200,8 +200,8 @@ class basic_evm_tester : public testing::validating_tester transaction_trace_ptr exec(const exec_input& input, const std::optional& callback); void pushtx(const silkworm::Transaction& trx, name miner = evm_account_name); - void call(name from, const evmc::bytes& to, uint128_t value, evmc::bytes& data, uint64_t gas_limit, name actor); - void admincall(const evmc::bytes& from, const evmc::bytes& to, uint128_t value, evmc::bytes& data, uint64_t gas_limit, name actor); + 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); evmc::address deploy_contract(evm_eoa& eoa, evmc::bytes bytecode); void addegress(const std::vector& accounts); diff --git a/tests/call_tests.cpp b/tests/call_tests.cpp index 0384f12d..085ea3f2 100644 --- a/tests/call_tests.cpp +++ b/tests/call_tests.cpp @@ -70,7 +70,10 @@ struct call_evm_tester : basic_evm_tester { data += evmc::from_hex("29e99f07").value(); // sha3(test(uint256))[:4] data += evmc::bytes32{amount}; // value - call(eos, to, 0, data, 500000, actor); + evmc::bytes32 v; + intx::be::store(v.bytes, intx::uint256(0)); + + call(eos, to, silkworm::Bytes(v), data, 500000, actor); } void call_testpay(const evmc::address& contract_addr, uint128_t amount, name eos, name actor) { @@ -80,7 +83,10 @@ struct call_evm_tester : basic_evm_tester { silkworm::Bytes data; data += evmc::from_hex("a1a7d817").value(); // sha3(testpay())[:4] - call(eos, to, amount, data, 500000, actor); + evmc::bytes32 v; + intx::be::store(v.bytes, intx::uint256(intx::uint128(amount))); + + call(eos, to, silkworm::Bytes(v), data, 500000, actor); } void call_notpayable(const evmc::address& contract_addr, uint128_t amount, name eos, name actor) { @@ -90,7 +96,10 @@ struct call_evm_tester : basic_evm_tester { silkworm::Bytes data; data += evmc::from_hex("d79e1b6a").value(); // sha3(notpayable())[:4] - call(eos, to, amount, data, 500000, actor); + evmc::bytes32 v; + intx::be::store(v.bytes, intx::uint256(intx::uint128(amount))); + + call(eos, to, silkworm::Bytes(v), data, 500000, actor); } void admincall_testpay(const evmc::address& contract_addr, uint128_t amount, evm_eoa& eoa, name actor) { @@ -101,7 +110,10 @@ struct call_evm_tester : basic_evm_tester { silkworm::Bytes data; data += evmc::from_hex("a1a7d817").value(); // sha3(testpay())[:4] - admincall(from, to, amount, data, 500000, actor); + evmc::bytes32 v; + intx::be::store(v.bytes, intx::uint256(intx::uint128(amount))); + + admincall(from, to, silkworm::Bytes(v), data, 500000, actor); } void admincall_notpayable(const evmc::address& contract_addr, uint128_t amount, evm_eoa& eoa, name actor) { @@ -112,7 +124,10 @@ struct call_evm_tester : basic_evm_tester { silkworm::Bytes data; data += evmc::from_hex("d79e1b6a").value(); // sha3(notpayable())[:4] - admincall(from, to, amount, data, 500000, actor); + evmc::bytes32 v; + intx::be::store(v.bytes, intx::uint256(intx::uint128(amount))); + + admincall(from, to, silkworm::Bytes(v), data, 500000, actor); } void admincall_test(const evmc::address& contract_addr, uint64_t amount, evm_eoa& eoa, name actor) { @@ -122,7 +137,10 @@ struct call_evm_tester : basic_evm_tester { data += evmc::from_hex("29e99f07").value(); // sha3(test(uint256))[:4] data += evmc::bytes32{amount}; // value - admincall(from, to, 0, data, 500000, actor); + evmc::bytes32 v; + intx::be::store(v.bytes, intx::uint256(0)); + + admincall(from, to, silkworm::Bytes(v), data, 500000, actor); } intx::uint256 get_count(const evmc::address& contract_addr, std::optional callback={}, std::optional context={}) { @@ -446,13 +464,15 @@ BOOST_FIXTURE_TEST_CASE(deploy_contract_function, call_evm_tester) try { auto alice_addr = make_reserved_address("alice"_n.to_uint64_t()); open("alice"_n); transfer_token("alice"_n, evm_account_name, make_asset(1000000), "alice"); - + + + evmc::bytes32 v; auto to = evmc::bytes(); auto data = evmc::from_hex(contract_bytecode); - call("alice"_n, to, 0, *data, 1000000, "alice"_n); // nonce 0->1 + call("alice"_n, to, silkworm::Bytes(v), *data, 1000000, "alice"_n); // nonce 0->1 auto addr = silkworm::create_address(alice_addr, 0); @@ -465,7 +485,7 @@ BOOST_FIXTURE_TEST_CASE(deploy_contract_function, call_evm_tester) try { auto from = evmc::bytes{std::begin(alice_addr.bytes), std::end(alice_addr.bytes)}; - admincall(from, to, 0, *data, 1000000, evm_account_name); // nonce 2->3 + admincall(from, to, silkworm::Bytes(v), *data, 1000000, evm_account_name); // nonce 2->3 addr = silkworm::create_address(alice_addr, 2); call_test(addr, 2222, "alice"_n, "alice"_n); // nonce 3->4 From 37b8ee22c8f205cec922ad40d3fb0c15652f0a4a Mon Sep 17 00:00:00 2001 From: yarkin Date: Mon, 14 Aug 2023 23:02:34 +0800 Subject: [PATCH 14/14] Fix an unsafe intx::load. --- src/actions.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/actions.cpp b/src/actions.cpp index 11ee59d6..116bf5c9 100644 --- a/src/actions.cpp +++ b/src/actions.cpp @@ -611,9 +611,9 @@ void evm_contract::admincall(const bytes& from, const bytes& to, const bytes& va // Prepare s eosio::check(from.size() == kAddressLength, err_msg_invalid_addr); - intx::uint256 s = intx::be::unsafe::load((const uint8_t *)from.data()); - // load will put the data in higher bytes, shift them donw. - s >>= 256 - kAddressLength * 8; + uint8_t s_buffer[32] = {}; + memcpy(s_buffer + 32 - kAddressLength, from.data(), kAddressLength); + intx::uint256 s = intx::be::load(s_buffer); // pad with '1's s |= ((~intx::uint256(0)) << (kAddressLength * 8));