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

Initial implementation of call in test host #9

Merged
merged 2 commits into from
Apr 19, 2024
Merged
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
1 change: 1 addition & 0 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@
clang_17
ethash
deps.intx
deps.evmc
crypto3
blueprint
];
Expand Down
6 changes: 3 additions & 3 deletions lib/assigner/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ cmake_policy(SET CMP0063 NEW)

option(BUILD_ASSIGNER_TESTS "Build unit tests" FALSE)

add_library(${PROJECT_NAME} INTERFACE)
add_library(${PROJECT_NAME} STATIC src/vm_host.cpp)

target_include_directories(${PROJECT_NAME} INTERFACE
target_include_directories(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/include
${include_dir}/evmone
${CMAKE_CURRENT_SOURCE_DIR}/../evmone
Expand All @@ -16,7 +16,7 @@ target_include_directories(${PROJECT_NAME} INTERFACE

# TODO: link to blueprint and crypto3 here after fixing this in crypto3:
# https://github.com/NilFoundation/crypto3/issues/175
target_link_libraries(${PROJECT_NAME} INTERFACE
target_link_libraries(${PROJECT_NAME} PUBLIC
evmone)

install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/nil/blueprint/
Expand Down
4 changes: 2 additions & 2 deletions lib/assigner/include/nil/blueprint/assigner.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ namespace nil {
handler_ptr = std::make_shared<handler<BlueprintFieldType>>(desc, assignments);
}

void evaluate(evmc_vm* c_vm, const evmc_host_interface* host, evmc_host_context* ctx,
evmc::Result evaluate(evmc_vm* c_vm, const evmc_host_interface* host, evmc_host_context* ctx,
evmc_revision rev, const evmc_message* msg, const uint8_t* code, size_t code_size) {

auto vm = static_cast<evmone::VM*>(c_vm);
Expand All @@ -40,7 +40,7 @@ namespace nil {
state->set_handler(handler_ptr);

auto res = execute(*vm, msg->gas, *state, code_analysis);
// TODO check res
return evmc::Result{res};
}

private:
Expand Down
60 changes: 36 additions & 24 deletions lib/assigner/test/test_host.h → lib/assigner/include/vm_host.h
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
#ifndef EVM1_ASSIGNER_LIB_ASSIGNER_TEST_TEST_HOST_H_
#define EVM1_ASSIGNER_LIB_ASSIGNER_TEST_TEST_HOST_H_
#ifndef EVM_ASSIGNER_LIB_ASSIGNER_INCLUDE_VM_HOST_H_
#define EVM_ASSIGNER_LIB_ASSIGNER_INCLUDE_VM_HOST_H_

// Based on example host

#include <evmc/evmc.hpp>
#include <nil/blueprint/assigner.hpp>

#include <algorithm>
#include <map>
Expand Down Expand Up @@ -36,16 +37,23 @@ using accounts = std::map<evmc::address, account>;

} // namespace evmc

class ExampleHost : public evmc::Host
using BlueprintFieldType = typename nil::crypto3::algebra::curves::pallas::base_field_type;
using AssignerType = nil::blueprint::assigner<BlueprintFieldType>;

class VMHost : public evmc::Host
{
evmc::accounts accounts;
evmc_tx_context tx_context{};
AssignerType *assigner;

public:
ExampleHost() = default;
explicit ExampleHost(evmc_tx_context& _tx_context) noexcept : tx_context{_tx_context} {}
ExampleHost(evmc_tx_context& _tx_context, evmc::accounts& _accounts) noexcept
: accounts{_accounts}, tx_context{_tx_context}
VMHost() = default;
explicit VMHost(evmc_tx_context& _tx_context, AssignerType* _assigner) noexcept
: tx_context{_tx_context}, assigner(_assigner)
{}

VMHost(evmc_tx_context& _tx_context, AssignerType* _assigner, evmc::accounts& _accounts) noexcept
: accounts{_accounts}, tx_context{_tx_context}, assigner(_assigner)
{}

bool account_exists(const evmc::address& addr) const noexcept final
Expand Down Expand Up @@ -131,7 +139,19 @@ class ExampleHost : public evmc::Host

evmc::Result call(const evmc_message& msg) noexcept final
{
return evmc::Result{EVMC_REVERT, msg.gas, 0, msg.input_data, msg.input_size};
switch (msg.kind)
{
case EVMC_CALL:
case EVMC_CALLCODE:
case EVMC_DELEGATECALL:
return handle_call(msg);
case EVMC_CREATE:
case EVMC_CREATE2:
return handle_create(msg);
default:
// Unexpected opcode
return evmc::Result{EVMC_INTERNAL_ERROR};
}
}

evmc_tx_context get_tx_context() const noexcept final { return tx_context; }
Expand Down Expand Up @@ -192,26 +212,18 @@ class ExampleHost : public evmc::Host
{
accounts[addr].transient_storage[key] = value;
}

private:
evmc::Result handle_call(const evmc_message& msg);
evmc::Result handle_create(const evmc_message& msg);
evmc::address calculate_address(const evmc_message& msg);
};


extern "C" {

const evmc_host_interface* example_host_get_interface()
{
return &evmc::Host::get_interface();
}

evmc_host_context* example_host_create_context(evmc_tx_context tx_context)
{
auto host = new ExampleHost{tx_context};
return host->to_context();
}

void example_host_destroy_context(evmc_host_context* context)
{
delete evmc::Host::from_context<ExampleHost>(context);
}
evmc_host_context* vm_host_create_context(evmc_tx_context tx_context, AssignerType *assigner);
void vm_host_destroy_context(evmc_host_context* context);
}

#endif // EVM1_ASSIGNER_LIB_ASSIGNER_TEST_TEST_HOST_H_
#endif // EVM_ASSIGNER_LIB_ASSIGNER_INCLUDE_VM_HOST_H_
93 changes: 93 additions & 0 deletions lib/assigner/src/vm_host.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
#include "vm_host.h"

#include <intx/intx.hpp>
#include <ethash/keccak.hpp>

extern "C" {

evmc_host_context* vm_host_create_context(evmc_tx_context tx_context, AssignerType *assigner)
{
auto host = new VMHost{tx_context, assigner};
return host->to_context();
}

void vm_host_destroy_context(evmc_host_context* context)
{
delete evmc::Host::from_context<VMHost>(context);
}
}

evmc::Result VMHost::handle_call(const evmc_message& msg)
{
evmc_vm * vm = evmc_create_evmone();
auto account_iter = accounts.find(msg.code_address);
auto &sender_acc = accounts[msg.sender];
if (account_iter == accounts.end())
{
// Create account
accounts[msg.code_address] = {};
}
auto& acc = account_iter->second;
if (msg.kind == EVMC_CALL) {
auto value_to_transfer = intx::be::load<intx::uint256>(msg.value);
auto balance = intx::be::load<intx::uint256>(sender_acc.balance);
// Balance was already checked in evmone, so simply adjust it
sender_acc.balance = intx::be::store<evmc::uint256be>(balance - value_to_transfer);
acc.balance = intx::be::store<evmc::uint256be>(
value_to_transfer + intx::be::load<intx::uint256>(acc.balance));
}
if (acc.code.empty())
{
return evmc::Result{EVMC_SUCCESS, msg.gas, 0, msg.input_data, msg.input_size};
}
// TODO: handle precompiled contracts
auto res = assigner->evaluate(vm, &get_interface(), to_context(),
EVMC_LATEST_STABLE_REVISION, &msg, acc.code.data(), acc.code.size());
return res;
}

evmc::Result VMHost::handle_create(const evmc_message& msg)
{
evmc::address new_contract_address = calculate_address(msg);
if (accounts.find(new_contract_address) != accounts.end())
{
// Address collision
return evmc::Result{EVMC_FAILURE};
}
accounts[new_contract_address] = {};
if (msg.input_size == 0)
{
return evmc::Result{EVMC_SUCCESS, msg.gas, 0, new_contract_address};
}
evmc::VM vm{evmc_create_evmone()};
evmc_message init_msg(msg);
init_msg.kind = EVMC_CALL;
init_msg.recipient = new_contract_address;
init_msg.sender = msg.sender;
init_msg.input_size = 0;
auto res = assigner->evaluate(vm.get_raw_pointer(), &get_interface(), to_context(),
EVMC_LATEST_STABLE_REVISION, &init_msg, msg.input_data, msg.input_size);

if (res.status_code == EVMC_SUCCESS)
{
accounts[new_contract_address].code =
std::vector<uint8_t>(res.output_data, res.output_data + res.output_size);
}
res.create_address = new_contract_address;
return res;
}

evmc::address VMHost::calculate_address(const evmc_message& msg)
{
// TODO: Implement for CREATE opcode, for now the result is only correct for CREATE2
// CREATE requires rlp encoding
auto seed = intx::be::load<intx::uint256>(msg.create2_salt);
auto hash = intx::be::load<intx::uint256>(ethash::keccak256(msg.input_data, msg.input_size));
auto sender = intx::be::load<intx::uint256>(msg.sender);
auto sum = 0xff + seed + hash + sender;
auto rehash = ethash::keccak256(reinterpret_cast<const uint8_t *>(&sum), sizeof(sum));
// Result address is the last 20 bytes of the hash
evmc::address res;
std::memcpy(res.bytes, rehash.bytes + 12, 20);
return res;
}
3 changes: 2 additions & 1 deletion lib/assigner/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ include(GoogleTest)

find_package(GTest CONFIG REQUIRED)

add_executable(assigner_tests assigner_test.cpp)
add_executable(assigner_tests
assigner_test.cpp)

target_link_libraries(
assigner_tests
Expand Down
Loading
Loading