diff --git a/lib/evmone/advanced_analysis.hpp b/lib/evmone/advanced_analysis.hpp index e4da712b77..8804443a3f 100644 --- a/lib/evmone/advanced_analysis.hpp +++ b/lib/evmone/advanced_analysis.hpp @@ -149,7 +149,7 @@ struct OpTableEntry { instruction_exec_fn fn; int16_t gas_cost; - int8_t stack_req; + uint8_t stack_req; int8_t stack_change; }; diff --git a/lib/evmone/advanced_instructions.cpp b/lib/evmone/advanced_instructions.cpp index 5ad8c9ce1c..b1cbd4afe0 100644 --- a/lib/evmone/advanced_instructions.cpp +++ b/lib/evmone/advanced_instructions.cpp @@ -70,6 +70,14 @@ inline code_iterator impl(AdvancedExecutionState& state, code_iterator pos) noex state.stack.top_item += instr::traits[Op].stack_height_change; return new_pos; } + +template > +inline code_iterator impl(AdvancedExecutionState& state, code_iterator pos) noexcept +{ + const auto new_pos = CoreFn(state.stack.top_item, pos); + state.stack.top_item += instr::traits[Op].stack_height_change; + return new_pos; +} /// @} } // namespace instr diff --git a/lib/evmone/baseline.cpp b/lib/evmone/baseline.cpp index 6acb4a4756..7146be9a6b 100644 --- a/lib/evmone/baseline.cpp +++ b/lib/evmone/baseline.cpp @@ -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, code_iterator) noexcept, Position pos, int64_t& /*gas*/, + ExecutionState& /*state*/) noexcept +{ + return instr_fn(pos.stack_top, pos.code_it); +} + [[release_inline]] inline code_iterator invoke( TermResult (*instr_fn)(StackTop, int64_t, ExecutionState&) noexcept, Position pos, int64_t& gas, ExecutionState& state) noexcept diff --git a/lib/evmone/baseline_instruction_table.cpp b/lib/evmone/baseline_instruction_table.cpp index 95405d436b..9440731393 100644 --- a/lib/evmone/baseline_instruction_table.cpp +++ b/lib/evmone/baseline_instruction_table.cpp @@ -34,6 +34,8 @@ constexpr auto legacy_cost_tables = []() noexcept { tables[EVMC_PRAGUE][OP_DATALOADN] = instr::undefined; tables[EVMC_PRAGUE][OP_DATASIZE] = instr::undefined; tables[EVMC_PRAGUE][OP_DATACOPY] = instr::undefined; + tables[EVMC_PRAGUE][OP_DUPN] = instr::undefined; + tables[EVMC_PRAGUE][OP_SWAPN] = instr::undefined; return tables; }(); diff --git a/lib/evmone/eof.cpp b/lib/evmone/eof.cpp index 640c197caa..75f1cdc539 100644 --- a/lib/evmone/eof.cpp +++ b/lib/evmone/eof.cpp @@ -370,7 +370,7 @@ std::variant validate_max_stack_height( const auto opcode = static_cast(code[i]); - auto stack_height_required = instr::traits[opcode].stack_height_required; + int stack_height_required = instr::traits[opcode].stack_height_required; auto stack_height_change = instr::traits[opcode].stack_height_change; auto stack_height = stack_heights[i]; @@ -380,7 +380,7 @@ std::variant validate_max_stack_height( { const auto fid = read_uint16_be(&code[i + 1]); - stack_height_required = static_cast(code_types[fid].inputs); + stack_height_required = code_types[fid].inputs; if (stack_height + code_types[fid].max_stack_height - stack_height_required > STACK_SIZE_LIMIT) @@ -401,26 +401,29 @@ std::variant validate_max_stack_height( if (code_types[fid].outputs == NON_RETURNING_FUNCITON) { - stack_height_required = static_cast(code_types[fid].inputs); + stack_height_required = code_types[fid].inputs; } else { if (code_types[func_index].outputs < code_types[fid].outputs) return EOFValidationError::jumpf_destination_incompatible_outputs; - stack_height_required = - static_cast(code_types[func_index].outputs + code_types[fid].inputs - - code_types[fid].outputs); + stack_height_required = code_types[func_index].outputs + code_types[fid].inputs - + code_types[fid].outputs; if (stack_heights[i] > stack_height_required) return EOFValidationError::stack_higher_than_outputs_required; } } else if (opcode == OP_RETF) { - stack_height_required = static_cast(code_types[func_index].outputs); + stack_height_required = code_types[func_index].outputs; if (stack_height > code_types[func_index].outputs) return EOFValidationError::stack_higher_than_outputs_required; } + else if (opcode == OP_DUPN) + stack_height_required = code[i + 1] + 1; + else if (opcode == OP_SWAPN) + stack_height_required = code[i + 1] + 2; if (stack_height < stack_height_required) return EOFValidationError::stack_underflow; diff --git a/lib/evmone/instructions.hpp b/lib/evmone/instructions.hpp index 03d9a813d4..23bbba058b 100644 --- a/lib/evmone/instructions.hpp +++ b/lib/evmone/instructions.hpp @@ -902,38 +902,16 @@ inline void swap(StackTop stack) noexcept a[3] = t3; } -inline code_iterator dupn(StackTop stack, ExecutionState& state, code_iterator pos) noexcept +inline code_iterator dupn(StackTop stack, code_iterator pos) noexcept { - const auto n = pos[1] + 1; - - const auto stack_size = &stack.top() - state.stack_space.bottom(); - - if (stack_size < n) - { - state.status = EVMC_STACK_UNDERFLOW; - return nullptr; - } - - stack.push(stack[n - 1]); - + stack.push(stack[pos[1]]); return pos + 2; } -inline code_iterator swapn(StackTop stack, ExecutionState& state, code_iterator pos) noexcept +inline code_iterator swapn(StackTop stack, code_iterator pos) noexcept { - const auto n = pos[1] + 1; - - const auto stack_size = &stack.top() - state.stack_space.bottom(); - - if (stack_size <= n) - { - state.status = EVMC_STACK_UNDERFLOW; - return nullptr; - } - // TODO: This may not be optimal, see instr::core::swap(). - std::swap(stack.top(), stack[n]); - + std::swap(stack.top(), stack[pos[1] + 1]); return pos + 2; } diff --git a/lib/evmone/instructions_traits.hpp b/lib/evmone/instructions_traits.hpp index 26b43b89f0..41e8ff744c 100644 --- a/lib/evmone/instructions_traits.hpp +++ b/lib/evmone/instructions_traits.hpp @@ -204,7 +204,7 @@ struct Traits bool is_terminating = false; /// The number of stack items the instruction accesses during execution. - int8_t stack_height_required = 0; + uint8_t stack_height_required = 0; /// The stack height change caused by the instruction execution. Can be negative. int8_t stack_height_change = 0; diff --git a/test/unittests/eof_validation_test.cpp b/test/unittests/eof_validation_test.cpp index 88b6d24d31..6be5ae1772 100644 --- a/test/unittests/eof_validation_test.cpp +++ b/test/unittests/eof_validation_test.cpp @@ -982,6 +982,40 @@ TEST(eof_validation, retf_stack_validation) EXPECT_EQ(validate_eof(code), EOFValidationError::stack_higher_than_outputs_required); } +TEST(eof_validation, dupn_stack_validation) +{ + const auto pushes = 20 * push(1); + EXPECT_EQ(validate_eof(eof_bytecode(pushes + OP_DUPN + "00" + OP_STOP, 21)), + EOFValidationError::success); + EXPECT_EQ(validate_eof(eof_bytecode(pushes + OP_DUPN + "13" + OP_STOP, 21)), + EOFValidationError::success); + EXPECT_EQ(validate_eof(eof_bytecode(pushes + OP_DUPN + "14" + OP_STOP, 21)), + EOFValidationError::stack_underflow); + EXPECT_EQ(validate_eof(eof_bytecode(pushes + OP_DUPN + "d0" + OP_STOP, 21)), + EOFValidationError::stack_underflow); + EXPECT_EQ(validate_eof(eof_bytecode(pushes + OP_DUPN + "fe" + OP_STOP, 21)), + EOFValidationError::stack_underflow); + EXPECT_EQ(validate_eof(eof_bytecode(pushes + OP_DUPN + "ff" + OP_STOP, 21)), + EOFValidationError::stack_underflow); +} + +TEST(eof_validation, swapn_stack_validation) +{ + const auto pushes = 20 * push(1); + EXPECT_EQ(validate_eof(eof_bytecode(pushes + OP_SWAPN + "00" + OP_STOP, 20)), + EOFValidationError::success); + EXPECT_EQ(validate_eof(eof_bytecode(pushes + OP_SWAPN + "12" + OP_STOP, 20)), + EOFValidationError::success); + EXPECT_EQ(validate_eof(eof_bytecode(pushes + OP_SWAPN + "13" + OP_STOP, 20)), + EOFValidationError::stack_underflow); + EXPECT_EQ(validate_eof(eof_bytecode(pushes + OP_SWAPN + "d0" + OP_STOP, 20)), + EOFValidationError::stack_underflow); + EXPECT_EQ(validate_eof(eof_bytecode(pushes + OP_SWAPN + "fe" + OP_STOP, 20)), + EOFValidationError::stack_underflow); + EXPECT_EQ(validate_eof(eof_bytecode(pushes + OP_SWAPN + "ff" + OP_STOP, 20)), + EOFValidationError::stack_underflow); +} + TEST(eof_validation, non_returning_status) { // Non-returning with no JUMPF and no RETF diff --git a/test/unittests/evm_eip663_dupn_swapn_test.cpp b/test/unittests/evm_eip663_dupn_swapn_test.cpp index 117b3cf340..8da3561c36 100644 --- a/test/unittests/evm_eip663_dupn_swapn_test.cpp +++ b/test/unittests/evm_eip663_dupn_swapn_test.cpp @@ -22,20 +22,17 @@ TEST_P(evm, dupn) for (uint64_t i = 1; i <= 20; ++i) pushes += push(i); - execute(pushes + OP_DUPN + "00" + ret_top()); + execute(eof_bytecode(pushes + OP_DUPN + "00" + ret_top(), 22)); EXPECT_STATUS(EVMC_SUCCESS); EXPECT_OUTPUT_INT(20); - execute(pushes + OP_DUPN + "02" + ret_top()); + execute(eof_bytecode(pushes + OP_DUPN + "02" + ret_top(), 22)); EXPECT_STATUS(EVMC_SUCCESS); EXPECT_OUTPUT_INT(18); - execute(pushes + OP_DUPN + "13" + ret_top()); + execute(eof_bytecode(pushes + OP_DUPN + "13" + ret_top(), 22)); EXPECT_STATUS(EVMC_SUCCESS); EXPECT_OUTPUT_INT(1); - - execute(pushes + OP_DUPN + "14" + ret_top()); - EXPECT_STATUS(EVMC_STACK_UNDERFLOW); } TEST_P(evm, swapn) @@ -50,32 +47,29 @@ TEST_P(evm, swapn) for (uint64_t i = 1; i <= 20; ++i) pushes += push(i); - execute(pushes + OP_SWAPN + "00" + ret_top()); + execute(eof_bytecode(pushes + OP_SWAPN + "00" + ret_top(), 21)); EXPECT_STATUS(EVMC_SUCCESS); EXPECT_OUTPUT_INT(19); - execute(pushes + OP_SWAPN + "00" + OP_DUPN + "01" + ret_top()); + execute(eof_bytecode(pushes + OP_SWAPN + "00" + OP_DUPN + "01" + ret_top(), 22)); EXPECT_STATUS(EVMC_SUCCESS); EXPECT_OUTPUT_INT(20); - execute(pushes + OP_SWAPN + "01" + ret_top()); + execute(eof_bytecode(pushes + OP_SWAPN + "01" + ret_top(), 21)); EXPECT_STATUS(EVMC_SUCCESS); EXPECT_OUTPUT_INT(18); - execute(pushes + OP_SWAPN + "01" + OP_DUPN + "02" + ret_top()); + execute(eof_bytecode(pushes + OP_SWAPN + "01" + OP_DUPN + "02" + ret_top(), 22)); EXPECT_STATUS(EVMC_SUCCESS); EXPECT_OUTPUT_INT(20); - execute(pushes + OP_SWAPN + "12" + ret_top()); + execute(eof_bytecode(pushes + OP_SWAPN + "12" + ret_top(), 21)); EXPECT_STATUS(EVMC_SUCCESS); EXPECT_OUTPUT_INT(1); - execute(pushes + OP_SWAPN + "12" + OP_DUPN + "13" + ret_top()); + execute(eof_bytecode(pushes + OP_SWAPN + "12" + OP_DUPN + "13" + ret_top(), 22)); EXPECT_STATUS(EVMC_SUCCESS); EXPECT_OUTPUT_INT(20); - - execute(pushes + OP_SWAPN + "13" + ret_top()); - EXPECT_STATUS(EVMC_STACK_UNDERFLOW); } TEST_P(evm, dupn_full_stack) @@ -86,23 +80,20 @@ TEST_P(evm, dupn_full_stack) rev = EVMC_PRAGUE; auto full_stack_code = bytecode{}; - for (uint64_t i = 1023; i >= 1; --i) + for (uint64_t i = 1022; i >= 1; --i) full_stack_code += push(i); - execute(full_stack_code + OP_POP + OP_DUPN + "00" + ret_top()); + execute(eof_bytecode(full_stack_code + OP_POP + OP_DUPN + "00" + ret_top(), 1023)); EXPECT_STATUS(EVMC_SUCCESS); EXPECT_OUTPUT_INT(2); - execute(full_stack_code + OP_POP + OP_DUPN + "ff" + ret_top()); + execute(eof_bytecode(full_stack_code + OP_POP + OP_DUPN + "ff" + ret_top(), 1023)); EXPECT_STATUS(EVMC_SUCCESS); EXPECT_OUTPUT_INT(257); - execute(full_stack_code + OP_POP + OP_DUPN + "fe" + ret_top()); + execute(eof_bytecode(full_stack_code + OP_POP + OP_DUPN + "fe" + ret_top(), 1023)); EXPECT_STATUS(EVMC_SUCCESS); EXPECT_OUTPUT_INT(256); - - execute(full_stack_code + OP_DUPN + "fe" + OP_DUPN + "ff"); - EXPECT_STATUS(EVMC_STACK_OVERFLOW); } TEST_P(evm, swapn_full_stack) @@ -113,22 +104,22 @@ TEST_P(evm, swapn_full_stack) rev = EVMC_PRAGUE; auto full_stack_code = bytecode{}; - for (uint64_t i = 1024; i >= 1; --i) + for (uint64_t i = 1023; i >= 1; --i) full_stack_code += push(i); - execute(full_stack_code + OP_POP + OP_SWAPN + "00" + ret_top()); + execute(eof_bytecode(full_stack_code + OP_POP + OP_SWAPN + "00" + ret_top(), 1023)); EXPECT_STATUS(EVMC_SUCCESS); EXPECT_OUTPUT_INT(3); - execute(full_stack_code + OP_POP + OP_SWAPN + "ff" + ret_top()); + execute(eof_bytecode(full_stack_code + OP_POP + OP_SWAPN + "ff" + ret_top(), 1023)); EXPECT_STATUS(EVMC_SUCCESS); EXPECT_OUTPUT_INT(255 + 3); - execute(full_stack_code + OP_POP + OP_SWAPN + "fe" + ret_top()); + execute(eof_bytecode(full_stack_code + OP_POP + OP_SWAPN + "fe" + ret_top(), 1023)); EXPECT_STATUS(EVMC_SUCCESS); EXPECT_OUTPUT_INT(254 + 3); - execute(full_stack_code + OP_SWAPN + "ff" + OP_SWAPN + "00" + OP_RETURN); + execute(eof_bytecode(full_stack_code + OP_SWAPN + "ff" + OP_SWAPN + "00" + OP_RETURN, 1023)); EXPECT_STATUS(EVMC_SUCCESS); EXPECT_EQ(result.output_size, 255 + 2); } @@ -144,19 +135,19 @@ TEST_P(evm, dupn_dup_consistency) for (uint64_t i = 32; i >= 1; --i) pushes += push(i); - execute(pushes + OP_DUP1 + ret_top()); + execute(eof_bytecode(pushes + OP_DUP1 + ret_top(), 34)); EXPECT_STATUS(EVMC_SUCCESS); EXPECT_OUTPUT_INT(1); - execute(pushes + OP_DUPN + "00" + ret_top()); + execute(eof_bytecode(pushes + OP_DUPN + "00" + ret_top(), 34)); EXPECT_STATUS(EVMC_SUCCESS); EXPECT_OUTPUT_INT(1); - execute(pushes + OP_DUP16 + ret_top()); + execute(eof_bytecode(pushes + OP_DUP16 + ret_top(), 34)); EXPECT_STATUS(EVMC_SUCCESS); EXPECT_OUTPUT_INT(16); - execute(pushes + OP_DUPN + "0f" + ret_top()); + execute(eof_bytecode(pushes + OP_DUPN + "0f" + ret_top(), 34)); EXPECT_STATUS(EVMC_SUCCESS); EXPECT_OUTPUT_INT(16); } @@ -172,19 +163,30 @@ TEST_P(evm, swapn_swap_consistency) for (uint64_t i = 32; i >= 1; --i) pushes += push(i); - execute(pushes + OP_SWAP1 + ret_top()); + execute(eof_bytecode(pushes + OP_SWAP1 + ret_top(), 33)); EXPECT_STATUS(EVMC_SUCCESS); EXPECT_OUTPUT_INT(2); - execute(pushes + OP_SWAPN + "00" + ret_top()); + execute(eof_bytecode(pushes + OP_SWAPN + "00" + ret_top(), 33)); EXPECT_STATUS(EVMC_SUCCESS); EXPECT_OUTPUT_INT(2); - execute(pushes + OP_SWAP16 + ret_top()); + execute(eof_bytecode(pushes + OP_SWAP16 + ret_top(), 33)); EXPECT_STATUS(EVMC_SUCCESS); EXPECT_OUTPUT_INT(17); - execute(pushes + OP_SWAPN + "0f" + ret_top()); + execute(eof_bytecode(pushes + OP_SWAPN + "0f" + ret_top(), 33)); EXPECT_STATUS(EVMC_SUCCESS); EXPECT_OUTPUT_INT(17); } + +TEST_P(evm, dupn_swapn_undefined_in_legacy) +{ + rev = EVMC_PRAGUE; + + execute(push(1) + push(2) + OP_SWAPN + "00"); + EXPECT_STATUS(EVMC_UNDEFINED_INSTRUCTION); + + execute(push(1) + OP_DUPN + "00"); + EXPECT_STATUS(EVMC_UNDEFINED_INSTRUCTION); +}