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

Bridge basic message emission #630

Closed
wants to merge 31 commits into from
Closed
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
c092f8a
Add bridgereg/bridgeunreg actions
elmato Jul 24, 2023
91c58a5
Handle 'emit' EVM call messages
elmato Jul 24, 2023
bd531a3
Add tests for 'emit' message functionality
elmato Jul 24, 2023
2850d30
Add more tests for the 'emit' message functionality
elmato Jul 24, 2023
1e300f2
Add call actions. Pending submodule silkworm updates.
yarkinwho Jul 27, 2023
02f90c7
Use EVM message filter functionality to process bridge messages
elmato Jul 27, 2023
6e8750e
Allow call to create contracts.
yarkinwho Jul 28, 2023
a6bd606
Change error message when inline pushtx actions fails and add tests for
yarkinwho Aug 4, 2023
48513d5
update submodule silkworm
yarkinwho Aug 4, 2023
867fa2c
Add missing const.
yarkinwho Aug 7, 2023
7ae7a54
Optimize build artifact size by removing object files.
oschwaldp-oci Aug 8, 2023
87dff73
Optimize tests build artifact size by removing object files.
oschwaldp-oci Aug 8, 2023
a7ed7d9
Merge pull request #639 from eosnetworkfoundation/gh-632-reduce-artif…
oschwaldp-oci Aug 8, 2023
bc173c3
Optimze logic when processing tx with special signature.
yarkinwho Aug 9, 2023
4a22a22
Fix test styles.
yarkinwho Aug 9, 2023
8156407
More detailed tests for gas fee.
yarkinwho Aug 9, 2023
3ec3131
Merge branch 'main' into yarkin/call_action_new
yarkinwho Aug 9, 2023
4b35e5e
Update submodule
yarkinwho Aug 9, 2023
33c066b
improve bridge tx handling logic.
yarkinwho Aug 10, 2023
5d9afcd
Only intx::uint128's ctor can work properly with uint128_t. We may
yarkinwho Aug 10, 2023
8101a51
Add tests for calls with payments.
yarkinwho Aug 10, 2023
9759072
Change data type of value to const byte&
yarkinwho Aug 11, 2023
37b8ee2
Fix an unsafe intx::load.
yarkinwho Aug 14, 2023
f1d9f9f
Merge pull request #634 from eosnetworkfoundation/yarkin/call_action_new
arhag Aug 14, 2023
31d7024
Update silkworm submodule
arhag Aug 14, 2023
509c3ee
Merge pull request #641 from eosnetworkfoundation/arhag/update-silkworm
arhag Aug 14, 2023
f506abe
tests: move inevm function to base tester class
elmato Aug 16, 2023
76319e7
Remove balance modification for the EOS EVM contract account
elmato Aug 16, 2023
656706e
Add versioning to the bridge message
elmato Aug 16, 2023
cecbafe
Merge branch 'elmato/bridge-basic-message-emission' into merge-bridge
elmato Aug 17, 2023
ccaa5d7
Add force_atomic parameter to method bridgeMsgV0
elmato Aug 21, 2023
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
73 changes: 73 additions & 0 deletions include/evm_runtime/bridge.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
#pragma once

#include <evm_runtime/types.hpp>

