Skip to content

Commit

Permalink
eof: Add CALLF & RETF instructions
Browse files Browse the repository at this point in the history
Implement CALLF and RETF EOF instructions specified by
EIP-4750 EOF Functions (https://eips.ethereum.org/EIPS/eip-4750).

Co-authored-by: Andrei Maiboroda <andrei@ethereum.org>
  • Loading branch information
2 people authored and chfast committed Mar 20, 2023
1 parent 1a92f10 commit 7feccf8
Show file tree
Hide file tree
Showing 11 changed files with 99 additions and 6 deletions.
2 changes: 2 additions & 0 deletions lib/evmone/advanced_instructions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,8 @@ constexpr std::array<instruction_exec_fn, 256> instruction_implementations = [](
table[OP_RJUMP] = op_undefined;
table[OP_RJUMPI] = op_undefined;
table[OP_RJUMPV] = op_undefined;
table[OP_CALLF] = op_undefined;
table[OP_RETF] = op_undefined;

table[OP_DUPN] = op_undefined;
table[OP_SWAPN] = op_undefined;
Expand Down
2 changes: 2 additions & 0 deletions lib/evmone/baseline_instruction_table.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ constexpr auto legacy_cost_tables = []() noexcept {
tables[EVMC_CANCUN][OP_RJUMP] = instr::undefined;
tables[EVMC_CANCUN][OP_RJUMPI] = instr::undefined;
tables[EVMC_CANCUN][OP_RJUMPV] = instr::undefined;
tables[EVMC_CANCUN][OP_CALLF] = instr::undefined;
tables[EVMC_CANCUN][OP_RETF] = instr::undefined;
return tables;
}();

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 @@ -154,6 +154,8 @@ class ExecutionState
const advanced::AdvancedCodeAnalysis* advanced;
} analysis{};

std::vector<const uint8_t*> call_stack;

/// Stack space allocation.
///
/// This is the last field to make other fields' offsets of reasonable values.
Expand Down
16 changes: 16 additions & 0 deletions lib/evmone/instructions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -933,6 +933,22 @@ 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>;

inline code_iterator callf(StackTop /*stack*/, ExecutionState& state, code_iterator pos) noexcept
{
const auto index = read_uint16_be(&pos[1]);
state.call_stack.push_back(pos + 3);
const auto offset = state.analysis.baseline->code_offsets[index];
auto code = state.analysis.baseline->executable_code;
return code.data() + offset;
}

inline code_iterator retf(StackTop /*stack*/, ExecutionState& state, code_iterator /*pos*/) noexcept
{
const auto p = state.call_stack.back();
state.call_stack.pop_back();
return p;
}

template <evmc_status_code StatusCode>
inline StopToken return_impl(StackTop stack, ExecutionState& state) noexcept
{
Expand Down
3 changes: 3 additions & 0 deletions lib/evmone/instructions_opcodes.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,9 @@ enum Opcode : uint8_t
OP_LOG3 = 0xa3,
OP_LOG4 = 0xa4,

OP_CALLF = 0xb0,
OP_RETF = 0xb1,

OP_DUPN = 0xb5,
OP_SWAPN = 0xb6,

Expand Down
4 changes: 4 additions & 0 deletions lib/evmone/instructions_traits.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,8 @@ constexpr inline GasCostTable gas_costs = []() noexcept {
table[EVMC_CANCUN][OP_RJUMP] = 2;
table[EVMC_CANCUN][OP_RJUMPI] = 4;
table[EVMC_CANCUN][OP_RJUMPV] = 4;
table[EVMC_CANCUN][OP_CALLF] = 5;
table[EVMC_CANCUN][OP_RETF] = 3;

table[EVMC_PRAGUE] = table[EVMC_CANCUN];

Expand Down Expand Up @@ -381,6 +383,8 @@ constexpr inline std::array<Traits, 256> traits = []() noexcept {
table[OP_DELEGATECALL] = {"DELEGATECALL", 0, false, 6, -5, EVMC_HOMESTEAD};
table[OP_CREATE2] = {"CREATE2", 0, false, 4, -3, EVMC_CONSTANTINOPLE};
table[OP_STATICCALL] = {"STATICCALL", 0, false, 6, -5, EVMC_BYZANTIUM};
table[OP_CALLF] = {"CALLF", 2, false, 0, 0, EVMC_CANCUN};
table[OP_RETF] = {"RETF", 0, true, 0, 0, EVMC_CANCUN};
table[OP_REVERT] = {"REVERT", 0, true, 2, -2, EVMC_BYZANTIUM};
table[OP_INVALID] = {"INVALID", 0, true, 0, 0, EVMC_FRONTIER};
table[OP_SELFDESTRUCT] = {"SELFDESTRUCT", 0, true, 1, -1, EVMC_FRONTIER};
Expand Down
4 changes: 2 additions & 2 deletions lib/evmone/instructions_xmacro.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -219,8 +219,8 @@
ON_OPCODE_UNDEFINED(0xae) \
ON_OPCODE_UNDEFINED(0xaf) \
\
ON_OPCODE_UNDEFINED(0xb0) \
ON_OPCODE_UNDEFINED(0xb1) \
ON_OPCODE_IDENTIFIER(OP_CALLF, callf) \
ON_OPCODE_IDENTIFIER(OP_RETF, retf) \
ON_OPCODE_UNDEFINED(0xb2) \
ON_OPCODE_UNDEFINED(0xb3) \
ON_OPCODE_UNDEFINED(0xb4) \
Expand Down
1 change: 1 addition & 0 deletions test/unittests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ target_sources(
evm_eip3855_push0_test.cpp
evm_eip3860_initcode_test.cpp
evm_eof_test.cpp
evm_eof_function_test.cpp
evm_eof_rjump_test.cpp
evm_memory_test.cpp
evm_state_test.cpp
Expand Down
7 changes: 4 additions & 3 deletions test/unittests/eof_validation_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -319,10 +319,11 @@ TEST(eof_validation, EOF1_undefined_opcodes)

for (uint16_t opcode = 0; opcode <= 0xff; ++opcode)
{
// PUSH*, DUPN, SWAPN, RJUMP* require immediate argument to be valid, checked in
// a separate test
// PUSH*, DUPN, SWAPN, RJUMP*, CALLF require immediate argument to be valid,
// checked in a separate test.
if ((opcode >= OP_PUSH1 && opcode <= OP_PUSH32) || opcode == OP_DUPN ||
opcode == OP_SWAPN || opcode == OP_RJUMP || opcode == OP_RJUMPI || opcode == OP_RJUMPV)
opcode == OP_SWAPN || opcode == OP_RJUMP || opcode == OP_RJUMPI ||
opcode == OP_RJUMPV || opcode == OP_CALLF)
continue;
// These opcodes are deprecated since Cancun.
// gas_cost table current implementation does not allow to undef instructions.
Expand Down
59 changes: 59 additions & 0 deletions test/unittests/evm_eof_function_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// evmone: Fast Ethereum Virtual Machine implementation
// Copyright 2022 The evmone Authors.
// SPDX-License-Identifier: Apache-2.0

#include "evm_fixture.hpp"
#include "evmone/eof.hpp"

using evmone::test::evm;

TEST_P(evm, eof_function_example1)
{
// Relative jumps are not implemented in Advanced.
if (is_advanced())
return;

rev = EVMC_CANCUN;
const auto code =
"EF00 01 010008 020002 000f 0002 030000 00"
"00000002 02010002"
"6001 6008 b00001 " +
ret_top() + "03b1";

ASSERT_EQ((int)evmone::validate_eof(rev, code), (int)evmone::EOFValidationError{});

execute(code);
EXPECT_GAS_USED(EVMC_SUCCESS, 32);
EXPECT_OUTPUT_INT(7);
}

TEST_P(evm, eof_function_example2)
{
// Relative jumps are not implemented in Advanced.
if (is_advanced())
return;

rev = EVMC_CANCUN;
const auto code =
"ef0001 01000c 020003 003b 0017 001d 030000 00 00000004 01010003 01010004"
"60043560003560e01c63c766526781145d001c63c6c2ea1781145d00065050600080fd50b00002600052602060"
"00f350b0000160005260206000f3"
"600181115d0004506001b160018103b0000181029050b1"
"600281115d0004506001b160028103b0000260018203b00002019050b1"_hex;

ASSERT_EQ((int)evmone::validate_eof(rev, code), (int)evmone::EOFValidationError{});

// Call fac(5)
const auto calldata_fac =
"c76652670000000000000000000000000000000000000000000000000000000000000005"_hex;
execute(bytecode{code}, calldata_fac);
EXPECT_GAS_USED(EVMC_SUCCESS, 246);
EXPECT_EQ(output, "0000000000000000000000000000000000000000000000000000000000000078"_hex);

// Call fib(15)
const auto calldata_fib =
"c6c2ea17000000000000000000000000000000000000000000000000000000000000000f"_hex;
execute(bytecode{code}, calldata_fib);
EXPECT_GAS_USED(EVMC_SUCCESS, 44544);
EXPECT_EQ(output, "0000000000000000000000000000000000000000000000000000000000000262"_hex);
}
5 changes: 4 additions & 1 deletion test/unittests/instructions_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ constexpr bool is_terminating(Opcode op) noexcept
{
case OP_STOP:
case OP_RETURN:
case OP_RETF:
case OP_REVERT:
case OP_INVALID:
case OP_SELFDESTRUCT:
Expand All @@ -55,7 +56,7 @@ constexpr void validate_traits_of() noexcept
// immediate_size
if constexpr (Op >= OP_PUSH1 && Op <= OP_PUSH32)
static_assert(tr.immediate_size == Op - OP_PUSH1 + 1);
else if constexpr (Op == OP_RJUMP || Op == OP_RJUMPI)
else if constexpr (Op == OP_RJUMP || Op == OP_RJUMPI || Op == OP_CALLF)
static_assert(tr.immediate_size == 2);
else if constexpr (Op == OP_DUPN || Op == OP_SWAPN)
static_assert(tr.immediate_size == 1);
Expand Down Expand Up @@ -105,6 +106,8 @@ constexpr bool instruction_only_in_evmone(evmc_revision rev, Opcode op) noexcept
case OP_RJUMP:
case OP_RJUMPI:
case OP_RJUMPV:
case OP_CALLF:
case OP_RETF:
case OP_DUPN:
case OP_SWAPN:
return true;
Expand Down

0 comments on commit 7feccf8

Please sign in to comment.