2018-11-26 15:52:36 +00:00
|
|
|
// EVMC: Ethereum Client-VM Connector API.
|
2019-04-24 14:12:13 +00:00
|
|
|
// Copyright 2018-2019 The EVMC Authors.
|
2018-11-26 15:52:36 +00:00
|
|
|
// Licensed under the Apache License, Version 2.0.
|
|
|
|
|
2019-05-14 08:30:31 +00:00
|
|
|
// The vector is not used here, but including it was causing compilation issues
|
|
|
|
// previously related to using explicit template argument (SFINAE disabled).
|
|
|
|
#include <vector>
|
|
|
|
|
2019-03-15 01:18:02 +00:00
|
|
|
#include "../../examples/example_host.h"
|
2018-11-26 15:52:36 +00:00
|
|
|
#include "../../examples/example_vm.h"
|
|
|
|
|
|
|
|
#include <evmc/evmc.hpp>
|
2019-03-15 01:18:02 +00:00
|
|
|
#include <evmc/helpers.hpp>
|
2018-11-26 15:52:36 +00:00
|
|
|
|
|
|
|
#include <gtest/gtest.h>
|
|
|
|
|
|
|
|
#include <cstring>
|
|
|
|
|
|
|
|
TEST(cpp, result)
|
|
|
|
{
|
|
|
|
static int release_called = 0;
|
|
|
|
release_called = 0;
|
|
|
|
|
|
|
|
{
|
|
|
|
EXPECT_EQ(release_called, 0);
|
|
|
|
auto raw_result = evmc_result{};
|
|
|
|
raw_result.output_data = static_cast<uint8_t*>(std::malloc(13));
|
|
|
|
raw_result.release = [](const evmc_result* r) {
|
|
|
|
std::free(const_cast<uint8_t*>(r->output_data));
|
|
|
|
++release_called;
|
|
|
|
};
|
|
|
|
|
|
|
|
auto res1 = evmc::result{raw_result};
|
|
|
|
auto res2 = std::move(res1);
|
|
|
|
|
|
|
|
EXPECT_EQ(release_called, 0);
|
|
|
|
|
|
|
|
[](evmc::result) {}(std::move(res2));
|
|
|
|
|
|
|
|
EXPECT_EQ(release_called, 1);
|
|
|
|
}
|
|
|
|
EXPECT_EQ(release_called, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(cpp, vm)
|
|
|
|
{
|
|
|
|
auto vm = evmc::vm{evmc_create_example_vm()};
|
|
|
|
EXPECT_TRUE(vm.is_abi_compatible());
|
|
|
|
|
|
|
|
auto r = vm.set_option("verbose", "3");
|
|
|
|
EXPECT_EQ(r, EVMC_SET_OPTION_SUCCESS);
|
|
|
|
|
|
|
|
EXPECT_EQ(vm.name(), std::string{"example_vm"});
|
|
|
|
EXPECT_NE(vm.version()[0], 0);
|
|
|
|
|
|
|
|
auto ctx = evmc_context{};
|
|
|
|
auto res = vm.execute(ctx, EVMC_MAX_REVISION, {}, nullptr, 0);
|
|
|
|
EXPECT_EQ(res.status_code, EVMC_FAILURE);
|
2019-05-29 11:11:52 +00:00
|
|
|
|
|
|
|
EXPECT_TRUE(vm.get_capabilities() & EVMC_CAPABILITY_EVM1);
|
2018-11-26 15:52:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST(cpp, vm_set_option)
|
|
|
|
{
|
2019-01-22 13:53:26 +00:00
|
|
|
evmc_instance raw_instance = {EVMC_ABI_VERSION, "", "", nullptr,
|
|
|
|
nullptr, nullptr, nullptr, nullptr};
|
2018-11-26 15:52:36 +00:00
|
|
|
raw_instance.destroy = [](evmc_instance*) {};
|
|
|
|
|
|
|
|
auto vm = evmc::vm{&raw_instance};
|
|
|
|
EXPECT_EQ(vm.set_option("1", "2"), EVMC_SET_OPTION_INVALID_NAME);
|
|
|
|
}
|
2019-03-15 01:18:02 +00:00
|
|
|
|
2019-07-03 16:57:08 +00:00
|
|
|
TEST(cpp, vm_null)
|
|
|
|
{
|
|
|
|
evmc::vm vm;
|
|
|
|
EXPECT_FALSE(vm);
|
|
|
|
EXPECT_TRUE(!vm);
|
|
|
|
}
|
|
|
|
|
2019-07-03 16:28:00 +00:00
|
|
|
TEST(cpp, vm_move)
|
|
|
|
{
|
|
|
|
static int destroy_counter = 0;
|
|
|
|
const auto template_instance =
|
|
|
|
evmc_instance{EVMC_ABI_VERSION, "", "", [](evmc_instance*) { ++destroy_counter; },
|
|
|
|
nullptr, nullptr, nullptr, nullptr};
|
|
|
|
|
|
|
|
EXPECT_EQ(destroy_counter, 0);
|
|
|
|
{
|
|
|
|
auto v1 = template_instance;
|
|
|
|
auto v2 = template_instance;
|
|
|
|
|
|
|
|
auto vm1 = evmc::vm{&v1};
|
2019-07-03 16:57:08 +00:00
|
|
|
EXPECT_TRUE(vm1);
|
2019-07-03 16:28:00 +00:00
|
|
|
vm1 = evmc::vm{&v2};
|
2019-07-03 16:57:08 +00:00
|
|
|
EXPECT_TRUE(vm1);
|
2019-07-03 16:28:00 +00:00
|
|
|
}
|
|
|
|
EXPECT_EQ(destroy_counter, 2);
|
|
|
|
{
|
|
|
|
auto v1 = template_instance;
|
|
|
|
|
|
|
|
auto vm1 = evmc::vm{&v1};
|
2019-07-03 16:57:08 +00:00
|
|
|
EXPECT_TRUE(vm1);
|
2019-07-03 16:28:00 +00:00
|
|
|
vm1 = evmc::vm{};
|
2019-07-03 16:57:08 +00:00
|
|
|
EXPECT_FALSE(vm1);
|
2019-07-03 16:28:00 +00:00
|
|
|
}
|
|
|
|
EXPECT_EQ(destroy_counter, 3);
|
|
|
|
{
|
|
|
|
auto v1 = template_instance;
|
|
|
|
|
|
|
|
auto vm1 = evmc::vm{&v1};
|
2019-07-03 16:57:08 +00:00
|
|
|
EXPECT_TRUE(vm1);
|
2019-07-03 16:28:00 +00:00
|
|
|
auto vm2 = std::move(vm1);
|
2019-07-03 16:57:08 +00:00
|
|
|
EXPECT_TRUE(vm2);
|
|
|
|
EXPECT_FALSE(vm1); // NOLINT
|
2019-07-03 16:28:00 +00:00
|
|
|
auto vm3 = std::move(vm2);
|
2019-07-03 16:57:08 +00:00
|
|
|
EXPECT_TRUE(vm3);
|
|
|
|
EXPECT_FALSE(vm2); // NOLINT
|
|
|
|
EXPECT_FALSE(vm1);
|
2019-07-03 16:28:00 +00:00
|
|
|
}
|
|
|
|
EXPECT_EQ(destroy_counter, 4);
|
2019-07-03 17:04:04 +00:00
|
|
|
{
|
|
|
|
// Moving to itself will destroy the VM and reset the evmc::vm.
|
|
|
|
auto v1 = template_instance;
|
|
|
|
|
|
|
|
auto vm1 = evmc::vm{&v1};
|
|
|
|
auto& vm1_ref = vm1;
|
|
|
|
vm1 = std::move(vm1_ref);
|
|
|
|
EXPECT_EQ(destroy_counter, 5); // Already destroyed.
|
|
|
|
EXPECT_FALSE(vm1); // Null.
|
|
|
|
}
|
|
|
|
EXPECT_EQ(destroy_counter, 5);
|
2019-07-03 16:28:00 +00:00
|
|
|
}
|
|
|
|
|
2019-03-15 01:18:02 +00:00
|
|
|
TEST(cpp, host)
|
|
|
|
{
|
|
|
|
// Use example host to execute all methods from the C++ host wrapper.
|
|
|
|
|
|
|
|
auto* host_context = example_host_create_context();
|
2019-03-28 15:23:23 +00:00
|
|
|
auto host = evmc::HostContext{host_context};
|
2019-03-15 01:18:02 +00:00
|
|
|
|
|
|
|
auto a = evmc_address{{1}};
|
|
|
|
auto v = evmc_bytes32{{7, 7, 7}};
|
|
|
|
|
|
|
|
EXPECT_FALSE(host.account_exists(a));
|
|
|
|
|
|
|
|
EXPECT_EQ(host.set_storage(a, {}, v), EVMC_STORAGE_MODIFIED);
|
|
|
|
EXPECT_EQ(host.set_storage(a, {}, v), EVMC_STORAGE_UNCHANGED);
|
|
|
|
EXPECT_EQ(host.get_storage(a, {}), v);
|
|
|
|
|
|
|
|
EXPECT_TRUE(is_zero(host.get_balance(a)));
|
|
|
|
|
|
|
|
EXPECT_EQ(host.get_code_size(a), 0);
|
|
|
|
EXPECT_EQ(host.get_code_hash(a), evmc_bytes32{});
|
|
|
|
EXPECT_EQ(host.copy_code(a, 0, nullptr, 0), 0);
|
|
|
|
|
|
|
|
host.selfdestruct(a, a);
|
|
|
|
|
2019-03-28 15:43:23 +00:00
|
|
|
auto tx = host.get_tx_context();
|
|
|
|
EXPECT_EQ(host.get_tx_context().block_number, tx.block_number);
|
2019-03-15 01:18:02 +00:00
|
|
|
|
|
|
|
EXPECT_EQ(host.get_block_hash(0), evmc_bytes32{});
|
|
|
|
|
|
|
|
host.emit_log(a, nullptr, 0, nullptr, 0);
|
|
|
|
|
|
|
|
example_host_destroy_context(host_context);
|
|
|
|
}
|
2019-04-29 12:53:17 +00:00
|
|
|
|
|
|
|
TEST(cpp, host_call)
|
|
|
|
{
|
|
|
|
// Use example host to test Host::call() method.
|
|
|
|
|
|
|
|
auto* host_context = example_host_create_context();
|
|
|
|
auto host = evmc::HostContext{host_context};
|
|
|
|
|
|
|
|
EXPECT_EQ(host.call({}).gas_left, 0);
|
|
|
|
|
|
|
|
auto msg = evmc_message{};
|
|
|
|
msg.gas = 1;
|
|
|
|
uint8_t input[] = {0xa, 0xb, 0xc};
|
|
|
|
msg.input_data = input;
|
|
|
|
msg.input_size = sizeof(input);
|
|
|
|
|
|
|
|
auto res = host.call(msg);
|
|
|
|
EXPECT_EQ(res.status_code, EVMC_REVERT);
|
|
|
|
EXPECT_EQ(res.output_size, msg.input_size);
|
|
|
|
EXPECT_TRUE(std::equal(&res.output_data[0], &res.output_data[res.output_size], msg.input_data));
|
|
|
|
|
|
|
|
example_host_destroy_context(host_context);
|
|
|
|
}
|
2019-05-14 14:06:56 +00:00
|
|
|
|
|
|
|
TEST(cpp, result_raii)
|
|
|
|
{
|
|
|
|
static auto release_called = 0;
|
|
|
|
release_called = 0;
|
|
|
|
auto release_fn = [](const evmc_result*) noexcept { ++release_called; };
|
|
|
|
|
|
|
|
{
|
|
|
|
auto raw_result = evmc_result{};
|
|
|
|
raw_result.status_code = EVMC_INTERNAL_ERROR;
|
|
|
|
raw_result.release = release_fn;
|
|
|
|
|
|
|
|
auto raii_result = evmc::result{raw_result};
|
|
|
|
EXPECT_EQ(raii_result.status_code, EVMC_INTERNAL_ERROR);
|
|
|
|
EXPECT_EQ(raii_result.gas_left, 0);
|
|
|
|
raii_result.gas_left = -1;
|
|
|
|
|
2019-05-15 14:51:37 +00:00
|
|
|
auto raw_result2 = raii_result.release_raw();
|
2019-05-14 14:06:56 +00:00
|
|
|
EXPECT_EQ(raw_result2.status_code, EVMC_INTERNAL_ERROR);
|
|
|
|
EXPECT_EQ(raw_result.status_code, EVMC_INTERNAL_ERROR);
|
|
|
|
EXPECT_EQ(raw_result2.gas_left, -1);
|
|
|
|
EXPECT_EQ(raw_result.gas_left, 0);
|
|
|
|
EXPECT_EQ(raw_result2.release, release_fn);
|
|
|
|
EXPECT_EQ(raw_result.release, release_fn);
|
|
|
|
}
|
|
|
|
EXPECT_EQ(release_called, 0);
|
|
|
|
|
|
|
|
{
|
|
|
|
auto raw_result = evmc_result{};
|
|
|
|
raw_result.status_code = EVMC_INTERNAL_ERROR;
|
|
|
|
raw_result.release = release_fn;
|
|
|
|
|
|
|
|
auto raii_result = evmc::result{raw_result};
|
|
|
|
EXPECT_EQ(raii_result.status_code, EVMC_INTERNAL_ERROR);
|
|
|
|
}
|
|
|
|
EXPECT_EQ(release_called, 1);
|
|
|
|
}
|
2019-05-14 09:53:47 +00:00
|
|
|
|
|
|
|
TEST(cpp, result_move)
|
|
|
|
{
|
|
|
|
static auto release_called = 0;
|
|
|
|
auto release_fn = [](const evmc_result*) noexcept { ++release_called; };
|
|
|
|
|
|
|
|
release_called = 0;
|
|
|
|
{
|
|
|
|
auto raw = evmc_result{};
|
|
|
|
raw.gas_left = -1;
|
|
|
|
raw.release = release_fn;
|
|
|
|
|
|
|
|
auto r0 = evmc::result{raw};
|
|
|
|
EXPECT_EQ(r0.gas_left, raw.gas_left);
|
|
|
|
|
|
|
|
auto r1 = std::move(r0);
|
|
|
|
EXPECT_EQ(r1.gas_left, raw.gas_left);
|
|
|
|
}
|
|
|
|
EXPECT_EQ(release_called, 1);
|
|
|
|
|
|
|
|
release_called = 0;
|
|
|
|
{
|
|
|
|
auto raw1 = evmc_result{};
|
|
|
|
raw1.gas_left = 1;
|
|
|
|
raw1.release = release_fn;
|
|
|
|
|
|
|
|
auto raw2 = evmc_result{};
|
|
|
|
raw2.gas_left = 1;
|
|
|
|
raw2.release = release_fn;
|
|
|
|
|
|
|
|
auto r1 = evmc::result{raw1};
|
|
|
|
auto r2 = evmc::result{raw2};
|
|
|
|
|
|
|
|
r2 = std::move(r1);
|
|
|
|
}
|
|
|
|
EXPECT_EQ(release_called, 2);
|
|
|
|
}
|