revm_handler/
precompile_provider.rs1use auto_impl::auto_impl;
2use context::{Cfg, LocalContextTr};
3use context_interface::{ContextTr, JournalTr};
4use interpreter::{CallInputs, Gas, InstructionResult, InterpreterResult};
5use precompile::{PrecompileOutput, PrecompileSpecId, PrecompileStatus, Precompiles};
6use primitives::{hardfork::SpecId, Address, Bytes};
7use std::{
8 boxed::Box,
9 string::{String, ToString},
10};
11
12#[auto_impl(&mut, Box)]
14pub trait PrecompileProvider<CTX: ContextTr> {
15 type Output;
17
18 fn set_spec(&mut self, spec: <CTX::Cfg as Cfg>::Spec) -> bool;
22
23 fn run(
25 &mut self,
26 context: &mut CTX,
27 inputs: &CallInputs,
28 ) -> Result<Option<Self::Output>, String>;
29
30 fn warm_addresses(&self) -> Box<impl Iterator<Item = Address>>;
32
33 fn contains(&self, address: &Address) -> bool;
35}
36
37#[derive(Debug)]
39pub struct EthPrecompiles {
40 pub precompiles: &'static Precompiles,
42 pub spec: SpecId,
44}
45
46impl EthPrecompiles {
47 pub fn new(spec: SpecId) -> Self {
49 Self {
50 precompiles: Precompiles::new(PrecompileSpecId::from_spec_id(spec)),
51 spec,
52 }
53 }
54
55 pub fn warm_addresses(&self) -> Box<impl Iterator<Item = Address>> {
57 Box::new(self.precompiles.addresses().cloned())
58 }
59
60 pub fn contains(&self, address: &Address) -> bool {
62 self.precompiles.contains(address)
63 }
64}
65
66impl Clone for EthPrecompiles {
67 fn clone(&self) -> Self {
68 Self {
69 precompiles: self.precompiles,
70 spec: self.spec,
71 }
72 }
73}
74
75pub fn precompile_output_to_interpreter_result(
83 output: PrecompileOutput,
84 gas_limit: u64,
85) -> InterpreterResult {
86 let bytes = if output.status.is_success_or_revert() {
88 output.bytes
89 } else {
90 Bytes::new()
91 };
92
93 let mut result = InterpreterResult {
94 result: InstructionResult::Return,
95 gas: Gas::new_with_regular_gas_and_reservoir(gas_limit, output.reservoir),
96 output: bytes,
97 };
98
99 result.gas.set_state_gas_spent(output.state_gas_used);
101 result.gas.record_refund(output.gas_refunded);
102
103 if output.status.is_success_or_revert() {
105 let _ = result.gas.record_regular_cost(output.gas_used);
106 } else {
107 result.gas.spend_all();
108 }
109
110 result.result = match output.status {
112 PrecompileStatus::Success => InstructionResult::Return,
113 PrecompileStatus::Revert => InstructionResult::Revert,
114 PrecompileStatus::Halt(halt_reason) => {
115 if halt_reason.is_oog() {
116 InstructionResult::PrecompileOOG
117 } else {
118 InstructionResult::PrecompileError
119 }
120 }
121 };
122
123 result
124}
125
126impl<CTX: ContextTr> PrecompileProvider<CTX> for EthPrecompiles {
127 type Output = InterpreterResult;
128
129 fn set_spec(&mut self, spec: <CTX::Cfg as Cfg>::Spec) -> bool {
130 let spec = spec.into();
131 if spec == self.spec {
133 return false;
134 }
135 self.precompiles = Precompiles::new(PrecompileSpecId::from_spec_id(spec));
136 self.spec = spec;
137 true
138 }
139
140 fn run(
141 &mut self,
142 context: &mut CTX,
143 inputs: &CallInputs,
144 ) -> Result<Option<InterpreterResult>, String> {
145 let Some(precompile) = self.precompiles.get(&inputs.bytecode_address) else {
146 return Ok(None);
147 };
148
149 let output = precompile
150 .execute(
151 &inputs.input.as_bytes(context),
152 inputs.gas_limit,
153 inputs.reservoir,
154 )
155 .map_err(|e| e.to_string())?;
156
157 if let Some(halt_reason) = output.halt_reason() {
161 if !halt_reason.is_oog() && context.journal().depth() == 1 {
162 context
163 .local_mut()
164 .set_precompile_error_context(halt_reason.to_string());
165 }
166 }
167
168 let result = precompile_output_to_interpreter_result(output, inputs.gas_limit);
169 Ok(Some(result))
170 }
171
172 fn warm_addresses(&self) -> Box<impl Iterator<Item = Address>> {
173 Self::warm_addresses(self)
174 }
175
176 fn contains(&self, address: &Address) -> bool {
177 Self::contains(self, address)
178 }
179}