diff --git a/lib/evmone/advanced_analysis.hpp b/lib/evmone/advanced_analysis.hpp index 252a158981..53fc7a5b2c 100644 --- a/lib/evmone/advanced_analysis.hpp +++ b/lib/evmone/advanced_analysis.hpp @@ -80,6 +80,7 @@ class Stack /// The execution state specialized for the Advanced interpreter. struct AdvancedExecutionState : ExecutionState { + int64_t gas_left = 0; Stack stack; /// The gas cost of the current block. @@ -93,6 +94,7 @@ struct AdvancedExecutionState : ExecutionState const evmc_host_interface& host_interface, evmc_host_context* host_ctx, bytes_view _code) noexcept : ExecutionState{message, revision, host_interface, host_ctx, _code}, + gas_left{message.gas}, stack{stack_space.bottom()} {} @@ -109,6 +111,7 @@ struct AdvancedExecutionState : ExecutionState bytes_view _code) noexcept { ExecutionState::reset(message, revision, host_interface, host_ctx, _code); + gas_left = message.gas; stack.reset(stack_space.bottom()); analysis.advanced = nullptr; // For consistency with previous behavior. current_block_cost = 0; diff --git a/lib/evmone/advanced_instructions.cpp b/lib/evmone/advanced_instructions.cpp index 936411fee4..282df04535 100644 --- a/lib/evmone/advanced_instructions.cpp +++ b/lib/evmone/advanced_instructions.cpp @@ -36,17 +36,30 @@ inline evmc_status_code impl(AdvancedExecutionState& state) noexcept return status; } -template > -inline StopToken impl(AdvancedExecutionState& /*state*/) noexcept +template > +inline evmc_status_code impl(AdvancedExecutionState& state) noexcept +{ + const auto status = CoreFn(state.stack.top_item, state.gas_left, state); + state.stack.top_item += instr::traits[Op].stack_height_change; + return status; +} + +template > +inline evmc_status_code impl(AdvancedExecutionState& state) noexcept { - return CoreFn(); + const auto status = CoreFn(state.stack.top_item, state.gas_left, state); + state.gas_left = status.gas_left; + state.stack.top_item += instr::traits[Op].stack_height_change; + return status.status; } -template > -inline StopToken impl(AdvancedExecutionState& state) noexcept +template > +inline TermResult impl(AdvancedExecutionState& state) noexcept { // Stack height adjustment may be omitted. - return CoreFn(state.stack.top_item, state); + return CoreFn(state.stack.top_item, state.gas_left, state); } template +template const Instruction* op(const Instruction* /*instr*/, AdvancedExecutionState& state) noexcept { - return state.exit(InstrFn(state).status); + const auto result = InstrFn(state); + state.gas_left = result.gas_left; + return state.exit(result.status); } const Instruction* op_sstore(const Instruction* instr, AdvancedExecutionState& state) noexcept diff --git a/lib/evmone/baseline.cpp b/lib/evmone/baseline.cpp index 5684b19364..5fcaa001a4 100644 --- a/lib/evmone/baseline.cpp +++ b/lib/evmone/baseline.cpp @@ -159,34 +159,29 @@ struct Position /// Helpers for invoking instruction implementations of different signatures. /// @{ -[[release_inline]] inline code_iterator invoke( - void (*instr_fn)(StackTop) noexcept, Position pos, ExecutionState& /*state*/) noexcept +[[release_inline]] inline code_iterator invoke(void (*instr_fn)(StackTop) noexcept, Position pos, + int64_t& /*gas*/, ExecutionState& /*state*/) noexcept { instr_fn(pos.stack_top); return pos.code_it + 1; } [[release_inline]] inline code_iterator invoke( - StopToken (*instr_fn)() noexcept, Position /*pos*/, ExecutionState& state) noexcept -{ - state.status = instr_fn().status; - return nullptr; -} - -[[release_inline]] inline code_iterator invoke( - evmc_status_code (*instr_fn)(StackTop, ExecutionState&) noexcept, Position pos, + Result (*instr_fn)(StackTop, int64_t, ExecutionState&) noexcept, Position pos, int64_t& gas, ExecutionState& state) noexcept { - if (const auto status = instr_fn(pos.stack_top, state); status != EVMC_SUCCESS) + const auto o = instr_fn(pos.stack_top, gas, state); + gas = o.gas_left; + if (o.status != EVMC_SUCCESS) { - state.status = status; + state.status = o.status; return nullptr; } return pos.code_it + 1; } [[release_inline]] inline code_iterator invoke(void (*instr_fn)(StackTop, ExecutionState&) noexcept, - Position pos, ExecutionState& state) noexcept + Position pos, int64_t& /*gas*/, ExecutionState& state) noexcept { instr_fn(pos.stack_top, state); return pos.code_it + 1; @@ -194,16 +189,18 @@ struct Position [[release_inline]] inline code_iterator invoke( code_iterator (*instr_fn)(StackTop, ExecutionState&, code_iterator) noexcept, Position pos, - ExecutionState& state) noexcept + int64_t& /*gas*/, ExecutionState& state) noexcept { return instr_fn(pos.stack_top, state, pos.code_it); } [[release_inline]] inline code_iterator invoke( - StopToken (*instr_fn)(StackTop, ExecutionState&) noexcept, Position pos, + TermResult (*instr_fn)(StackTop, int64_t, ExecutionState&) noexcept, Position pos, int64_t& gas, ExecutionState& state) noexcept { - state.status = instr_fn(pos.stack_top, state).status; + const auto result = instr_fn(pos.stack_top, gas, state); + gas = result.gas_left; + state.status = result.status; return nullptr; } /// @} @@ -211,24 +208,23 @@ struct Position /// A helper to invoke the instruction implementation of the given opcode Op. template [[release_inline]] inline Position invoke(const CostTable& cost_table, const uint256* stack_bottom, - Position pos, ExecutionState& state) noexcept + Position pos, int64_t& gas, ExecutionState& state) noexcept { - if (const auto status = - check_requirements(cost_table, state.gas_left, pos.stack_top, stack_bottom); + if (const auto status = check_requirements(cost_table, gas, pos.stack_top, stack_bottom); status != EVMC_SUCCESS) { state.status = status; return {nullptr, pos.stack_top}; } - const auto new_pos = invoke(instr::core::impl, pos, state); + const auto new_pos = invoke(instr::core::impl, pos, gas, state); const auto new_stack_top = pos.stack_top + instr::traits[Op].stack_height_change; return {new_pos, new_stack_top}; } template -void dispatch(const CostTable& cost_table, ExecutionState& state, const uint8_t* code, - Tracer* tracer = nullptr) noexcept +int64_t dispatch(const CostTable& cost_table, ExecutionState& state, int64_t gas, + const uint8_t* code, Tracer* tracer = nullptr) noexcept { const auto stack_bottom = state.stack_space.bottom(); @@ -244,27 +240,27 @@ void dispatch(const CostTable& cost_table, ExecutionState& state, const uint8_t* if (offset < state.original_code.size()) // Skip STOP from code padding. { tracer->notify_instruction_start( - offset, position.stack_top, stack_height, state.gas_left, state); + offset, position.stack_top, stack_height, gas, state); } } const auto op = *position.code_it; switch (op) { -#define ON_OPCODE(OPCODE) \ - case OPCODE: \ - ASM_COMMENT(OPCODE); \ - if (const auto next = invoke(cost_table, stack_bottom, position, state); \ - next.code_it == nullptr) \ - { \ - return; \ - } \ - else \ - { \ - /* Update current position only when no error, \ - this improves compiler optimization. */ \ - position = next; \ - } \ +#define ON_OPCODE(OPCODE) \ + case OPCODE: \ + ASM_COMMENT(OPCODE); \ + if (const auto next = invoke(cost_table, stack_bottom, position, gas, state); \ + next.code_it == nullptr) \ + { \ + return gas; \ + } \ + else \ + { \ + /* Update current position only when no error, \ + this improves compiler optimization. */ \ + position = next; \ + } \ break; MAP_OPCODES @@ -272,14 +268,15 @@ void dispatch(const CostTable& cost_table, ExecutionState& state, const uint8_t* default: state.status = EVMC_UNDEFINED_INSTRUCTION; - return; + return gas; } } + INTX_UNREACHABLE(); } #if EVMONE_CGOTO_SUPPORTED -void dispatch_cgoto( - const CostTable& cost_table, ExecutionState& state, const uint8_t* code) noexcept +int64_t dispatch_cgoto( + const CostTable& cost_table, ExecutionState& state, int64_t gas, const uint8_t* code) noexcept { #pragma GCC diagnostic ignored "-Wpedantic" @@ -301,19 +298,19 @@ void dispatch_cgoto( goto* cgoto_table[*position.code_it]; -#define ON_OPCODE(OPCODE) \ - TARGET_##OPCODE : ASM_COMMENT(OPCODE); \ - if (const auto next = invoke(cost_table, stack_bottom, position, state); \ - next.code_it == nullptr) \ - { \ - return; \ - } \ - else \ - { \ - /* Update current position only when no error, \ - this improves compiler optimization. */ \ - position = next; \ - } \ +#define ON_OPCODE(OPCODE) \ + TARGET_##OPCODE : ASM_COMMENT(OPCODE); \ + if (const auto next = invoke(cost_table, stack_bottom, position, gas, state); \ + next.code_it == nullptr) \ + { \ + return gas; \ + } \ + else \ + { \ + /* Update current position only when no error, \ + this improves compiler optimization. */ \ + position = next; \ + } \ goto* cgoto_table[*position.code_it]; MAP_OPCODES @@ -321,11 +318,13 @@ void dispatch_cgoto( TARGET_OP_UNDEFINED: state.status = EVMC_UNDEFINED_INSTRUCTION; + return gas; } #endif } // namespace -evmc_result execute(const VM& vm, ExecutionState& state, const CodeAnalysis& analysis) noexcept +evmc_result execute( + const VM& vm, int64_t gas, ExecutionState& state, const CodeAnalysis& analysis) noexcept { state.analysis.baseline = &analysis; // Assign code analysis for instruction implementations. @@ -337,20 +336,19 @@ evmc_result execute(const VM& vm, ExecutionState& state, const CodeAnalysis& ana if (INTX_UNLIKELY(tracer != nullptr)) { tracer->notify_execution_start(state.rev, *state.msg, analysis.executable_code); - dispatch(cost_table, state, code.data(), tracer); + gas = dispatch(cost_table, state, gas, code.data(), tracer); } else { #if EVMONE_CGOTO_SUPPORTED if (vm.cgoto) - dispatch_cgoto(cost_table, state, code.data()); + gas = dispatch_cgoto(cost_table, state, gas, code.data()); else #endif - dispatch(cost_table, state, code.data()); + gas = dispatch(cost_table, state, gas, code.data()); } - const auto gas_left = - (state.status == EVMC_SUCCESS || state.status == EVMC_REVERT) ? state.gas_left : 0; + const auto gas_left = (state.status == EVMC_SUCCESS || state.status == EVMC_REVERT) ? gas : 0; const auto gas_refund = (state.status == EVMC_SUCCESS) ? state.gas_refund : 0; assert(state.output_size != 0 || state.output_offset == 0); @@ -370,6 +368,6 @@ evmc_result execute(evmc_vm* c_vm, const evmc_host_interface* host, evmc_host_co const auto jumpdest_map = analyze(rev, {code, code_size}); auto state = std::make_unique(*msg, rev, *host, ctx, bytes_view{code, code_size}); - return execute(*vm, *state, jumpdest_map); + return execute(*vm, msg->gas, *state, jumpdest_map); } } // namespace evmone::baseline diff --git a/lib/evmone/baseline.hpp b/lib/evmone/baseline.hpp index 718e24d188..7456f53dce 100644 --- a/lib/evmone/baseline.hpp +++ b/lib/evmone/baseline.hpp @@ -58,7 +58,7 @@ evmc_result execute(evmc_vm* vm, const evmc_host_interface* host, evmc_host_cont /// Executes in Baseline interpreter on the given external and initialized state. EVMC_EXPORT evmc_result execute( - const VM&, ExecutionState& state, const CodeAnalysis& analysis) noexcept; + const VM&, int64_t gas_limit, ExecutionState& state, const CodeAnalysis& analysis) noexcept; } // namespace baseline } // namespace evmone diff --git a/lib/evmone/execution_state.hpp b/lib/evmone/execution_state.hpp index 8aa54353ca..f8478516c7 100644 --- a/lib/evmone/execution_state.hpp +++ b/lib/evmone/execution_state.hpp @@ -125,7 +125,6 @@ class Memory class ExecutionState { public: - int64_t gas_left = 0; int64_t gas_refund = 0; Memory memory; const evmc_message* msg = nullptr; @@ -166,11 +165,7 @@ class ExecutionState ExecutionState(const evmc_message& message, evmc_revision revision, const evmc_host_interface& host_interface, evmc_host_context* host_ctx, bytes_view _code) noexcept - : gas_left{message.gas}, - msg{&message}, - host{host_interface, host_ctx}, - rev{revision}, - original_code{_code} + : msg{&message}, host{host_interface, host_ctx}, rev{revision}, original_code{_code} {} /// Resets the contents of the ExecutionState so that it could be reused. @@ -178,7 +173,6 @@ class ExecutionState const evmc_host_interface& host_interface, evmc_host_context* host_ctx, bytes_view _code) noexcept { - gas_left = message.gas; gas_refund = 0; memory.clear(); msg = &message; diff --git a/lib/evmone/instructions.hpp b/lib/evmone/instructions.hpp index 8f3ce87d5b..0224c3ae79 100644 --- a/lib/evmone/instructions.hpp +++ b/lib/evmone/instructions.hpp @@ -39,13 +39,18 @@ class StackTop void push(const uint256& value) noexcept { *++m_top = value; } }; -/// A wrapper for evmc_status_code to indicate that an instruction -/// unconditionally terminates execution. -struct StopToken + +/// Instruction execution result. +struct Result { - const evmc_status_code status; ///< The status code execution terminates with. + evmc_status_code status; + int64_t gas_left; }; +/// Instruction result indicating that execution terminates unconditionally. +struct TermResult : Result +{}; + constexpr auto max_buffer_size = std::numeric_limits::max(); /// The size of the EVM 256-bit word. @@ -132,9 +137,10 @@ inline constexpr auto pop = noop; inline constexpr auto jumpdest = noop; template -inline StopToken stop_impl() noexcept +inline TermResult stop_impl( + StackTop /*stack*/, int64_t gas_left, ExecutionState& /*state*/) noexcept { - return {Status}; + return {Status, gas_left}; } inline constexpr auto stop = stop_impl; inline constexpr auto invalid = stop_impl; @@ -194,7 +200,7 @@ inline void mulmod(StackTop stack) noexcept m = m != 0 ? intx::mulmod(x, y, m) : 0; } -inline evmc_status_code exp(StackTop stack, ExecutionState& state) noexcept +inline Result exp(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { const auto& base = stack.pop(); auto& exponent = stack.top(); @@ -203,11 +209,11 @@ inline evmc_status_code exp(StackTop stack, ExecutionState& state) noexcept static_cast(intx::count_significant_bytes(exponent)); const auto exponent_cost = state.rev >= EVMC_SPURIOUS_DRAGON ? 50 : 10; const auto additional_cost = exponent_significant_bytes * exponent_cost; - if ((state.gas_left -= additional_cost) < 0) - return EVMC_OUT_OF_GAS; + if ((gas_left -= additional_cost) < 0) + return {EVMC_OUT_OF_GAS, gas_left}; exponent = intx::exp(base, exponent); - return EVMC_SUCCESS; + return {EVMC_SUCCESS, gas_left}; } inline void signextend(StackTop stack) noexcept @@ -334,24 +340,24 @@ inline void sar(StackTop stack) noexcept x = (x >> y) | (sign_mask << mask_shift); } -inline evmc_status_code keccak256(StackTop stack, ExecutionState& state) noexcept +inline Result keccak256(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { const auto& index = stack.pop(); auto& size = stack.top(); - if (!check_memory(state.gas_left, state.memory, index, size)) - return EVMC_OUT_OF_GAS; + if (!check_memory(gas_left, state.memory, index, size)) + return {EVMC_OUT_OF_GAS, gas_left}; const auto i = static_cast(index); const auto s = static_cast(size); const auto w = num_words(s); const auto cost = w * 6; - if ((state.gas_left -= cost) < 0) - return EVMC_OUT_OF_GAS; + if ((gas_left -= cost) < 0) + return {EVMC_OUT_OF_GAS, gas_left}; auto data = s != 0 ? &state.memory[i] : nullptr; size = intx::be::load(ethash::keccak256(data, s)); - return EVMC_SUCCESS; + return {EVMC_SUCCESS, gas_left}; } @@ -360,19 +366,19 @@ inline void address(StackTop stack, ExecutionState& state) noexcept stack.push(intx::be::load(state.msg->recipient)); } -inline evmc_status_code balance(StackTop stack, ExecutionState& state) noexcept +inline Result balance(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { auto& x = stack.top(); const auto addr = intx::be::trunc(x); if (state.rev >= EVMC_BERLIN && state.host.access_account(addr) == EVMC_ACCESS_COLD) { - if ((state.gas_left -= instr::additional_cold_account_access_cost) < 0) - return EVMC_OUT_OF_GAS; + if ((gas_left -= instr::additional_cold_account_access_cost) < 0) + return {EVMC_OUT_OF_GAS, gas_left}; } x = intx::be::load(state.host.get_balance(addr)); - return EVMC_SUCCESS; + return {EVMC_SUCCESS, gas_left}; } inline void origin(StackTop stack, ExecutionState& state) noexcept @@ -414,14 +420,14 @@ inline void calldatasize(StackTop stack, ExecutionState& state) noexcept stack.push(state.msg->input_size); } -inline evmc_status_code calldatacopy(StackTop stack, ExecutionState& state) noexcept +inline Result calldatacopy(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { const auto& mem_index = stack.pop(); const auto& input_index = stack.pop(); const auto& size = stack.pop(); - if (!check_memory(state.gas_left, state.memory, mem_index, size)) - return EVMC_OUT_OF_GAS; + if (!check_memory(gas_left, state.memory, mem_index, size)) + return {EVMC_OUT_OF_GAS, gas_left}; auto dst = static_cast(mem_index); auto src = state.msg->input_size < input_index ? state.msg->input_size : @@ -430,8 +436,8 @@ inline evmc_status_code calldatacopy(StackTop stack, ExecutionState& state) noex auto copy_size = std::min(s, state.msg->input_size - src); const auto copy_cost = num_words(s) * 3; - if ((state.gas_left -= copy_cost) < 0) - return EVMC_OUT_OF_GAS; + if ((gas_left -= copy_cost) < 0) + return {EVMC_OUT_OF_GAS, gas_left}; if (copy_size > 0) std::memcpy(&state.memory[dst], &state.msg->input_data[src], copy_size); @@ -439,7 +445,7 @@ inline evmc_status_code calldatacopy(StackTop stack, ExecutionState& state) noex if (s - copy_size > 0) std::memset(&state.memory[dst + copy_size], 0, s - copy_size); - return EVMC_SUCCESS; + return {EVMC_SUCCESS, gas_left}; } inline void codesize(StackTop stack, ExecutionState& state) noexcept @@ -447,7 +453,7 @@ inline void codesize(StackTop stack, ExecutionState& state) noexcept stack.push(state.original_code.size()); } -inline evmc_status_code codecopy(StackTop stack, ExecutionState& state) noexcept +inline Result codecopy(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { // TODO: Similar to calldatacopy(). @@ -455,8 +461,8 @@ inline evmc_status_code codecopy(StackTop stack, ExecutionState& state) noexcept const auto& input_index = stack.pop(); const auto& size = stack.pop(); - if (!check_memory(state.gas_left, state.memory, mem_index, size)) - return EVMC_OUT_OF_GAS; + if (!check_memory(gas_left, state.memory, mem_index, size)) + return {EVMC_OUT_OF_GAS, gas_left}; const auto code_size = state.original_code.size(); const auto dst = static_cast(mem_index); @@ -465,8 +471,8 @@ inline evmc_status_code codecopy(StackTop stack, ExecutionState& state) noexcept const auto copy_size = std::min(s, code_size - src); const auto copy_cost = num_words(s) * 3; - if ((state.gas_left -= copy_cost) < 0) - return EVMC_OUT_OF_GAS; + if ((gas_left -= copy_cost) < 0) + return {EVMC_OUT_OF_GAS, gas_left}; // TODO: Add unit tests for each combination of conditions. if (copy_size > 0) @@ -475,7 +481,7 @@ inline evmc_status_code codecopy(StackTop stack, ExecutionState& state) noexcept if (s - copy_size > 0) std::memset(&state.memory[dst + copy_size], 0, s - copy_size); - return EVMC_SUCCESS; + return {EVMC_SUCCESS, gas_left}; } @@ -489,40 +495,40 @@ inline void basefee(StackTop stack, ExecutionState& state) noexcept stack.push(intx::be::load(state.get_tx_context().block_base_fee)); } -inline evmc_status_code extcodesize(StackTop stack, ExecutionState& state) noexcept +inline Result extcodesize(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { auto& x = stack.top(); const auto addr = intx::be::trunc(x); if (state.rev >= EVMC_BERLIN && state.host.access_account(addr) == EVMC_ACCESS_COLD) { - if ((state.gas_left -= instr::additional_cold_account_access_cost) < 0) - return EVMC_OUT_OF_GAS; + if ((gas_left -= instr::additional_cold_account_access_cost) < 0) + return {EVMC_OUT_OF_GAS, gas_left}; } x = state.host.get_code_size(addr); - return EVMC_SUCCESS; + return {EVMC_SUCCESS, gas_left}; } -inline evmc_status_code extcodecopy(StackTop stack, ExecutionState& state) noexcept +inline Result extcodecopy(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { const auto addr = intx::be::trunc(stack.pop()); const auto& mem_index = stack.pop(); const auto& input_index = stack.pop(); const auto& size = stack.pop(); - if (!check_memory(state.gas_left, state.memory, mem_index, size)) - return EVMC_OUT_OF_GAS; + if (!check_memory(gas_left, state.memory, mem_index, size)) + return {EVMC_OUT_OF_GAS, gas_left}; const auto s = static_cast(size); const auto copy_cost = num_words(s) * 3; - if ((state.gas_left -= copy_cost) < 0) - return EVMC_OUT_OF_GAS; + if ((gas_left -= copy_cost) < 0) + return {EVMC_OUT_OF_GAS, gas_left}; if (state.rev >= EVMC_BERLIN && state.host.access_account(addr) == EVMC_ACCESS_COLD) { - if ((state.gas_left -= instr::additional_cold_account_access_cost) < 0) - return EVMC_OUT_OF_GAS; + if ((gas_left -= instr::additional_cold_account_access_cost) < 0) + return {EVMC_OUT_OF_GAS, gas_left}; } if (s > 0) @@ -535,7 +541,7 @@ inline evmc_status_code extcodecopy(StackTop stack, ExecutionState& state) noexc std::memset(&state.memory[dst + num_bytes_copied], 0, num_bytes_to_clear); } - return EVMC_SUCCESS; + return {EVMC_SUCCESS, gas_left}; } inline void returndatasize(StackTop stack, ExecutionState& state) noexcept @@ -543,48 +549,48 @@ inline void returndatasize(StackTop stack, ExecutionState& state) noexcept stack.push(state.return_data.size()); } -inline evmc_status_code returndatacopy(StackTop stack, ExecutionState& state) noexcept +inline Result returndatacopy(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { const auto& mem_index = stack.pop(); const auto& input_index = stack.pop(); const auto& size = stack.pop(); - if (!check_memory(state.gas_left, state.memory, mem_index, size)) - return EVMC_OUT_OF_GAS; + if (!check_memory(gas_left, state.memory, mem_index, size)) + return {EVMC_OUT_OF_GAS, gas_left}; auto dst = static_cast(mem_index); auto s = static_cast(size); if (state.return_data.size() < input_index) - return EVMC_INVALID_MEMORY_ACCESS; + return {EVMC_INVALID_MEMORY_ACCESS, gas_left}; auto src = static_cast(input_index); if (src + s > state.return_data.size()) - return EVMC_INVALID_MEMORY_ACCESS; + return {EVMC_INVALID_MEMORY_ACCESS, gas_left}; const auto copy_cost = num_words(s) * 3; - if ((state.gas_left -= copy_cost) < 0) - return EVMC_OUT_OF_GAS; + if ((gas_left -= copy_cost) < 0) + return {EVMC_OUT_OF_GAS, gas_left}; if (s > 0) std::memcpy(&state.memory[dst], &state.return_data[src], s); - return EVMC_SUCCESS; + return {EVMC_SUCCESS, gas_left}; } -inline evmc_status_code extcodehash(StackTop stack, ExecutionState& state) noexcept +inline Result extcodehash(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { auto& x = stack.top(); const auto addr = intx::be::trunc(x); if (state.rev >= EVMC_BERLIN && state.host.access_account(addr) == EVMC_ACCESS_COLD) { - if ((state.gas_left -= instr::additional_cold_account_access_cost) < 0) - return EVMC_OUT_OF_GAS; + if ((gas_left -= instr::additional_cold_account_access_cost) < 0) + return {EVMC_OUT_OF_GAS, gas_left}; } x = intx::be::load(state.host.get_code_hash(addr)); - return EVMC_SUCCESS; + return {EVMC_SUCCESS, gas_left}; } @@ -638,44 +644,44 @@ inline void selfbalance(StackTop stack, ExecutionState& state) noexcept stack.push(intx::be::load(state.host.get_balance(state.msg->recipient))); } -inline evmc_status_code mload(StackTop stack, ExecutionState& state) noexcept +inline Result mload(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { auto& index = stack.top(); - if (!check_memory(state.gas_left, state.memory, index, 32)) - return EVMC_OUT_OF_GAS; + if (!check_memory(gas_left, state.memory, index, 32)) + return {EVMC_OUT_OF_GAS, gas_left}; index = intx::be::unsafe::load(&state.memory[static_cast(index)]); - return EVMC_SUCCESS; + return {EVMC_SUCCESS, gas_left}; } -inline evmc_status_code mstore(StackTop stack, ExecutionState& state) noexcept +inline Result mstore(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { const auto& index = stack.pop(); const auto& value = stack.pop(); - if (!check_memory(state.gas_left, state.memory, index, 32)) - return EVMC_OUT_OF_GAS; + if (!check_memory(gas_left, state.memory, index, 32)) + return {EVMC_OUT_OF_GAS, gas_left}; intx::be::unsafe::store(&state.memory[static_cast(index)], value); - return EVMC_SUCCESS; + return {EVMC_SUCCESS, gas_left}; } -inline evmc_status_code mstore8(StackTop stack, ExecutionState& state) noexcept +inline Result mstore8(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { const auto& index = stack.pop(); const auto& value = stack.pop(); - if (!check_memory(state.gas_left, state.memory, index, 1)) - return EVMC_OUT_OF_GAS; + if (!check_memory(gas_left, state.memory, index, 1)) + return {EVMC_OUT_OF_GAS, gas_left}; state.memory[static_cast(index)] = static_cast(value); - return EVMC_SUCCESS; + return {EVMC_SUCCESS, gas_left}; } -evmc_status_code sload(StackTop stack, ExecutionState& state) noexcept; +Result sload(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept; -evmc_status_code sstore(StackTop stack, ExecutionState& state) noexcept; +Result sstore(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept; /// Internal jump implementation for JUMP/JUMPI instructions. inline code_iterator jump_impl(ExecutionState& state, const uint256& dst) noexcept @@ -749,9 +755,10 @@ inline void msize(StackTop stack, ExecutionState& state) noexcept stack.push(state.memory.size()); } -inline void gas(StackTop stack, ExecutionState& state) noexcept +inline Result gas(StackTop stack, int64_t gas_left, ExecutionState& /*state*/) noexcept { - stack.push(state.gas_left); + stack.push(gas_left); + return {EVMC_SUCCESS, gas_left}; } inline void push0(StackTop stack) noexcept @@ -893,25 +900,25 @@ inline code_iterator swapn(StackTop stack, ExecutionState& state, code_iterator } template -inline evmc_status_code log(StackTop stack, ExecutionState& state) noexcept +inline Result log(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { static_assert(NumTopics <= 4); if (state.in_static_mode()) - return EVMC_STATIC_MODE_VIOLATION; + return {EVMC_STATIC_MODE_VIOLATION, 0}; const auto& offset = stack.pop(); const auto& size = stack.pop(); - if (!check_memory(state.gas_left, state.memory, offset, size)) - return EVMC_OUT_OF_GAS; + if (!check_memory(gas_left, state.memory, offset, size)) + return {EVMC_OUT_OF_GAS, gas_left}; const auto o = static_cast(offset); const auto s = static_cast(size); const auto cost = int64_t(s) * 8; - if ((state.gas_left -= cost) < 0) - return EVMC_OUT_OF_GAS; + if ((gas_left -= cost) < 0) + return {EVMC_OUT_OF_GAS, gas_left}; std::array topics; // NOLINT(cppcoreguidelines-pro-type-member-init) for (auto& topic : topics) @@ -919,19 +926,19 @@ inline evmc_status_code log(StackTop stack, ExecutionState& state) noexcept const auto data = s != 0 ? &state.memory[o] : nullptr; state.host.emit_log(state.msg->recipient, data, s, topics.data(), NumTopics); - return EVMC_SUCCESS; + return {EVMC_SUCCESS, gas_left}; } template -evmc_status_code call_impl(StackTop stack, ExecutionState& state) noexcept; +Result call_impl(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept; inline constexpr auto call = call_impl; inline constexpr auto callcode = call_impl; inline constexpr auto delegatecall = call_impl; inline constexpr auto staticcall = call_impl; template -evmc_status_code create_impl(StackTop stack, ExecutionState& state) noexcept; +Result create_impl(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept; inline constexpr auto create = create_impl; inline constexpr auto create2 = create_impl; @@ -967,33 +974,33 @@ inline code_iterator retf(StackTop /*stack*/, ExecutionState& state, code_iterat } template -inline StopToken return_impl(StackTop stack, ExecutionState& state) noexcept +inline TermResult return_impl(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { const auto& offset = stack[0]; const auto& size = stack[1]; - if (!check_memory(state.gas_left, state.memory, offset, size)) - return {EVMC_OUT_OF_GAS}; + if (!check_memory(gas_left, state.memory, offset, size)) + return {EVMC_OUT_OF_GAS, gas_left}; state.output_size = static_cast(size); if (state.output_size != 0) state.output_offset = static_cast(offset); - return {StatusCode}; + return {StatusCode, gas_left}; } inline constexpr auto return_ = return_impl; inline constexpr auto revert = return_impl; -inline StopToken selfdestruct(StackTop stack, ExecutionState& state) noexcept +inline TermResult selfdestruct(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { if (state.in_static_mode()) - return {EVMC_STATIC_MODE_VIOLATION}; + return {EVMC_STATIC_MODE_VIOLATION, gas_left}; const auto beneficiary = intx::be::trunc(stack[0]); if (state.rev >= EVMC_BERLIN && state.host.access_account(beneficiary) == EVMC_ACCESS_COLD) { - if ((state.gas_left -= instr::cold_account_access_cost) < 0) - return {EVMC_OUT_OF_GAS}; + if ((gas_left -= instr::cold_account_access_cost) < 0) + return {EVMC_OUT_OF_GAS, gas_left}; } if (state.rev >= EVMC_TANGERINE_WHISTLE) @@ -1004,8 +1011,8 @@ inline StopToken selfdestruct(StackTop stack, ExecutionState& state) noexcept // sending value to a non-existing account. if (!state.host.account_exists(beneficiary)) { - if ((state.gas_left -= 25000) < 0) - return {EVMC_OUT_OF_GAS}; + if ((gas_left -= 25000) < 0) + return {EVMC_OUT_OF_GAS, gas_left}; } } } @@ -1015,7 +1022,7 @@ inline StopToken selfdestruct(StackTop stack, ExecutionState& state) noexcept if (state.rev < EVMC_LONDON) state.gas_refund += 24000; } - return {EVMC_SUCCESS}; + return {EVMC_SUCCESS, gas_left}; } diff --git a/lib/evmone/instructions_calls.cpp b/lib/evmone/instructions_calls.cpp index ef94c797ba..480dc7eaa8 100644 --- a/lib/evmone/instructions_calls.cpp +++ b/lib/evmone/instructions_calls.cpp @@ -7,7 +7,7 @@ namespace evmone::instr::core { template -evmc_status_code call_impl(StackTop stack, ExecutionState& state) noexcept +Result call_impl(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { static_assert( Op == OP_CALL || Op == OP_CALLCODE || Op == OP_DELEGATECALL || Op == OP_STATICCALL); @@ -26,15 +26,15 @@ evmc_status_code call_impl(StackTop stack, ExecutionState& state) noexcept if (state.rev >= EVMC_BERLIN && state.host.access_account(dst) == EVMC_ACCESS_COLD) { - if ((state.gas_left -= instr::additional_cold_account_access_cost) < 0) - return EVMC_OUT_OF_GAS; + if ((gas_left -= instr::additional_cold_account_access_cost) < 0) + return {EVMC_OUT_OF_GAS, gas_left}; } - if (!check_memory(state.gas_left, state.memory, input_offset_u256, input_size_u256)) - return EVMC_OUT_OF_GAS; + if (!check_memory(gas_left, state.memory, input_offset_u256, input_size_u256)) + return {EVMC_OUT_OF_GAS, gas_left}; - if (!check_memory(state.gas_left, state.memory, output_offset_u256, output_size_u256)) - return EVMC_OUT_OF_GAS; + if (!check_memory(gas_left, state.memory, output_offset_u256, output_size_u256)) + return {EVMC_OUT_OF_GAS, gas_left}; const auto input_offset = static_cast(input_offset_u256); const auto input_size = static_cast(input_size_u256); @@ -65,35 +65,35 @@ evmc_status_code call_impl(StackTop stack, ExecutionState& state) noexcept if constexpr (Op == OP_CALL) { if (has_value && state.in_static_mode()) - return EVMC_STATIC_MODE_VIOLATION; + return {EVMC_STATIC_MODE_VIOLATION, gas_left}; if ((has_value || state.rev < EVMC_SPURIOUS_DRAGON) && !state.host.account_exists(dst)) cost += 25000; } - if ((state.gas_left -= cost) < 0) - return EVMC_OUT_OF_GAS; + if ((gas_left -= cost) < 0) + return {EVMC_OUT_OF_GAS, gas_left}; msg.gas = std::numeric_limits::max(); if (gas < msg.gas) msg.gas = static_cast(gas); if (state.rev >= EVMC_TANGERINE_WHISTLE) // TODO: Always true for STATICCALL. - msg.gas = std::min(msg.gas, state.gas_left - state.gas_left / 64); - else if (msg.gas > state.gas_left) - return EVMC_OUT_OF_GAS; + msg.gas = std::min(msg.gas, gas_left - gas_left / 64); + else if (msg.gas > gas_left) + return {EVMC_OUT_OF_GAS, gas_left}; if (has_value) { msg.gas += 2300; // Add stipend. - state.gas_left += 2300; + gas_left += 2300; } if (state.msg->depth >= 1024) - return EVMC_SUCCESS; // "Light" failure. + return {EVMC_SUCCESS, gas_left}; // "Light" failure. if (has_value && intx::be::load(state.host.get_balance(state.msg->recipient)) < value) - return EVMC_SUCCESS; // "Light" failure. + return {EVMC_SUCCESS, gas_left}; // "Light" failure. const auto result = state.host.call(msg); state.return_data.assign(result.output_data, result.output_size); @@ -103,25 +103,28 @@ evmc_status_code call_impl(StackTop stack, ExecutionState& state) noexcept std::memcpy(&state.memory[output_offset], result.output_data, copy_size); const auto gas_used = msg.gas - result.gas_left; - state.gas_left -= gas_used; + gas_left -= gas_used; state.gas_refund += result.gas_refund; - return EVMC_SUCCESS; + return {EVMC_SUCCESS, gas_left}; } -template evmc_status_code call_impl(StackTop stack, ExecutionState& state) noexcept; -template evmc_status_code call_impl(StackTop stack, ExecutionState& state) noexcept; -template evmc_status_code call_impl( - StackTop stack, ExecutionState& state) noexcept; -template evmc_status_code call_impl(StackTop stack, ExecutionState& state) noexcept; +template Result call_impl( + StackTop stack, int64_t gas_left, ExecutionState& state) noexcept; +template Result call_impl( + StackTop stack, int64_t gas_left, ExecutionState& state) noexcept; +template Result call_impl( + StackTop stack, int64_t gas_left, ExecutionState& state) noexcept; +template Result call_impl( + StackTop stack, int64_t gas_left, ExecutionState& state) noexcept; template -evmc_status_code create_impl(StackTop stack, ExecutionState& state) noexcept +Result create_impl(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { static_assert(Op == OP_CREATE || Op == OP_CREATE2); if (state.in_static_mode()) - return EVMC_STATIC_MODE_VIOLATION; + return {EVMC_STATIC_MODE_VIOLATION, gas_left}; const auto endowment = stack.pop(); const auto init_code_offset_u256 = stack.pop(); @@ -131,29 +134,29 @@ evmc_status_code create_impl(StackTop stack, ExecutionState& state) noexcept stack.push(0); // Assume failure. state.return_data.clear(); - if (!check_memory(state.gas_left, state.memory, init_code_offset_u256, init_code_size_u256)) - return EVMC_OUT_OF_GAS; + if (!check_memory(gas_left, state.memory, init_code_offset_u256, init_code_size_u256)) + return {EVMC_OUT_OF_GAS, gas_left}; const auto init_code_offset = static_cast(init_code_offset_u256); const auto init_code_size = static_cast(init_code_size_u256); if (state.rev >= EVMC_SHANGHAI && init_code_size > 0xC000) - return EVMC_OUT_OF_GAS; + return {EVMC_OUT_OF_GAS, gas_left}; const auto init_code_word_cost = 6 * (Op == OP_CREATE2) + 2 * (state.rev >= EVMC_SHANGHAI); const auto init_code_cost = num_words(init_code_size) * init_code_word_cost; - if ((state.gas_left -= init_code_cost) < 0) - return EVMC_OUT_OF_GAS; + if ((gas_left -= init_code_cost) < 0) + return {EVMC_OUT_OF_GAS, gas_left}; if (state.msg->depth >= 1024) - return EVMC_SUCCESS; // "Light" failure. + return {EVMC_SUCCESS, gas_left}; // "Light" failure. if (endowment != 0 && intx::be::load(state.host.get_balance(state.msg->recipient)) < endowment) - return EVMC_SUCCESS; // "Light" failure. + return {EVMC_SUCCESS, gas_left}; // "Light" failure. auto msg = evmc_message{}; - msg.gas = state.gas_left; + msg.gas = gas_left; if (state.rev >= EVMC_TANGERINE_WHISTLE) msg.gas = msg.gas - msg.gas / 64; @@ -170,16 +173,18 @@ evmc_status_code create_impl(StackTop stack, ExecutionState& state) noexcept msg.value = intx::be::store(endowment); const auto result = state.host.call(msg); - state.gas_left -= msg.gas - result.gas_left; + gas_left -= msg.gas - result.gas_left; state.gas_refund += result.gas_refund; state.return_data.assign(result.output_data, result.output_size); if (result.status_code == EVMC_SUCCESS) stack.top() = intx::be::load(result.create_address); - return EVMC_SUCCESS; + return {EVMC_SUCCESS, gas_left}; } -template evmc_status_code create_impl(StackTop stack, ExecutionState& state) noexcept; -template evmc_status_code create_impl(StackTop stack, ExecutionState& state) noexcept; +template Result create_impl( + StackTop stack, int64_t gas_left, ExecutionState& state) noexcept; +template Result create_impl( + StackTop stack, int64_t gas_left, ExecutionState& state) noexcept; } // namespace evmone::instr::core diff --git a/lib/evmone/instructions_storage.cpp b/lib/evmone/instructions_storage.cpp index 4440496f14..0d7474a847 100644 --- a/lib/evmone/instructions_storage.cpp +++ b/lib/evmone/instructions_storage.cpp @@ -91,7 +91,7 @@ constexpr auto sstore_costs = []() noexcept { }(); } // namespace -evmc_status_code sload(StackTop stack, ExecutionState& state) noexcept +Result sload(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { auto& x = stack.top(); const auto key = intx::be::store(x); @@ -103,22 +103,22 @@ evmc_status_code sload(StackTop stack, ExecutionState& state) noexcept // Here we need to apply additional cold storage access cost. constexpr auto additional_cold_sload_cost = instr::cold_sload_cost - instr::warm_storage_read_cost; - if ((state.gas_left -= additional_cold_sload_cost) < 0) - return EVMC_OUT_OF_GAS; + if ((gas_left -= additional_cold_sload_cost) < 0) + return {EVMC_OUT_OF_GAS, gas_left}; } x = intx::be::load(state.host.get_storage(state.msg->recipient, key)); - return EVMC_SUCCESS; + return {EVMC_SUCCESS, gas_left}; } -evmc_status_code sstore(StackTop stack, ExecutionState& state) noexcept +Result sstore(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { if (state.in_static_mode()) - return EVMC_STATIC_MODE_VIOLATION; + return {EVMC_STATIC_MODE_VIOLATION, gas_left}; - if (state.rev >= EVMC_ISTANBUL && state.gas_left <= 2300) - return EVMC_OUT_OF_GAS; + if (state.rev >= EVMC_ISTANBUL && gas_left <= 2300) + return {EVMC_OUT_OF_GAS, gas_left}; const auto key = intx::be::store(stack.pop()); const auto value = intx::be::store(stack.pop()); @@ -132,9 +132,9 @@ evmc_status_code sstore(StackTop stack, ExecutionState& state) noexcept const auto [gas_cost_warm, gas_refund] = sstore_costs[state.rev][status]; const auto gas_cost = gas_cost_warm + gas_cost_cold; - if ((state.gas_left -= gas_cost) < 0) - return EVMC_OUT_OF_GAS; + if ((gas_left -= gas_cost) < 0) + return {EVMC_OUT_OF_GAS, gas_left}; state.gas_refund += gas_refund; - return EVMC_SUCCESS; + return {EVMC_SUCCESS, gas_left}; } } // namespace evmone::instr::core diff --git a/test/bench/helpers.hpp b/test/bench/helpers.hpp index ed4958b4e4..f978140e2b 100644 --- a/test/bench/helpers.hpp +++ b/test/bench/helpers.hpp @@ -65,7 +65,7 @@ inline evmc::Result baseline_execute(evmc::VM& c_vm, ExecutionState& exec_state, { const auto& vm = *static_cast(c_vm.get_raw_pointer()); exec_state.reset(msg, rev, host.get_interface(), host.to_context(), code); - return evmc::Result{baseline::execute(vm, exec_state, analysis)}; + return evmc::Result{baseline::execute(vm, msg.gas, exec_state, analysis)}; } inline evmc::Result evmc_execute(evmc::VM& vm, FakeExecutionState& /*exec_state*/, diff --git a/test/unittests/execution_state_test.cpp b/test/unittests/execution_state_test.cpp index 9e5f2c99f5..4be02355a5 100644 --- a/test/unittests/execution_state_test.cpp +++ b/test/unittests/execution_state_test.cpp @@ -28,7 +28,6 @@ TEST(execution_state, construct) const evmone::ExecutionState st{ msg, EVMC_MAX_REVISION, host_interface, nullptr, {code, std::size(code)}}; - EXPECT_EQ(st.gas_left, -1); EXPECT_EQ(st.memory.size(), 0); EXPECT_EQ(st.msg, &msg); EXPECT_EQ(st.rev, EVMC_MAX_REVISION); @@ -42,7 +41,6 @@ TEST(execution_state, default_construct) { const evmone::ExecutionState st; - EXPECT_EQ(st.gas_left, 0); EXPECT_EQ(st.memory.size(), 0); EXPECT_EQ(st.msg, nullptr); EXPECT_EQ(st.rev, EVMC_FRONTIER);