Skip to content

Commit

Permalink
Merge pull request #643 from eosnetworkfoundation/elmato/bridge-basic…
Browse files Browse the repository at this point in the history
…-message-emission-fix

Bridge basic message emission (2)
  • Loading branch information
arhag committed Aug 22, 2023
2 parents 509c3ee + cbff79d commit c460b54
Show file tree
Hide file tree
Showing 21 changed files with 799 additions and 22 deletions.
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) {
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) {
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

0 comments on commit c460b54

Please sign in to comment.