revm_handler/
precompile_provider.rs

1use auto_impl::auto_impl;
2use context::{Cfg, LocalContextTr};
3use context_interface::ContextTr;
4use interpreter::{CallInput, Gas, InputsImpl, InstructionResult, InterpreterResult};
5use precompile::PrecompileError;
6use precompile::{PrecompileSpecId, Precompiles};
7use primitives::{hardfork::SpecId, Address, Bytes};
8use std::boxed::Box;
9use std::string::String;
10
11#[auto_impl(&mut, Box)]
12pub trait PrecompileProvider<CTX: ContextTr> {
13    type Output;
14
15    /// Sets the spec id and returns true if the spec id was changed. Initial call to set_spec will always return true.
16    ///
17    /// Returned booling will determine if precompile addresses should be injected into the journal.
18    fn set_spec(&mut self, spec: <CTX::Cfg as Cfg>::Spec) -> bool;
19
20    /// Run the precompile.
21    fn run(
22        &mut self,
23        context: &mut CTX,
24        address: &Address,
25        inputs: &InputsImpl,
26        is_static: bool,
27        gas_limit: u64,
28    ) -> Result<Option<Self::Output>, String>;
29
30    /// Get the warm addresses.
31    fn warm_addresses(&self) -> Box<impl Iterator<Item = Address>>;
32
33    /// Check if the address is a precompile.
34    fn contains(&self, address: &Address) -> bool;
35}
36
37/// The [`PrecompileProvider`] for ethereum precompiles.
38#[derive(Debug)]
39pub struct EthPrecompiles {
40    /// Contains precompiles for the current spec.
41    pub precompiles: &'static Precompiles,
42    /// Current spec. None means that spec was not set yet.
43    pub spec: SpecId,
44}
45
46impl EthPrecompiles {
47    /// Returns addresses of the precompiles.
48    pub fn warm_addresses(&self) -> Box<impl Iterator<Item = Address>> {
49        Box::new(self.precompiles.addresses().cloned())
50    }
51
52    /// Returns whether the address is a precompile.
53    pub fn contains(&self, address: &Address) -> bool {
54        self.precompiles.contains(address)
55    }
56}
57
58impl Clone for EthPrecompiles {
59    fn clone(&self) -> Self {
60        Self {
61            precompiles: self.precompiles,
62            spec: self.spec,
63        }
64    }
65}
66
67impl Default for EthPrecompiles {
68    fn default() -> Self {
69        let spec = SpecId::default();
70        Self {
71            precompiles: Precompiles::new(PrecompileSpecId::from_spec_id(spec)),
72            spec,
73        }
74    }
75}
76
77impl<CTX: ContextTr> PrecompileProvider<CTX> for EthPrecompiles {
78    type Output = InterpreterResult;
79
80    fn set_spec(&mut self, spec: <CTX::Cfg as Cfg>::Spec) -> bool {
81        let spec = spec.into();
82        // generate new precompiles only on new spec
83        if spec == self.spec {
84            return false;
85        }
86        self.precompiles = Precompiles::new(PrecompileSpecId::from_spec_id(spec));
87        self.spec = spec;
88        true
89    }
90
91    fn run(
92        &mut self,
93        context: &mut CTX,
94        address: &Address,
95        inputs: &InputsImpl,
96        _is_static: bool,
97        gas_limit: u64,
98    ) -> Result<Option<InterpreterResult>, String> {
99        let Some(precompile) = self.precompiles.get(address) else {
100            return Ok(None);
101        };
102        let mut result = InterpreterResult {
103            result: InstructionResult::Return,
104            gas: Gas::new(gas_limit),
105            output: Bytes::new(),
106        };
107
108        let r;
109        let input_bytes = match &inputs.input {
110            CallInput::SharedBuffer(range) => {
111                if let Some(slice) = context.local().shared_memory_buffer_slice(range.clone()) {
112                    r = slice;
113                    r.as_ref()
114                } else {
115                    &[]
116                }
117            }
118            CallInput::Bytes(bytes) => bytes.0.iter().as_slice(),
119        };
120
121        match (*precompile)(input_bytes, gas_limit) {
122            Ok(output) => {
123                let underflow = result.gas.record_cost(output.gas_used);
124                assert!(underflow, "Gas underflow is not possible");
125                result.result = InstructionResult::Return;
126                result.output = output.bytes;
127            }
128            Err(PrecompileError::Fatal(e)) => return Err(e),
129            Err(e) => {
130                result.result = if e.is_oog() {
131                    InstructionResult::PrecompileOOG
132                } else {
133                    InstructionResult::PrecompileError
134                };
135            }
136        }
137        Ok(Some(result))
138    }
139
140    fn warm_addresses(&self) -> Box<impl Iterator<Item = Address>> {
141        self.warm_addresses()
142    }
143
144    fn contains(&self, address: &Address) -> bool {
145        self.contains(address)
146    }
147}