Skip to content

Commit

Permalink
evmmax: Implement evmmax instructions
Browse files Browse the repository at this point in the history
Add new instruction implementation signature `code_iterator (*instr_fn)(StackTop, ExecutionState&, code_iterator, int64_t&)`
  • Loading branch information
rodiazet committed Jan 3, 2024
1 parent 3f96507 commit a434884
Show file tree
Hide file tree
Showing 5 changed files with 157 additions and 9 deletions.
9 changes: 9 additions & 0 deletions lib/evmone/advanced_instructions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,15 @@ inline code_iterator impl(AdvancedExecutionState& state, code_iterator pos) noex
state.stack.top_item += instr::traits[Op].stack_height_change;
return new_pos;
}

template <Opcode Op, code_iterator CoreFn(StackTop, ExecutionState&, code_iterator,
int64_t&) noexcept = core::impl<Op>>
inline code_iterator impl(AdvancedExecutionState& state, code_iterator pos) noexcept
{
const auto new_pos = CoreFn(state.stack.top_item, state, pos, state.gas_left);
state.stack.top_item += instr::traits[Op].stack_height_change;
return new_pos;
}
/// @}
} // namespace instr

Expand Down
7 changes: 7 additions & 0 deletions lib/evmone/baseline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,13 @@ struct Position
return instr_fn(pos.stack_top, state, pos.code_it);
}

[[release_inline]] inline code_iterator invoke(
code_iterator (*instr_fn)(StackTop, ExecutionState&, code_iterator, int64_t&) noexcept,
Position pos, int64_t& gas, ExecutionState& state) noexcept
{
return instr_fn(pos.stack_top, state, pos.code_it, gas);
}

[[release_inline]] inline code_iterator invoke(
TermResult (*instr_fn)(StackTop, int64_t, ExecutionState&) noexcept, Position pos, int64_t& gas,
ExecutionState& state) noexcept
Expand Down
132 changes: 132 additions & 0 deletions lib/evmone/instructions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1167,6 +1167,138 @@ inline TermResult selfdestruct(StackTop stack, int64_t gas_left, ExecutionState&
return {EVMC_SUCCESS, gas_left};
}

/// EVMMAX instructions
namespace
{
// TODO: Use it in `grom_memory` function
[[nodiscard]] inline int64_t evm_memory_expansion_cost(
const Memory& memory, uint64_t new_size) noexcept
{
const auto new_words = num_words(new_size);
const auto current_words = static_cast<int64_t>(memory.size() / word_size);
const auto new_cost = 3 * new_words + new_words * new_words / 512;
const auto current_cost = 3 * current_words + current_words * current_words / 512;
return new_cost - current_cost;
}
} // namespace

