revm_handler/
precompile_provider.rs

1use auto_impl::auto_impl;
2use context::Cfg;
3use context_interface::ContextTr;
4use interpreter::{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
103        let mut result = InterpreterResult {
104            result: InstructionResult::Return,
105            gas: Gas::new(gas_limit),
106            output: Bytes::new(),
107        };
108
109        match (*precompile)(&inputs.input, gas_limit) {
110            Ok(output) => {
111                let underflow = result.gas.record_cost(output.gas_used);
112                assert!(underflow, "Gas underflow is not possible");
113                result.result = InstructionResult::Return;
114                result.output = output.bytes;
115            }
116            Err(PrecompileError::Fatal(e)) => return Err(e),
117            Err(e) => {
118                result.result = if e.is_oog() {
119                    InstructionResult::PrecompileOOG
120                } else {
121                    InstructionResult::PrecompileError
122                };
123            }
124        }
125        Ok(Some(result))
126    }
127
128    fn warm_addresses(&self) -> Box<impl Iterator<Item = Address>> {
129        self.warm_addresses()
130    }
131
132    fn contains(&self, address: &Address) -> bool {
133        self.contains(address)
134    }
135}