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