inline Result setupx(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept
{
static constexpr auto MAX_MOD_SIZE = 4096;

const auto mod_id = stack.pop();
const auto mod_offset_256 = stack.pop();
const auto mod_size_256 = stack.pop();
const auto vals_used_256 = stack.pop();

if (!check_memory(gas_left, state.memory, mod_offset_256, mod_size_256))
return {EVMC_OUT_OF_GAS, gas_left};

// Maximum allowed modulus size
if (mod_size_256 > MAX_MOD_SIZE / 8)
return {EVMC_FAILURE, gas_left};

const auto mod_ptr = &state.memory[static_cast<size_t>(mod_offset_256)];
const auto mod_size = static_cast<size_t>(mod_size_256);

// Modulus must be odd
if (mod_ptr[mod_size - 1] % 2 == 0)
return {EVMC_FAILURE, gas_left};

const auto vals_used = static_cast<size_t>(vals_used_256);
// max number of value slots is 256
if (vals_used_256 > 256)
return {EVMC_FAILURE, gas_left};

if (!state.evmmax_state.exists(mod_id))
{
const auto value_size_multiplier = (mod_size + 7) / 8;
if ((gas_left -= evm_memory_expansion_cost(
state.memory, (vals_used * value_size_multiplier * 8 + 31) / 32)) < 0)
{
return {EVMC_OUT_OF_GAS, gas_left};
}
}

const auto status = state.evmmax_state.setupx(gas_left, mod_id, mod_ptr, mod_size, vals_used);
return {status, gas_left};
}

inline Result loadx(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept
{
const auto dst_offset = stack.pop();
const auto val_idx = stack.pop();
const auto num_vals = stack.pop();

if (!check_memory(gas_left, state.memory, dst_offset,
num_vals * state.evmmax_state.active_mod_value_size_multiplier() * 8))
return {EVMC_OUT_OF_GAS, gas_left};

const auto status =
state.evmmax_state.loadx(gas_left, &state.memory[static_cast<size_t>(dst_offset)],
static_cast<size_t>(val_idx), static_cast<size_t>(num_vals));

return {status, gas_left};
}

inline Result storex(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept
{
const auto dst_val = stack.pop();
const auto offset = stack.pop();
const auto num_vals = stack.pop();

if (!check_memory(gas_left, state.memory, offset,
num_vals * state.evmmax_state.active_mod_value_size_multiplier() * 8))
return {EVMC_OUT_OF_GAS, gas_left};

const auto status =
state.evmmax_state.storex(gas_left, &state.memory[static_cast<size_t>(offset)],
static_cast<size_t>(dst_val), static_cast<size_t>(num_vals));

return {status, gas_left};
}

inline code_iterator addmodx(
StackTop /*stack*/, ExecutionState& state, code_iterator pos, int64_t& gas_left) noexcept
{
const auto dst_idx = pos[1];
const auto x_idx = pos[2];
const auto y_idx = pos[3];

state.status = state.evmmax_state.addmodx(gas_left, dst_idx, x_idx, y_idx);
if (state.status == EVMC_SUCCESS)
return pos + 4;
else
return nullptr;
}

inline code_iterator submodx(
StackTop /*stack*/, ExecutionState& state, code_iterator pos, int64_t& gas_left) noexcept
{
const auto dst_idx = pos[1];
const auto x_idx = pos[2];
const auto y_idx = pos[3];

state.status = state.evmmax_state.submodx(gas_left, dst_idx, x_idx, y_idx);
if (state.status == EVMC_SUCCESS)
return pos + 4;
else
return nullptr;
}

inline code_iterator mulmodx(
StackTop /*stack*/, ExecutionState& state, code_iterator pos, int64_t& gas_left) noexcept
{
const auto dst_idx = pos[1];
const auto x_idx = pos[2];
const auto y_idx = pos[3];

state.status = state.evmmax_state.mulmodx(gas_left, dst_idx, x_idx, y_idx);
if (state.status == EVMC_SUCCESS)
return pos + 4;
else
return nullptr;
}

/// Maps an opcode to the instruction implementation.
///
Expand Down
6 changes: 3 additions & 3 deletions lib/evmone/instructions_traits.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -185,9 +185,9 @@ constexpr inline GasCostTable gas_costs = []() noexcept {
table[EVMC_PRAGUE][OP_DATASIZE] = 2;
table[EVMC_PRAGUE][OP_DATACOPY] = 3;
table[EVMC_EVMMAX][OP_SETUPX] = 3;
table[EVMC_EVMMAX][OP_ADDMODX] = 1;
table[EVMC_EVMMAX][OP_SUBMODX] = 1;
table[EVMC_EVMMAX][OP_MULMODX] = 1;
table[EVMC_EVMMAX][OP_ADDMODX] = 0;
table[EVMC_EVMMAX][OP_SUBMODX] = 0;
table[EVMC_EVMMAX][OP_MULMODX] = 0;
table[EVMC_EVMMAX][OP_LOADX] = 3;
table[EVMC_EVMMAX][OP_STOREX] = 3;

Expand Down
12 changes: 6 additions & 6 deletions lib/evmone/instructions_xmacro.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,12 @@
ON_OPCODE_UNDEFINED(0x1f) \
\
ON_OPCODE_IDENTIFIER(OP_KECCAK256, keccak256) \
ON_OPCODE_UNDEFINED(0x21) \
ON_OPCODE_UNDEFINED(0x22) \
ON_OPCODE_UNDEFINED(0x23) \
ON_OPCODE_UNDEFINED(0x24) \
ON_OPCODE_UNDEFINED(0x25) \
ON_OPCODE_UNDEFINED(0x26) \
ON_OPCODE_IDENTIFIER(OP_SETUPX, setupx) \
ON_OPCODE_IDENTIFIER(OP_ADDMODX, addmodx) \
ON_OPCODE_IDENTIFIER(OP_SUBMODX, submodx) \
ON_OPCODE_IDENTIFIER(OP_MULMODX, mulmodx) \
ON_OPCODE_IDENTIFIER(OP_LOADX, loadx) \
ON_OPCODE_IDENTIFIER(OP_STOREX, storex) \
ON_OPCODE_UNDEFINED(0x27) \
ON_OPCODE_UNDEFINED(0x28) \
ON_OPCODE_UNDEFINED(0x29) \
Expand Down

0 comments on commit a434884

Please sign in to comment.