Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add call actions #634

Merged
merged 15 commits into from
Aug 14, 2023
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 6 additions & 0 deletions include/evm_runtime/evm_contract.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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, 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);
yarkinwho marked this conversation as resolved.
Show resolved Hide resolved

#ifdef WITH_TEST_ACTIONS
[[eosio::action]] void testtx(const std::optional<bytes>& orlptx, const evm_runtime::test::block_info& bi);
[[eosio::action]] void
Expand Down Expand Up @@ -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 bytes& to, intx::uint256 value, const bytes& data, uint64_t gas_limit, uint64_t nonce);

// to allow sending through a Bytes (basic_string<uint8_t>) w/o copying over to a std::vector<char>
void pushtx_bytes(eosio::name miner, const std::basic_string<uint8_t>& rlptx);
using pushtx_action = eosio::action_wrapper<"pushtx"_n, &evm_contract::pushtx_bytes>;
Expand Down
83 changes: 76 additions & 7 deletions src/actions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -219,13 +220,19 @@ Receipt evm_contract::execute_tx( eosio::name miner, Block& block, Transaction&
inevm.emplace(get_self(), get_self().value);
};

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");

if(from_self) {
check(is_reserved_address(*tx.from), "actions from self without a reserved from address are unexpected");
// 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)) {
arhag marked this conversation as resolved.
Show resolved Hide resolved
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;
Expand All @@ -241,10 +248,10 @@ Receipt evm_contract::execute_tx( eosio::name miner, Block& block, Transaction&
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");

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");
}

Expand All @@ -269,7 +276,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, "tx executed inline by contract must succeed");

if(!ep.state().reserved_objects().empty()) {
bool non_open_account_sent = false;
Expand Down Expand Up @@ -452,7 +459,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;
Expand Down Expand Up @@ -555,6 +562,68 @@ bool evm_contract::gc(uint32_t max) {
return state.gc(max);
}

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;
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}});
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) {
arhag marked this conversation as resolved.
Show resolved Hide resolved
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, const bytes& data, uint64_t gas_limit) {
arhag marked this conversation as resolved.
Show resolved Hide resolved
assert_unfrozen();
require_auth(get_self());

// Prepare s
eosio::check(from.size() == kAddressLength, err_msg_invalid_addr);
intx::uint256 s = intx::be::unsafe::load<intx::uint256>((const uint8_t *)from.data());
arhag marked this conversation as resolved.
Show resolved Hide resolved
// 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 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
[[eosio::action]] void evm_contract::testtx( const std::optional<bytes>& orlptx, const evm_runtime::test::block_info& bi ) {
assert_unfrozen();
Expand Down
1 change: 1 addition & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
30 changes: 30 additions & 0 deletions tests/basic_evm_tester.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,36 @@ 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(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));
}

void basic_evm_tester::pushtx(const silkworm::Transaction& trx, name miner)
{
silkworm::Bytes rlp;
Expand Down
2 changes: 2 additions & 0 deletions tests/basic_evm_tester.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,8 @@ class basic_evm_tester : public testing::validating_tester

transaction_trace_ptr exec(const exec_input& input, const std::optional<exec_callback>& 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<name>& accounts);
Expand Down