From 33875b4a76b3587d457e5165bd5aadf0c708528a Mon Sep 17 00:00:00 2001 From: Sixtysixter <20945591+Sixtysixter@users.noreply.github.com> Date: Sat, 3 Aug 2024 00:58:22 +0200 Subject: [PATCH] rpcdaemon: fix trace_* API family gas cost (#2187) --- .github/workflows/rpc-integration-tests.yml | 2 +- silkworm/rpc/core/evm_debug.cpp | 2 +- silkworm/rpc/core/evm_trace.cpp | 81 ++++++++++++++++++--- silkworm/rpc/core/evm_trace.hpp | 19 ++++- 4 files changed, 89 insertions(+), 15 deletions(-) diff --git a/.github/workflows/rpc-integration-tests.yml b/.github/workflows/rpc-integration-tests.yml index 11dda7140a..501b6be59d 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.33.0 https://github.com/erigontech/rpc-tests ${{runner.workspace}}/rpc-tests + git -c advice.detachedHead=false clone --depth 1 --branch v0.34.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_debug.cpp b/silkworm/rpc/core/evm_debug.cpp index 11216695df..0e79508cef 100644 --- a/silkworm/rpc/core/evm_debug.cpp +++ b/silkworm/rpc/core/evm_debug.cpp @@ -297,7 +297,7 @@ void DebugTracer::fill_call_gas_info(unsigned char opcode, const evmone::Executi 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; + 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 diff --git a/silkworm/rpc/core/evm_trace.cpp b/silkworm/rpc/core/evm_trace.cpp index e4a7b4e2a2..0cf3c10369 100644 --- a/silkworm/rpc/core/evm_trace.cpp +++ b/silkworm/rpc/core/evm_trace.cpp @@ -24,7 +24,6 @@ #include #include -#include #include #include #include @@ -576,10 +575,6 @@ void VmTraceTracer::on_execution_start(evmc_revision rev, const evmc_message& ms opcode_names_ = evmc_get_instruction_names_table(rev); metrics_ = evmc_get_instruction_metrics_table(rev); } - if (precompile::is_precompile(msg.code_address, rev)) { - is_precompile_ = true; - return; - } start_gas_.push(msg.gas); @@ -622,11 +617,24 @@ 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); + 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(); + } + auto& vm_trace = traces_stack_.top().get(); if (!vm_trace.ops.empty()) { auto& op = vm_trace.ops[vm_trace.ops.size() - 1]; @@ -655,6 +663,9 @@ void VmTraceTracer::on_instruction_start(uint32_t pc, const intx::uint256* stack copy_store(op_code, stack_top, trace_op.trace_ex.storage); 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) @@ -680,13 +691,14 @@ 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 { - if (is_precompile_) { - is_precompile_ = false; - return; - } auto& vm_trace = traces_stack_.top().get(); traces_stack_.pop(); @@ -736,9 +748,27 @@ void VmTraceTracer::on_execution_end(const evmc_result& result, const silkworm:: break; case evmc_status_code::EVMC_REVERT: + op.gas_cost = op.gas_cost - result.gas_left; + op.trace_ex.used = result.gas_left; + 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; + } + } break; } } @@ -747,6 +777,35 @@ 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 1d3caaa813..87f638bda9 100644 --- a/silkworm/rpc/core/evm_trace.hpp +++ b/silkworm/rpc/core/evm_trace.hpp @@ -128,6 +128,15 @@ void copy_store(std::uint8_t op_code, const evmone::uint256* stack, std::optiona void copy_memory_offset_len(std::uint8_t op_code, const evmone::uint256* stack, std::optional& trace_memory); void push_memory_offset_len(std::uint8_t op_code, const evmone::uint256* stack, std::stack& tms); +struct FixCallGasInfo { + int32_t depth{0}; + int64_t stipend{0}; + int16_t code_cost{0}; + TraceOp& trace_op_; + int64_t gas_cost{0}; + bool precompiled{false}; +}; + class VmTraceTracer : public silkworm::EvmTracer { public: explicit VmTraceTracer(VmTrace& vm_trace, std::int32_t index = -1) : vm_trace_(vm_trace), transaction_index_{index} {} @@ -144,15 +153,21 @@ 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: - bool is_precompile_{false}; + 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_; std::stack> traces_stack_; const char* const* opcode_names_ = nullptr; + const evmc_instruction_metrics* metrics_ = nullptr; std::stack start_gas_; std::stack trace_memory_stack_; - const evmc_instruction_metrics* metrics_ = nullptr; + std::optional fix_call_gas_info_; }; struct TraceAction {