namespace evm_runtime { namespace bridge {

struct emit {
static constexpr uint32_t id = 0x44282a35; //sha3('emit_(string,bytes)')[:4]

string account;
bytes data;

name get_account_as_name() const {
if(!account_.has_value()) {
account_ = name{account};
eosio::check(account_.value().to_string() == account, "invalid account name");
}
return *account_;
}

mutable std::optional<name> account_;
};

using call = std::variant<emit>;

std::optional<call> decode_emit_message(eosio::datastream<const uint8_t*>& ds) {
// offset_p1 (32) + offset_p2 (32)
// p1_len (32) + p1_data ((p1_len+31)/32*32)
// p2_len (32) + p1_data ((p2_len+31)/32*32)
uint256 offset_p1, offset_p2;
uint32_t p1_len, p2_len;

ds >> offset_p1;
eosio::check(offset_p1 == 0x40, "invalid p1 offset");
ds >> offset_p2;
eosio::check(offset_p2 == 0x80, "invalid p2 offset");

emit res;

auto get_length=[&]() -> uint32_t {
uint256 len;
ds >> len;
eosio::check(len < std::numeric_limits<uint32_t>::max(), "invalid length");
return static_cast<uint32_t>(len);
};

p1_len = get_length();
auto p1_len_32 = (p1_len+31)/32*32;
res.account.resize(p1_len_32);
ds.read(res.account.data(), p1_len_32);
res.account.resize(p1_len);

p2_len = get_length();
auto p2_len_32 = (p2_len+31)/32*32;
res.data.resize(p2_len_32);
ds.read(res.data.data(), p2_len_32);
res.data.resize(p2_len);

return res;
}

std::optional<call> decode_call_message(ByteView bv) {
// method_id (4)
eosio::datastream<const uint8_t*> ds(bv.data(), bv.size());
uint32_t method_id;
ds >> method_id;

if(method_id == __builtin_bswap32(emit::id)) return decode_emit_message(ds);
return {};
}

} //namespace bridge
} //namespace evm_runtime
3 changes: 3 additions & 0 deletions include/evm_runtime/evm_contract.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,9 @@ 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 bridgereg(eosio::name receiver, const eosio::asset& min_fee);
[[eosio::action]] void bridgeunreg(eosio::name receiver);

#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
11 changes: 11 additions & 0 deletions include/evm_runtime/tables.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -182,4 +182,15 @@ struct [[eosio::table]] [[eosio::contract("evm_contract")]] allowed_egress_accou

typedef eosio::multi_index<"egresslist"_n, allowed_egress_account> egresslist;

struct [[eosio::table]] [[eosio::contract("evm_contract")]] message_receiver {
name account;
asset min_fee;

uint64_t primary_key() const { return account.value; }

EOSLIB_SERIALIZE(message_receiver, (account)(min_fee));
};

typedef eosio::multi_index<"msgreceiver"_n, message_receiver> message_receiver_table;

} //namespace evm_runtime
12 changes: 11 additions & 1 deletion include/evm_runtime/types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ namespace evm_runtime {
eosio::checksum256 make_key(bytes data);
eosio::checksum256 make_key(const evmc::address& addr);
eosio::checksum256 make_key(const evmc::bytes32& data);

bytes to_bytes(const uint256& val);
bytes to_bytes(const evmc::bytes32& val);
bytes to_bytes(const evmc::address& addr);
Expand Down Expand Up @@ -63,6 +63,16 @@ namespace evm_runtime {
EOSLIB_SERIALIZE(exec_output, (status)(data)(context));
};

struct bridge_emit_message {
eosio::name receiver;
bytes sender;
eosio::time_point timestamp;
bytes value;
bytes data;

EOSLIB_SERIALIZE(bridge_emit_message, (receiver)(sender)(timestamp)(value)(data));
};

} //namespace evm_runtime

