use revm_interpreter::CallOutcome;
use crate::{
interpreter::{CallInputs, CreateInputs, CreateOutcome},
primitives::db::Database,
EvmContext, Inspector,
};
#[allow(dead_code)]
#[derive(Clone, Copy, Debug, Default)]
pub struct GasInspector {
gas_remaining: u64,
last_gas_cost: u64,
}
impl GasInspector {
pub fn gas_remaining(&self) -> u64 {
self.gas_remaining
}
pub fn last_gas_cost(&self) -> u64 {
self.last_gas_cost
}
}
impl<DB: Database> Inspector<DB> for GasInspector {
fn initialize_interp(
&mut self,
interp: &mut crate::interpreter::Interpreter,
_context: &mut EvmContext<DB>,
) {
self.gas_remaining = interp.gas.limit();
}
fn step(
&mut self,
interp: &mut crate::interpreter::Interpreter,
_context: &mut EvmContext<DB>,
) {
self.gas_remaining = interp.gas.remaining();
}
fn step_end(
&mut self,
interp: &mut crate::interpreter::Interpreter,
_context: &mut EvmContext<DB>,
) {
let remaining = interp.gas.remaining();
self.last_gas_cost = self.gas_remaining.saturating_sub(remaining);
self.gas_remaining = remaining;
}
fn call_end(
&mut self,
_context: &mut EvmContext<DB>,
_inputs: &CallInputs,
mut outcome: CallOutcome,
) -> CallOutcome {
if outcome.result.result.is_error() {
outcome.result.gas.spend_all();
self.gas_remaining = 0;
}
outcome
}
fn create_end(
&mut self,
_context: &mut EvmContext<DB>,
_inputs: &CreateInputs,
mut outcome: CreateOutcome,
) -> CreateOutcome {
if outcome.result.result.is_error() {
outcome.result.gas.spend_all();
self.gas_remaining = 0;
}
outcome
}
}
#[cfg(test)]
mod tests {
use revm_interpreter::CallOutcome;
use revm_interpreter::CreateOutcome;
use crate::{
inspectors::GasInspector,
interpreter::{CallInputs, CreateInputs, Interpreter},
primitives::Log,
Database, EvmContext, Inspector,
};
#[derive(Default, Debug)]
struct StackInspector {
pc: usize,
gas_inspector: GasInspector,
gas_remaining_steps: Vec<(usize, u64)>,
}
impl<DB: Database> Inspector<DB> for StackInspector {
fn initialize_interp(&mut self, interp: &mut Interpreter, context: &mut EvmContext<DB>) {
self.gas_inspector.initialize_interp(interp, context);
}
fn step(&mut self, interp: &mut Interpreter, context: &mut EvmContext<DB>) {
self.pc = interp.program_counter();
self.gas_inspector.step(interp, context);
}
fn log(&mut self, interp: &mut Interpreter, context: &mut EvmContext<DB>, log: &Log) {
self.gas_inspector.log(interp, context, log);
}
fn step_end(&mut self, interp: &mut Interpreter, context: &mut EvmContext<DB>) {
self.gas_inspector.step_end(interp, context);
self.gas_remaining_steps
.push((self.pc, self.gas_inspector.gas_remaining()));
}
fn call(
&mut self,
context: &mut EvmContext<DB>,
call: &mut CallInputs,
) -> Option<CallOutcome> {
self.gas_inspector.call(context, call)
}
fn call_end(
&mut self,
context: &mut EvmContext<DB>,
inputs: &CallInputs,
outcome: CallOutcome,
) -> CallOutcome {
self.gas_inspector.call_end(context, inputs, outcome)
}
fn create(
&mut self,
context: &mut EvmContext<DB>,
call: &mut CreateInputs,
) -> Option<CreateOutcome> {
self.gas_inspector.create(context, call);
None
}
fn create_end(
&mut self,
context: &mut EvmContext<DB>,
inputs: &CreateInputs,
outcome: CreateOutcome,
) -> CreateOutcome {
self.gas_inspector.create_end(context, inputs, outcome)
}
}
#[test]
fn test_gas_inspector() {
use crate::{
db::BenchmarkDB,
inspector::inspector_handle_register,
interpreter::opcode,
primitives::{address, Bytecode, Bytes, TxKind},
Evm,
};
let contract_data: Bytes = Bytes::from(vec![
opcode::PUSH1,
0x1,
opcode::PUSH1,
0xb,
opcode::JUMPI,
opcode::PUSH1,
0x1,
opcode::PUSH1,
0x1,
opcode::PUSH1,
0x1,
opcode::JUMPDEST,
opcode::STOP,
]);
let bytecode = Bytecode::new_raw(contract_data);
let mut evm: Evm<'_, StackInspector, BenchmarkDB> = Evm::builder()
.with_db(BenchmarkDB::new_bytecode(bytecode.clone()))
.with_external_context(StackInspector::default())
.modify_tx_env(|tx| {
tx.clear();
tx.caller = address!("1000000000000000000000000000000000000000");
tx.transact_to = TxKind::Call(address!("0000000000000000000000000000000000000000"));
tx.gas_limit = 21100;
})
.append_handler_register(inspector_handle_register)
.build();
evm.transact().unwrap();
let inspector = evm.into_context().external;
let steps = vec![
(0, 97),
(2, 94),
(4, 84),
(11, 83),
(12, 83),
];
assert_eq!(inspector.gas_remaining_steps, steps);
}
}