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 (2) #643

Merged
merged 11 commits into from
Aug 22, 2023
76 changes: 76 additions & 0 deletions include/evm_runtime/bridge.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#pragma once

#include <evm_runtime/types.hpp>

namespace evm_runtime { namespace bridge {

struct message_v0 {
static constexpr uint32_t id = 0xf781185b; //sha3('bridgeMsgV0(string,bool,bytes)')[:4]

string account;
bool force_atomic; //currently only atomic is supported
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 message = std::variant<message_v0>;

message_v0 decode_message_v0(eosio::datastream<const uint8_t*>& ds) {
// offset_p1 (32) + p2_value (32) + offset_p3 (32)
// p1_len (32) + p1_data ((p1_len+31)/32*32)
// p3_len (32) + p3_data ((p2_len+31)/32*32)
uint256 offset_p1, value_p2, offset_p3;

ds >> offset_p1;
eosio::check(offset_p1 == 0x60, "invalid p1 offset");
ds >> value_p2;
eosio::check(value_p2 <= 1, "invalid p2 value");
ds >> offset_p3;
eosio::check(offset_p3 == 0xA0, "invalid p3 offset");

message_v0 res;
res.force_atomic = value_p2 ? true : false;

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

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

uint32_t p3_len = get_length();
auto p3_len_32 = (p3_len+31)/32*32;
res.data.resize(p3_len_32);
ds.read(res.data.data(), p3_len_32);
res.data.resize(p3_len);

return res;
}

std::optional<message> decode_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(message_v0::id)) return decode_message_v0(ds);
return {};
}

} //namespace bridge
} //namespace evm_runtime
4 changes: 4 additions & 0 deletions include/evm_runtime/evm_contract.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,9 @@ class [[eosio::contract]] evm_contract : public contract
[[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);

[[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 Expand Up @@ -136,6 +139,7 @@ class [[eosio::contract]] evm_contract : public contract
}

silkworm::Receipt execute_tx(eosio::name miner, silkworm::Block& block, silkworm::Transaction& tx, silkworm::ExecutionProcessor& ep, bool enforce_chain_id);
void process_filtered_messages(const std::vector<silkworm::FilteredMessage>& filtered_messages);

uint64_t get_and_increment_nonce(const name owner);

Expand Down
18 changes: 10 additions & 8 deletions include/evm_runtime/print_tracer.hpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
#pragma once
#include <evmc/instructions.h>
#include <eosio/eosio.hpp>
namespace evm_runtime {

struct print_tracer : EvmTracer {
struct print_tracer : silkworm::EvmTracer {

const char* const* opcode_names_ = nullptr;

Expand All @@ -19,27 +20,28 @@ struct print_tracer : EvmTracer {
}

void on_instruction_start(uint32_t pc, const intx::uint256* stack_top, int stack_height,
const evmone::ExecutionState& state,
const IntraBlockState& intra_block_state) noexcept override {
const auto opcode = state.code[pc];
int64_t gas, const evmone::ExecutionState& state,
const silkworm::IntraBlockState& intra_block_state) override {

const auto opcode = state.original_code[pc];
auto opcode_name = get_opcode_name(opcode_names_, opcode);
eosio::print(opcode_name.c_str(), "\n");
}

void on_execution_end(const evmc_result& result, const IntraBlockState& intra_block_state) noexcept override {
void on_execution_end(const evmc_result& result, const silkworm::IntraBlockState& intra_block_state) noexcept override {
eosio::print("TRACE: end\n");
}

void on_creation_completed(const evmc_result& result, const IntraBlockState& intra_block_state) noexcept override {
void on_creation_completed(const evmc_result& result, const silkworm::IntraBlockState& intra_block_state) noexcept override {

}

void on_precompiled_run(const evmc_result& result, int64_t gas,
const IntraBlockState& intra_block_state) noexcept override {
const silkworm::IntraBlockState& intra_block_state) noexcept override {

}

void on_reward_granted(const CallResult& result, const IntraBlockState& intra_block_state) noexcept override {
void on_reward_granted(const silkworm::CallResult& result, const silkworm::IntraBlockState& intra_block_state) noexcept override {

}

Expand Down
22 changes: 21 additions & 1 deletion include/evm_runtime/tables.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -182,4 +182,24 @@ struct [[eosio::table]] [[eosio::contract("evm_contract")]] allowed_egress_accou

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

} //namespace evm_runtime
struct [[eosio::table]] [[eosio::contract("evm_contract")]] message_receiver {

enum flag : uint32_t {
FORCE_ATOMIC = 0x1
};

name account;
asset min_fee;
uint32_t flags;

uint64_t primary_key() const { return account.value; }
bool has_flag(flag f) const {
return (flags & f) != 0;
}

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

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

} //namespace evm_runtime
14 changes: 13 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,18 @@ namespace evm_runtime {
EOSLIB_SERIALIZE(exec_output, (status)(data)(context));
};

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

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

using bridge_message = std::variant<bridge_message_v0>;

} //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=38432)
endif()
101 changes: 101 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 @@ -384,6 +385,61 @@ void evm_contract::exec(const exec_input& input, const std::optional<exec_callba
}
}

void evm_contract::process_filtered_messages(const std::vector<silkworm::FilteredMessage>& filtered_messages ) {

intx::uint256 accumulated_value;
for(const auto& rawmsg : filtered_messages) {
auto msg = bridge::decode_message(ByteView{rawmsg.data});
eosio::check(msg.has_value(), "unable to decode bridge message");

auto& msg_v0 = std::get<bridge::message_v0>(msg.value());
eosio::print("FIL MESSAGE: ", uint64_t(msg_v0.force_atomic), "\n");

const auto& receiver = msg_v0.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");

eosio::check(msg_v0.force_atomic == false || it->has_flag(message_receiver::FORCE_ATOMIC), "unable to process message");

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

auto value = intx::be::unsafe::load<uint256>(rawmsg.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(msg_v0.get_account_as_name().value, "receiver account is not open");

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

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

accumulated_value += value;
}

if(accumulated_value > 0) {
balances balance_table(get_self(), get_self().value);
const balance& self_balance = balance_table.get(get_self().value);
balance_table.modify(self_balance, eosio::same_payer, [&](balance& row) {
row.balance -= accumulated_value;
});
}

}

void evm_contract::pushtx( eosio::name miner, const bytes& rlptx ) {
LOGTIME("EVM START");

Expand Down Expand Up @@ -415,8 +471,17 @@ 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 {
static auto me = make_reserved_address(get_self().value);
return message.recipient == me && message.input_size > 0;
});

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

process_filtered_messages(ep.state().filtered_messages());

engine.finalize(ep.state(), ep.evm().block());
ep.state().write_to_db(ep.evm().block().header.number);
LOGTIME("EVM END");
Expand Down Expand Up @@ -634,6 +699,42 @@ void evm_contract::admincall(const bytes& from, const bytes& to, const bytes& va
call_(s, to, v, data, gas_limit, nonce);
}

void evm_contract::bridgereg(eosio::name receiver, const eosio::asset& min_fee) {
arhag marked this conversation as resolved.
Show resolved Hide resolved
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;
row.flags = message_receiver::FORCE_ATOMIC;
};

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) {
arhag marked this conversation as resolved.
Show resolved Hide resolved
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 @@ -32,6 +32,7 @@ add_eosio_test_executable( unit_test
${CMAKE_SOURCE_DIR}/exec_tests.cpp
${CMAKE_SOURCE_DIR}/call_tests.cpp
${CMAKE_SOURCE_DIR}/chainid_tests.cpp
${CMAKE_SOURCE_DIR}/bridge_message_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
Loading