namespace eosio {
Expand Down
2 changes: 1 addition & 1 deletion src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -85,5 +85,5 @@ target_compile_options(evm_runtime PUBLIC --no-missing-ricardian-clause)
if (WITH_LARGE_STACK)
target_link_options(evm_runtime PUBLIC --stack-size=50000000)
else()
target_link_options(evm_runtime PUBLIC --stack-size=38560)
target_link_options(evm_runtime PUBLIC --stack-size=32768)
endif()
87 changes: 87 additions & 0 deletions src/actions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <evm_runtime/state.hpp>
#include <evm_runtime/intrinsics.hpp>
#include <evm_runtime/eosio.token.hpp>
#include <evm_runtime/bridge.hpp>

#include <eosevm/block_mapping.hpp>

Expand Down Expand Up @@ -406,8 +407,58 @@ void evm_contract::pushtx( eosio::name miner, const bytes& rlptx ) {
check(tx.max_priority_fee_per_gas == tx.max_fee_per_gas, "max_priority_fee_per_gas must be equal to max_fee_per_gas");
check(tx.max_fee_per_gas >= current_config.gas_price, "gas price is too low");

// Filter EVM messages (with data) that are sent to the reserved address
// corresponding to the EOS account holding the contract (self)
ep.set_evm_message_filter([&](const evmc_message& message) -> bool {
auto me = make_reserved_address(get_self().value);
elmato marked this conversation as resolved.
Show resolved Hide resolved
return message.recipient == me && message.input_size > 0;
});

auto receipt = execute_tx(miner, block, tx, ep, true);

for(const auto& msg : ep.state().filtered_messages()) {

auto call = bridge::decode_call_message(ByteView{msg.data});
eosio::check(call.has_value(), "unable to decode call message");

auto& emit = std::get<bridge::emit>(call.value());

const auto& receiver = emit.get_account_as_name();
eosio::check(eosio::is_account(receiver), "receiver is not account");

message_receiver_table message_receivers(get_self(), get_self().value);
auto it = message_receivers.find(receiver.value);
eosio::check(it != message_receivers.end(), "receiver not registered");

intx::uint256 min_fee((uint64_t)it->min_fee.amount);
min_fee *= minimum_natively_representable;

auto value = intx::be::unsafe::load<uint256>(msg.value.bytes);
eosio::check(value >= min_fee, "min_fee not covered");

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

action(std::vector<permission_level>{}, emit.get_account_as_name(), "onbridgemsg"_n,
bridge_emit_message{
.receiver = emit.get_account_as_name(),
.sender = to_bytes(msg.sender),
.timestamp = eosio::current_time_point(),
.value = to_bytes(msg.value),
.data = emit.data
}
).send();

balance_table.modify(receiver_account, eosio::same_payer, [&](balance& row) {
row.balance += value;
});

const balance& self_account = balance_table.get(get_self().value, "self account is not open");
balance_table.modify(self_account, eosio::same_payer, [&](balance& row) {
row.balance -= value;
});
}

engine.finalize(ep.state(), ep.evm().block());
ep.state().write_to_db(ep.evm().block().header.number);
LOGTIME("EVM END");
Expand Down Expand Up @@ -555,6 +606,42 @@ bool evm_contract::gc(uint32_t max) {
return state.gc(max);
}

void evm_contract::bridgereg(eosio::name receiver, const eosio::asset& min_fee) {
assert_unfrozen();
require_auth(receiver);
require_auth(get_self()); // to temporarily prevent registration of unauthorized accounts

eosio::check(min_fee.symbol == token_symbol, "unexpected symbol");
eosio::check(min_fee.amount >= 0, "min_fee cannot be negative");

auto update_row = [&](auto& row) {
row.account = receiver;
row.min_fee = min_fee;
};

message_receiver_table message_receivers(get_self(), get_self().value);
auto it = message_receivers.find(receiver.value);

if(it == message_receivers.end()) {
message_receivers.emplace(receiver, update_row);
} else {
message_receivers.modify(*it, eosio::same_payer, update_row);
}

open(receiver);
}

void evm_contract::bridgeunreg(eosio::name receiver) {
assert_unfrozen();
require_auth(receiver);

message_receiver_table message_receivers(get_self(), get_self().value);
auto it = message_receivers.find(receiver.value);
eosio::check(it != message_receivers.end(), "receiver not found");
message_receivers.erase(*it);
}


#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 @@ -31,6 +31,7 @@ add_eosio_test_executable( unit_test
${CMAKE_SOURCE_DIR}/blockhash_tests.cpp
${CMAKE_SOURCE_DIR}/exec_tests.cpp
${CMAKE_SOURCE_DIR}/chainid_tests.cpp
${CMAKE_SOURCE_DIR}/bridge_emit_tests.cpp
${CMAKE_SOURCE_DIR}/main.cpp
${CMAKE_SOURCE_DIR}/silkworm/core/silkworm/rlp/encode.cpp
${CMAKE_SOURCE_DIR}/silkworm/core/silkworm/rlp/decode.cpp
Expand Down
20 changes: 18 additions & 2 deletions tests/basic_evm_tester.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,10 @@ evmc::address basic_evm_tester::make_reserved_address(uint64_t account)
// clang-format on
}

evmc::address basic_evm_tester::make_reserved_address(name account) {
return make_reserved_address(account.to_uint64_t());
}

basic_evm_tester::basic_evm_tester(std::string native_symbol_str) :
native_symbol(symbol::from_string(native_symbol_str))
{
Expand Down Expand Up @@ -286,12 +290,24 @@ basic_evm_tester::generate_tx(const evmc::address& to, const intx::uint256& valu
.value = value,
};
}

transaction_trace_ptr basic_evm_tester::bridgereg(name receiver, asset min_fee, vector<account_name> extra_signers) {
extra_signers.push_back(receiver);
return basic_evm_tester::push_action(evm_account_name, "bridgereg"_n, extra_signers,
mvo()("receiver", receiver)("min_fee", min_fee));
}

transaction_trace_ptr basic_evm_tester::bridgeunreg(name receiver) {
return basic_evm_tester::push_action(evm_account_name, "bridgeunreg"_n, receiver,
mvo()("receiver", receiver));
}

transaction_trace_ptr basic_evm_tester::exec(const exec_input& input, const std::optional<exec_callback>& callback) {
auto binary_data = fc::raw::pack<exec_input, std::optional<exec_callback>>(input, callback);
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::pushtx(const silkworm::Transaction& trx, name miner)
transaction_trace_ptr basic_evm_tester::pushtx(const silkworm::Transaction& trx, name miner)
{
silkworm::Bytes rlp;
silkworm::rlp::encode(rlp, trx);
Expand All @@ -300,7 +316,7 @@ void basic_evm_tester::pushtx(const silkworm::Transaction& trx, name miner)
rlp_bytes.resize(rlp.size());
memcpy(rlp_bytes.data(), rlp.data(), rlp.size());

push_action(evm_account_name, "pushtx"_n, miner, mvo()("miner", miner)("rlptx", rlp_bytes));
return push_action(evm_account_name, "pushtx"_n, miner, mvo()("miner", miner)("rlptx", rlp_bytes));
}

evmc::address basic_evm_tester::deploy_contract(evm_eoa& eoa, evmc::bytes bytecode)
Expand Down
23 changes: 21 additions & 2 deletions tests/basic_evm_tester.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,19 @@ struct exec_output {
std::optional<bytes> context;
};

struct message_receiver {
name account;
asset min_fee;
};

struct bridge_emit_message {
name receiver;
bytes sender;
time_point timestamp;
bytes value;
bytes data;
};

} // namespace evm_test


Expand All @@ -123,6 +136,9 @@ FC_REFLECT(evm_test::exec_input, (context)(from)(to)(data)(value))
FC_REFLECT(evm_test::exec_callback, (contract)(action))
FC_REFLECT(evm_test::exec_output, (status)(data)(context))

FC_REFLECT(evm_test::message_receiver, (account)(min_fee));
FC_REFLECT(evm_test::bridge_emit_message, (receiver)(sender)(timestamp)(value)(data));

namespace evm_test {
class evm_eoa
{
Expand Down Expand Up @@ -166,6 +182,7 @@ class basic_evm_tester : public testing::validating_tester
const symbol native_symbol;

static evmc::address make_reserved_address(uint64_t account);
static evmc::address make_reserved_address(name account);

explicit basic_evm_tester(std::string native_symbol_str = "4,EOS");

Expand Down Expand Up @@ -198,8 +215,10 @@ class basic_evm_tester : public testing::validating_tester
silkworm::Transaction
generate_tx(const evmc::address& to, const intx::uint256& value, uint64_t gas_limit = 21000) const;

transaction_trace_ptr bridgereg(name receiver, asset min_fee, vector<account_name> extra_signers={evm_account_name});
transaction_trace_ptr bridgeunreg(name receiver);
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);
transaction_trace_ptr pushtx(const silkworm::Transaction& trx, name miner = evm_account_name);
evmc::address deploy_contract(evm_eoa& eoa, evmc::bytes bytecode);

void addegress(const std::vector<name>& accounts);
Expand Down Expand Up @@ -308,4 +327,4 @@ class speculative_block_starter
bool canceled = false;
};

} // namespace evm_test
} // namespace evm_test
Loading