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

Yarkin/call action #633

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions contract/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, 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<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 evmc::address& to, intx::uint256 value, 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
92 changes: 77 additions & 15 deletions contract/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,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<intx::uint256>::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<intx::uint256>::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");
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -556,6 +564,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();

const Transaction txn {
.type = Transaction::Type::kLegacy,
.nonce = nonce,
.max_priority_fee_per_gas = current_config.gas_price,
.max_fee_per_gas = current_config.gas_price,
.gas_limit = gas_limit,
.to = to,
.value = value,
.data = Bytes{(const uint8_t*)data.data(), data.size()},
.r = 0u, // r == 0 is pseudo signature that resolves to reserved address range
.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<intx::uint256>((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<bytes>& orlptx, const evm_runtime::test::block_info& bi ) {
assert_unfrozen();
Expand Down
1 change: 1 addition & 0 deletions contract/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,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
28 changes: 28 additions & 0 deletions contract/tests/basic_evm_tester.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions contract/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
Loading
Loading