Skip to content

Commit

Permalink
rpcdaemon: gas cost in trace_replay* API (#2353)
Browse files Browse the repository at this point in the history
  • Loading branch information
Sixtysixter authored Sep 19, 2024
1 parent 7d8050e commit e49e1d2
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 95 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/rpc-integration-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
118 changes: 32 additions & 86 deletions silkworm/rpc/core/evm_trace.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<VmTrace>();
traces_stack_.emplace(*op.sub);
op.sub->code = "0x" + silkworm::to_hex(code);
Expand All @@ -623,48 +623,39 @@ 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);
}

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<struct TraceEx>();
trace_op.trace_ex = TraceEx{used};

if (op_code == OP_SELFDESTRUCT) {
trace_op.sub = std::make_shared<VmTrace>();
Expand All @@ -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)
Expand All @@ -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 {
Expand Down Expand Up @@ -737,51 +721,42 @@ 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
case evmc_status_code::EVMC_STACK_UNDERFLOW:
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;
}

Expand All @@ -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<evmc::address>(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);
Expand Down
9 changes: 1 addition & 8 deletions silkworm/rpc/core/evm_trace.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,10 @@ struct TraceMemory {
};

struct TraceEx {
int64_t used{0};
std::optional<TraceMemory> memory;
std::vector<std::string> stack;
std::optional<TraceStorage> storage;
int64_t used{0};
};

struct VmTrace;
Expand Down Expand Up @@ -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<std::string> index_prefix_;
Expand All @@ -167,7 +161,6 @@ class VmTraceTracer : public silkworm::EvmTracer {
const evmc_instruction_metrics* metrics_ = nullptr;
std::stack<int64_t> start_gas_;
std::stack<TraceMemory> trace_memory_stack_;
std::optional<FixCallGasInfo> fix_call_gas_info_;
std::optional<uint8_t> last_opcode_;
};

Expand Down

0 comments on commit e49e1d2

Please sign in to comment.