Skip to content

Commit

Permalink
CREATE3 and RETURNCONTRACT implementations
Browse files Browse the repository at this point in the history
  • Loading branch information
gumb0 committed Feb 6, 2023
1 parent 927db27 commit e5b5a78
Show file tree
Hide file tree
Showing 14 changed files with 318 additions and 23 deletions.
2 changes: 1 addition & 1 deletion evmc
Submodule evmc updated 1 files
+4 −1 include/evmc/evmc.h
36 changes: 36 additions & 0 deletions lib/evmone/advanced_instructions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,22 @@ inline StopToken impl(AdvancedExecutionState& state) noexcept
return CoreFn(state.stack.top_item, state);
}

template <Opcode Op,
evmc_status_code CoreFn(StackTop, ExecutionState&, code_iterator) noexcept = core::impl<Op>>
inline evmc_status_code impl(AdvancedExecutionState& state, code_iterator pos) noexcept
{
// Stack height adjustment may be omitted.
return CoreFn(state.stack.top_item, state, pos);
}

template <Opcode Op,
StopToken CoreFn(StackTop, ExecutionState&, code_iterator) noexcept = core::impl<Op>>
inline StopToken impl(AdvancedExecutionState& state, code_iterator pos) noexcept
{
// Stack height adjustment may be omitted.
return CoreFn(state.stack.top_item, state, pos);
}

template <Opcode Op,
code_iterator CoreFn(StackTop, ExecutionState&, code_iterator) noexcept = core::impl<Op>>
inline code_iterator impl(AdvancedExecutionState& state, code_iterator pos) noexcept
Expand Down Expand Up @@ -95,6 +111,24 @@ const Instruction* op(const Instruction* /*instr*/, AdvancedExecutionState& stat
return state.exit(InstrFn(state).status);
}

/// Wraps the generic instruction implementation to advanced instruction function signature.
template <evmc_status_code InstrFn(AdvancedExecutionState&, code_iterator) noexcept>
const Instruction* op(const Instruction* instr, AdvancedExecutionState& state) noexcept
{
// FIXME 0??
if (const auto status_code = InstrFn(state, 0); status_code != EVMC_SUCCESS)
return state.exit(status_code);
return ++instr;
}

/// Wraps the generic instruction implementation to advanced instruction function signature.
template <StopToken InstrFn(AdvancedExecutionState&, code_iterator) noexcept>
const Instruction* op(const Instruction* /*instr*/, AdvancedExecutionState& state) noexcept
{
// FIXME 0??
return state.exit(InstrFn(state, 0).status);
}

const Instruction* op_sstore(const Instruction* instr, AdvancedExecutionState& state) noexcept
{
const auto gas_left_correction = state.current_block_cost - instr->arg.number;
Expand Down Expand Up @@ -253,6 +287,8 @@ constexpr std::array<instruction_exec_fn, 256> instruction_implementations = [](

table[OP_DUPN] = op_undefined;
table[OP_SWAPN] = op_undefined;
table[OP_CREATE3] = op_undefined;
table[OP_RETURNCONTRACT] = op_undefined;

return table;
}();
Expand Down
29 changes: 27 additions & 2 deletions lib/evmone/baseline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,26 @@ struct Position
state.status = instr_fn(pos.stack_top, state).status;
return nullptr;
}

[[release_inline]] inline code_iterator invoke(
evmc_status_code (*instr_fn)(StackTop, ExecutionState&, code_iterator) noexcept, Position pos,
ExecutionState& state) noexcept
{
if (const auto status = instr_fn(pos.stack_top, state, pos.code_it); status != EVMC_SUCCESS)
{
state.status = status;
return nullptr;
}
return pos.code_it + 1;
}

[[release_inline]] inline code_iterator invoke(
StopToken (*instr_fn)(StackTop, ExecutionState&, code_iterator) noexcept, Position pos,
ExecutionState& state) noexcept
{
state.status = instr_fn(pos.stack_top, state, pos.code_it).status;
return nullptr;
}
/// @}

