revm_handler/
precompile_provider.rs

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