From e49e1d261bea50617ed791506c817a1e7bf952a6 Mon Sep 17 00:00:00 2001 From: Sixtysixter <20945591+Sixtysixter@users.noreply.github.com> Date: Thu, 19 Sep 2024 18:10:49 +0200 Subject: [PATCH] rpcdaemon: gas cost in trace_replay* API (#2353) --- .github/workflows/rpc-integration-tests.yml | 2 +- silkworm/rpc/core/evm_trace.cpp | 118 ++++++-------------- silkworm/rpc/core/evm_trace.hpp | 9 +- 3 files changed, 34 insertions(+), 95 deletions(-) diff --git a/.github/workflows/rpc-integration-tests.yml b/.github/workflows/rpc-integration-tests.yml index 258cf2ac44..8c859fc9b7 100644 --- a/.github/workflows/rpc-integration-tests.yml +++ b/.github/workflows/rpc-integration-tests.yml @@ -27,7 +27,7 @@ jobs: - name: Checkout RPC Tests Repository & Install Requirements run: | rm -rf ${{runner.workspace}}/rpc-tests - git -c advice.detachedHead=false clone --depth 1 --branch v0.50.0 https://github.com/erigontech/rpc-tests ${{runner.workspace}}/rpc-tests + git -c advice.detachedHead=false clone --depth 1 --branch v0.51.0 https://github.com/erigontech/rpc-tests ${{runner.workspace}}/rpc-tests cd ${{runner.workspace}}/rpc-tests pip3 install -r requirements.txt diff --git a/silkworm/rpc/core/evm_trace.cpp b/silkworm/rpc/core/evm_trace.cpp index 6bc5ac2f10..368ea2dfc0 100644 --- a/silkworm/rpc/core/evm_trace.cpp +++ b/silkworm/rpc/core/evm_trace.cpp @@ -599,13 +599,13 @@ void VmTraceTracer::on_execution_start(evmc_revision rev, const evmc_message& ms index_prefix_.push(index_prefix); auto& op = vm_trace.ops[vm_trace.ops.size() - 1]; - if (op.op_code == evmc_opcode::OP_STATICCALL || op.op_code == evmc_opcode::OP_DELEGATECALL || op.op_code == evmc_opcode::OP_CALL) { - auto& op_1 = vm_trace.ops[vm_trace.ops.size() - 2]; - auto cap = op_1.trace_ex->used - msg.gas; - op.depth = msg.depth; - op.gas_cost = op.gas_cost - msg.gas; - op.call_gas_cap = cap; + + if (op.op_code == OP_CREATE || op.op_code == OP_CREATE2) { + op.gas_cost = msg.gas; + } else { + op.gas_cost = msg.gas_cost; } + op.sub = std::make_shared(); traces_stack_.emplace(*op.sub); op.sub->code = "0x" + silkworm::to_hex(code); @@ -623,34 +623,25 @@ void VmTraceTracer::on_execution_start(evmc_revision rev, const evmc_message& ms << ", index_prefix: " << index_prefix; } -void VmTraceTracer::on_instruction_start(uint32_t pc, const intx::uint256* stack_top, const int stack_height, const int64_t gas, - const evmone::ExecutionState& execution_state, const silkworm::IntraBlockState& intra_block_state) noexcept { +void VmTraceTracer::on_instruction_start(uint32_t pc, const intx::uint256* stack_top, const int /*stack_height*/, const int64_t gas, + const evmone::ExecutionState& execution_state, const silkworm::IntraBlockState& /*intra_block_state*/) noexcept { const auto op_code = execution_state.original_code[pc]; auto op_name = get_opcode_name(opcode_names_, op_code); last_opcode_ = op_code; - if (fix_call_gas_info_) { // previous opcode was a CALL - auto& trace_op = fix_call_gas_info_->trace_op_; - if (execution_state.msg->depth == fix_call_gas_info_->depth) { - if (fix_call_gas_info_->gas_cost) { - trace_op.gas_cost = fix_call_gas_info_->gas_cost + fix_call_gas_info_->code_cost; - } - } else { - trace_op.gas_cost = gas + fix_call_gas_info_->stipend + fix_call_gas_info_->code_cost; - } - - fix_call_gas_info_.reset(); - } - + int64_t used = 0; auto& vm_trace = traces_stack_.top().get(); if (!vm_trace.ops.empty()) { auto& op = vm_trace.ops[vm_trace.ops.size() - 1]; - if (op.precompiled_call_gas) { - op.gas_cost = op.gas_cost - op.precompiled_call_gas.value(); + if (op.op_code == OP_RETURN || op.op_code == OP_STOP || op.op_code == OP_REVERT) { + op.gas_cost = 0; + } else if (op.op_code == OP_CREATE || op.op_code == OP_CREATE2) { + op.gas_cost += execution_state.last_opcode_gas_cost; } else if (op.depth == execution_state.msg->depth) { - op.gas_cost = op.gas_cost - gas; + op.gas_cost = execution_state.last_opcode_gas_cost; } op.trace_ex->used = gas; + used = op.trace_ex->used; copy_memory(execution_state.memory, op.trace_ex->memory); copy_stack(op.op_code, stack_top, op.trace_ex->stack); } @@ -658,13 +649,13 @@ void VmTraceTracer::on_instruction_start(uint32_t pc, const intx::uint256* stack auto index_prefix = index_prefix_.top() + std::to_string(vm_trace.ops.size()); TraceOp trace_op; - trace_op.gas_cost = gas; + trace_op.gas_cost = metrics_[op_code].gas_cost; trace_op.idx = index_prefix; trace_op.depth = execution_state.msg->depth; trace_op.op_code = op_code; trace_op.op_name = op_name; trace_op.pc = pc; - trace_op.trace_ex = std::make_optional(); + trace_op.trace_ex = TraceEx{used}; if (op_code == OP_SELFDESTRUCT) { trace_op.sub = std::make_shared(); @@ -676,8 +667,6 @@ void VmTraceTracer::on_instruction_start(uint32_t pc, const intx::uint256* stack vm_trace.ops.push_back(trace_op); - fill_call_gas_info(vm_trace.ops.back(), execution_state, stack_top, stack_height, intra_block_state); - SILK_DEBUG << "VmTraceTracer::on_instruction_start:" << " pc: " << std::dec << pc << ", opcode: 0x" << std::hex << evmc::hex(op_code) @@ -703,11 +692,6 @@ void VmTraceTracer::on_precompiled_run(const evmc_result& result, int64_t gas, c op.sub->code = "0x"; } } - if (fix_call_gas_info_) { - fix_call_gas_info_->gas_cost += gas + fix_call_gas_info_->code_cost; - fix_call_gas_info_->code_cost = 0; - fix_call_gas_info_->precompiled = true; - } } void VmTraceTracer::on_execution_end(const evmc_result& result, const silkworm::IntraBlockState& /*intra_block_state*/) noexcept { @@ -737,7 +721,9 @@ void VmTraceTracer::on_execution_end(const evmc_result& result, const silkworm:: case evmc_status_code::EVMC_OUT_OF_GAS: // If we run out of gas, we reset trace_ex to null (no matter what the content is) as Erigon does op.trace_ex = std::nullopt; - op.gas_cost -= result.gas_left; + if (op.op_code != OP_CALLCODE) { + op.gas_cost = result.gas_cost; + } break; // We need to adjust gas used and gas cost from evmone to match evm.go values @@ -745,43 +731,32 @@ void VmTraceTracer::on_execution_end(const evmc_result& result, const silkworm:: case evmc_status_code::EVMC_STACK_OVERFLOW: case evmc_status_code::EVMC_BAD_JUMP_DESTINATION: if (op.op_code == evmc_opcode::OP_EXP) { // In Erigon the static part is 0 - op.trace_ex->used = op.gas_cost; + op.trace_ex->used = start_gas; op.gas_cost = 0; } else { /* EVM WA: EVMONE in case of this error returns always zero on gas-left */ - op.trace_ex->used = op.gas_cost - metrics_[op.op_code].gas_cost; + if (op.trace_ex->used > 0) { + op.trace_ex->used -= op.gas_cost; + } else { + op.trace_ex->used = start_gas - op.gas_cost; + } op.gas_cost = metrics_[op.op_code].gas_cost; } break; case evmc_status_code::EVMC_UNDEFINED_INSTRUCTION: case evmc_status_code::EVMC_INVALID_INSTRUCTION: - op.trace_ex->used = op.gas_cost; op.gas_cost = 0; - break; - - case evmc_status_code::EVMC_REVERT: - op.gas_cost = op.gas_cost - result.gas_left; - op.trace_ex->used = result.gas_left; + if (op.trace_ex->used == 0) { + op.trace_ex->used = start_gas; + } break; default: - op.gas_cost = op.gas_cost - result.gas_left; - op.trace_ex->used = result.gas_left; - if (fix_call_gas_info_) { - auto& trace_op = fix_call_gas_info_->trace_op_; - if (result.gas_left == 0 && !fix_call_gas_info_->precompiled) { - trace_op.gas_cost = fix_call_gas_info_->stipend + fix_call_gas_info_->gas_cost; - } else if (!fix_call_gas_info_->precompiled) { - trace_op.gas_cost = result.gas_left + fix_call_gas_info_->gas_cost + fix_call_gas_info_->code_cost; - fix_call_gas_info_->gas_cost = 0; - } else if (fix_call_gas_info_->precompiled) { - trace_op.gas_cost = fix_call_gas_info_->gas_cost; - fix_call_gas_info_->gas_cost = 0; - } else { - fix_call_gas_info_->gas_cost = 0; - } + if (op.op_code == OP_CALL || op.op_code == OP_CALLCODE || op.op_code == OP_STATICCALL || op.op_code == OP_DELEGATECALL || op.op_code == OP_CREATE || op.op_code == OP_CREATE2) { + op.gas_cost += result.gas_cost; } + op.trace_ex->used = result.gas_left; break; } @@ -807,35 +782,6 @@ void VmTraceTracer::on_pre_check_failed(const evmc_result& /*result*/, const evm vm_trace_.code = "0x" + silkworm::to_hex(ByteView{msg.input_data, msg.input_size}); } -void VmTraceTracer::fill_call_gas_info(TraceOp& trace_op, const evmone::ExecutionState& execution_state, const intx::uint256* stack_top, const int stack_height, const silkworm::IntraBlockState& intra_block_state) { - auto op_code = trace_op.op_code; - if (op_code == evmc_opcode::OP_CALL || op_code == evmc_opcode::OP_CALLCODE || op_code == evmc_opcode::OP_STATICCALL || op_code == evmc_opcode::OP_DELEGATECALL || op_code == evmc_opcode::OP_CREATE || op_code == evmc_opcode::OP_CREATE2) { - fix_call_gas_info_.emplace(FixCallGasInfo{execution_state.msg->depth, 0, metrics_[op_code].gas_cost, trace_op}); - - const auto value = stack_top[-2]; // value - if (value != 0) { - fix_call_gas_info_->gas_cost += 9000; - } - if (op_code == OP_CALL) { - if (op_code == OP_CALL && stack_height >= 7 && value != 0) { - fix_call_gas_info_->stipend = 2300; // for CALLs with value, include stipend - } - const auto call_gas = stack_top[0]; // gas - const auto dst = intx::be::trunc(stack_top[-1]); // dst - - if ((value != 0 || execution_state.rev < EVMC_SPURIOUS_DRAGON) && !intra_block_state.exists(dst)) { - fix_call_gas_info_->gas_cost += 25000; // add ACCOUNT_CREATION_COST as in instructions_calls.cpp:105 - } - SILK_DEBUG << "DebugTracer::evaluate_call_fixes:" - << " call_gas: " << call_gas - << " dst: " << dst - << " value: " << value - << " gas_cost: " << fix_call_gas_info_->gas_cost - << " stipend: " << fix_call_gas_info_->stipend; - } - } -} - void TraceTracer::on_execution_start(evmc_revision rev, const evmc_message& msg, evmone::bytes_view code) noexcept { if (opcode_names_ == nullptr) { opcode_names_ = evmc_get_instruction_names_table(rev); diff --git a/silkworm/rpc/core/evm_trace.hpp b/silkworm/rpc/core/evm_trace.hpp index 3639622edc..e8c1346b88 100644 --- a/silkworm/rpc/core/evm_trace.hpp +++ b/silkworm/rpc/core/evm_trace.hpp @@ -89,10 +89,10 @@ struct TraceMemory { }; struct TraceEx { + int64_t used{0}; std::optional memory; std::vector stack; std::optional storage; - int64_t used{0}; }; struct VmTrace; @@ -153,12 +153,6 @@ class VmTraceTracer : public silkworm::EvmTracer { void on_precompiled_run(const evmc_result& result, int64_t gas, const silkworm::IntraBlockState& intra_block_state) noexcept override; private: - void fill_call_gas_info(TraceOp& trace_op, - const evmone::ExecutionState& execution_state, - const intx::uint256* stack_top, - int stack_height, - const silkworm::IntraBlockState& intra_block_state); - VmTrace& vm_trace_; std::int32_t transaction_index_; std::stack index_prefix_; @@ -167,7 +161,6 @@ class VmTraceTracer : public silkworm::EvmTracer { const evmc_instruction_metrics* metrics_ = nullptr; std::stack start_gas_; std::stack trace_memory_stack_; - std::optional fix_call_gas_info_; std::optional last_opcode_; };