/// A helper to invoke the instruction implementation of the given opcode Op.
Expand Down Expand Up @@ -356,8 +376,13 @@ evmc_result execute(const VM& vm, ExecutionState& state, const CodeAnalysis& ana
const auto gas_refund = (state.status == EVMC_SUCCESS) ? state.gas_refund : 0;

assert(state.output_size != 0 || state.output_offset == 0);
const auto result = evmc::make_result(state.status, gas_left, gas_refund,
state.output_size != 0 ? &state.memory[state.output_offset] : nullptr, state.output_size);
const auto result =
(state.deploy_container.has_value() ?
evmc::make_result(state.status, gas_left, gas_refund,
state.deploy_container->data(), state.deploy_container->size()) :
evmc::make_result(state.status, gas_left, gas_refund,
state.output_size != 0 ? &state.memory[state.output_offset] : nullptr,
state.output_size));

if (INTX_UNLIKELY(tracer != nullptr))
tracer->notify_execution_end(result);
Expand Down
83 changes: 78 additions & 5 deletions lib/evmone/eof.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#include "eof.hpp"
#include "baseline_instruction_table.hpp"
#include "execution_state.hpp"
#include "instructions_traits.hpp"

#include <algorithm>
Expand All @@ -23,7 +24,8 @@ constexpr uint8_t TERMINATOR = 0x00;
constexpr uint8_t TYPE_SECTION = 0x01;
constexpr uint8_t CODE_SECTION = 0x02;
constexpr uint8_t DATA_SECTION = 0x03;
constexpr uint8_t MAX_SECTION = DATA_SECTION;
constexpr uint8_t CONTAINER_SECTION = 0x04;
constexpr uint8_t MAX_SECTION = CONTAINER_SECTION;
constexpr auto CODE_SECTION_NUMBER_LIMIT = 1024;
constexpr auto MAX_STACK_HEIGHT = 0x0400;
constexpr auto OUTPUTS_INPUTS_NUMBER_LIMIT = 0x7F;
Expand All @@ -34,13 +36,15 @@ size_t eof_header_size(const EOFSectionHeaders& headers) noexcept
{
const auto non_code_section_count = headers[TYPE_SECTION].size() + headers[DATA_SECTION].size();
const auto code_section_count = headers[CODE_SECTION].size();
const auto container_section_count = headers[CONTAINER_SECTION].size();

constexpr auto non_code_section_header_size = 3; // (SECTION_ID + SIZE) per each section
constexpr auto code_section_size_size = 2;

return sizeof(MAGIC) + 1 + // 1 version byte
non_code_section_count * non_code_section_header_size + sizeof(CODE_SECTION) + 2 +
code_section_count * code_section_size_size + sizeof(TERMINATOR);
code_section_count * code_section_size_size + sizeof(TERMINATOR) +
sizeof(CONTAINER_SECTION) + 2 + container_section_count * code_section_size_size;
}

std::pair<EOFSectionHeaders, EOFValidationError> validate_eof_headers(bytes_view container)
Expand Down Expand Up @@ -114,14 +118,35 @@ std::pair<EOFSectionHeaders, EOFValidationError> validate_eof_headers(bytes_view
state = State::section_size;
break;
}
case CONTAINER_SECTION:
{
// TODO validation of order of sections
if (section_headers[TYPE_SECTION].empty())
return {{}, EOFValidationError::container_section_before_type_section};
if (section_headers[CODE_SECTION].empty())
return {{}, EOFValidationError::container_section_before_code_section};
if (!section_headers[CONTAINER_SECTION].empty())
return {{}, EOFValidationError::multiple_container_sections_headers};
if (it == container_end)
return {{}, EOFValidationError::incomplete_section_number};
const auto section_number_hi = *it++;
if (it == container_end)
return {{}, EOFValidationError::incomplete_section_number};
const auto section_number_lo = *it++;
section_num = static_cast<uint16_t>((section_number_hi << 8) | section_number_lo);
if (section_num == 0)
return {{}, EOFValidationError::zero_section_size};
state = State::section_size;
break;
}
default:
return {{}, EOFValidationError::unknown_section_id};
}
break;
}
case State::section_size:
{
if (section_id == CODE_SECTION)
if (section_id == CODE_SECTION || section_id == CONTAINER_SECTION)
{
assert(section_num > 0); // Guaranteed by previous validation step.
for (size_t i = 0; i < section_num; ++i)
Expand Down Expand Up @@ -472,7 +497,9 @@ std::pair<EOF1Header, EOFValidationError> validate_eof1(
offset += code_size;
}

EOF1Header header{code_sizes, code_offsets, data_size, types};
// TODO calculate container offsets and add to EOF1Header

EOF1Header header{code_sizes, code_offsets, data_size, {}, {}, types};

for (size_t code_idx = 0; code_idx < header.code_sizes.size(); ++code_idx)
{
Expand All @@ -493,6 +520,8 @@ std::pair<EOF1Header, EOFValidationError> validate_eof1(
return {{}, EOFValidationError::invalid_max_stack_height};
}

// TODO recursively validate embedded container section

return {header, EOFValidationError::success};
}

Expand All @@ -511,6 +540,19 @@ size_t EOF1Header::code_end(size_t index) const noexcept
return size_t{code_offsets[index]} + code_sizes[index];
}

size_t EOF1Header::container_begin(size_t index) const noexcept
{
assert(index < container_offsets.size());
return container_offsets[index];
}

size_t EOF1Header::container_end(size_t index) const noexcept
{
assert(index < container_offsets.size());
assert(index < container_sizes.size());
return size_t{container_offsets[index]} + container_sizes[index];
}

bool is_eof_code(bytes_view code) noexcept
{
return code.size() > 1 && code[0] == MAGIC[0] && code[1] == MAGIC[1];
Expand All @@ -523,7 +565,7 @@ EOF1Header read_valid_eof1_header(bytes_view container)
while (*it != TERMINATOR)
{
const auto section_id = *it++;
if (section_id == CODE_SECTION)
if (section_id == CODE_SECTION || section_id == CONTAINER_SECTION)
{
const auto code_section_num_hi = *it++;
const auto code_section_num_lo = *it++;
Expand Down Expand Up @@ -576,9 +618,34 @@ EOF1Header read_valid_eof1_header(bytes_view container)
header.data_size =
section_headers[DATA_SECTION].empty() ? uint16_t{0} : section_headers[DATA_SECTION][0];

header.container_sizes = section_headers[CONTAINER_SECTION];
std::vector<uint16_t> container_offsets;
auto container_offset = code_offset + header.data_size;
for (const auto container_size : header.container_sizes)
{
header.container_offsets.emplace_back(static_cast<uint16_t>(container_offset));
container_offset += container_size;
}

return header;
}

void append_data_section(bytes& container, bytes_view aux_data)
{
const auto header = read_valid_eof1_header(container);
const auto insert_pos =
header.container_offsets.empty() ? container.size() : header.container_begin(0);
container.insert(insert_pos, aux_data);
const auto new_data_size = header.data_size + aux_data.size();

// find data size position
const auto num_code_sections = header.code_sizes.size();
const auto data_size_pos = std::size(MAGIC) + 1 + 3 + 3 + 2 * num_code_sections + 1;
// Update data size
container[data_size_pos] = static_cast<uint8_t>(new_data_size >> 8);
container[data_size_pos + 1] = static_cast<uint8_t>(new_data_size);
}

uint8_t get_eof_version(bytes_view container) noexcept
{
return (container.size() >= 3 && container[0] == MAGIC[0] && container[1] == MAGIC[1]) ?
Expand Down Expand Up @@ -679,6 +746,12 @@ std::string_view get_error_message(EOFValidationError err) noexcept
return "stack_underflow";
case EOFValidationError::invalid_code_section_index:
return "invalid_code_section_index";
case EOFValidationError::multiple_container_sections_headers:
return "multiple_container_sections_headers";
case EOFValidationError::container_section_before_type_section:
return "container_section_before_type_section";
case EOFValidationError::container_section_before_code_section:
return "container_section_before_code_section";
case EOFValidationError::impossible:
return "impossible";
}
Expand Down
14 changes: 14 additions & 0 deletions lib/evmone/eof.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

namespace evmone
{
using bytes = std::basic_string<uint8_t>;
using bytes_view = std::basic_string_view<uint8_t>;

struct EOF1TypeHeader
Expand All @@ -32,12 +33,20 @@ struct EOF1Header
/// Offset of every code section start;
std::vector<uint16_t> code_offsets;
uint16_t data_size = 0;
/// Size of every container section.
std::vector<uint16_t> container_sizes;
/// Offset of every container section start;
std::vector<uint16_t> container_offsets;

std::vector<EOF1TypeHeader> types;

/// Returns offset of code section start.
[[nodiscard]] EVMC_EXPORT size_t code_begin(size_t index) const noexcept;
[[nodiscard]] EVMC_EXPORT size_t code_end(size_t index) const noexcept;
/// Returns offset of container section start.
[[nodiscard]] EVMC_EXPORT size_t container_begin(size_t index) const noexcept;
/// Returns offset of container section end.
[[nodiscard]] EVMC_EXPORT size_t container_end(size_t index) const noexcept;
};

/// Checks if code starts with EOF FORMAT + MAGIC, doesn't validate the format.
Expand All @@ -47,6 +56,8 @@ struct EOF1Header
/// (must be true for all EOF contracts on-chain)
[[nodiscard]] EVMC_EXPORT EOF1Header read_valid_eof1_header(bytes_view container);

void append_data_section(bytes& contaniner, bytes_view aux_data);

enum class EOFValidationError
{
success,
Expand Down Expand Up @@ -86,6 +97,9 @@ enum class EOFValidationError
unreachable_instructions,
stack_underflow,
invalid_code_section_index,
multiple_container_sections_headers,
container_section_before_type_section,
container_section_before_code_section,

impossible,
};
Expand Down
2 changes: 2 additions & 0 deletions lib/evmone/execution_state.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,8 @@ class ExecutionState
size_t output_offset = 0;
size_t output_size = 0;

std::optional<bytes> deploy_container;

private:
evmc_tx_context m_tx = {};

Expand Down
27 changes: 27 additions & 0 deletions lib/evmone/instructions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#pragma once

#include "baseline.hpp"
#include "eof.hpp"
#include "execution_state.hpp"
#include "instructions_traits.hpp"
#include "instructions_xmacro.hpp"
Expand Down Expand Up @@ -935,6 +936,8 @@ evmc_status_code create_impl(StackTop stack, ExecutionState& state) noexcept;
inline constexpr auto create = create_impl<OP_CREATE>;
inline constexpr auto create2 = create_impl<OP_CREATE2>;

evmc_status_code create3(StackTop stack, ExecutionState& state, code_iterator pos) noexcept;

inline code_iterator callf(StackTop /*stack*/, ExecutionState& state, code_iterator pos) noexcept
{
const auto index = (size_t{pos[1]} << 8) | pos[2];
Expand Down Expand Up @@ -968,6 +971,30 @@ inline StopToken return_impl(StackTop stack, ExecutionState& state) noexcept
inline constexpr auto return_ = return_impl<EVMC_SUCCESS>;
inline constexpr auto revert = return_impl<EVMC_REVERT>;

inline StopToken returncontract(StackTop stack, ExecutionState& state, code_iterator pos) noexcept
{
const auto& offset = stack[0];
const auto& size = stack[1];

if (!check_memory(state, offset, size))
return {EVMC_OUT_OF_GAS};

const auto deploy_container_index = size_t{pos[1]};

const auto header = read_valid_eof1_header(state.original_code);
bytes deploy_container{&state.original_code[header.container_begin(deploy_container_index)],
header.container_sizes[deploy_container_index]};

// Append (offset, size) to data section
const auto deploy_header = read_valid_eof1_header(deploy_container);
append_data_section(
deploy_container, {&state.memory[static_cast<size_t>(offset)], static_cast<size_t>(size)});

state.deploy_container = std::move(deploy_container);

return {EVMC_SUCCESS};
}

inline StopToken selfdestruct(StackTop stack, ExecutionState& state) noexcept
{
if (state.in_static_mode())
Expand Down
Loading

0 comments on commit e5b5a78

Please sign in to comment.