revm_primitives/
precompile.rs

1use crate::{Bytes, Env};
2use core::fmt::{self};
3use dyn_clone::DynClone;
4use std::{boxed::Box, string::String, sync::Arc};
5
6/// A precompile operation result.
7///
8/// Returns either `Ok((gas_used, return_bytes))` or `Err(error)`.
9pub type PrecompileResult = Result<PrecompileOutput, PrecompileErrors>;
10
11/// Precompile execution output
12#[derive(Clone, Debug, PartialEq, Eq, Hash)]
13pub struct PrecompileOutput {
14    /// Gas used by the precompile.
15    pub gas_used: u64,
16    /// Output bytes.
17    pub bytes: Bytes,
18}
19
20impl PrecompileOutput {
21    /// Returns new precompile output with the given gas used and output bytes.
22    pub fn new(gas_used: u64, bytes: Bytes) -> Self {
23        Self { gas_used, bytes }
24    }
25}
26
27pub type StandardPrecompileFn = fn(&Bytes, u64) -> PrecompileResult;
28pub type EnvPrecompileFn = fn(&Bytes, u64, env: &Env) -> PrecompileResult;
29
30/// Stateful precompile trait. It is used to create
31/// a arc precompile Precompile::Stateful.
32pub trait StatefulPrecompile: Sync + Send {
33    fn call(&self, bytes: &Bytes, gas_limit: u64, env: &Env) -> PrecompileResult;
34}
35
36/// Mutable stateful precompile trait. It is used to create
37/// a boxed precompile in Precompile::StatefulMut.
38pub trait StatefulPrecompileMut: DynClone + Send + Sync {
39    fn call_mut(&mut self, bytes: &Bytes, gas_limit: u64, env: &Env) -> PrecompileResult;
40}
41
42dyn_clone::clone_trait_object!(StatefulPrecompileMut);
43
44/// Arc over stateful precompile.
45pub type StatefulPrecompileArc = Arc<dyn StatefulPrecompile>;
46
47/// Box over mutable stateful precompile
48pub type StatefulPrecompileBox = Box<dyn StatefulPrecompileMut>;
49
50/// Precompile and its handlers.
51#[derive(Clone)]
52pub enum Precompile {
53    /// Standard simple precompile that takes input and gas limit.
54    Standard(StandardPrecompileFn),
55    /// Similar to Standard but takes reference to environment.
56    Env(EnvPrecompileFn),
57    /// Stateful precompile that is Arc over [`StatefulPrecompile`] trait.
58    /// It takes a reference to input, gas limit and environment.
59    Stateful(StatefulPrecompileArc),
60    /// Mutable stateful precompile that is Box over [`StatefulPrecompileMut`] trait.
61    /// It takes a reference to input, gas limit and environment.
62    StatefulMut(StatefulPrecompileBox),
63}
64
65impl From<StandardPrecompileFn> for Precompile {
66    fn from(p: StandardPrecompileFn) -> Self {
67        Precompile::Standard(p)
68    }
69}
70
71impl From<EnvPrecompileFn> for Precompile {
72    fn from(p: EnvPrecompileFn) -> Self {
73        Precompile::Env(p)
74    }
75}
76
77impl From<StatefulPrecompileArc> for Precompile {
78    fn from(p: StatefulPrecompileArc) -> Self {
79        Precompile::Stateful(p)
80    }
81}
82
83impl From<StatefulPrecompileBox> for Precompile {
84    fn from(p: StatefulPrecompileBox) -> Self {
85        Precompile::StatefulMut(p)
86    }
87}
88
89impl fmt::Debug for Precompile {
90    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
91        match self {
92            Precompile::Standard(_) => f.write_str("Standard"),
93            Precompile::Env(_) => f.write_str("Env"),
94            Precompile::Stateful(_) => f.write_str("Stateful"),
95            Precompile::StatefulMut(_) => f.write_str("StatefulMut"),
96        }
97    }
98}
99
100impl Precompile {
101    /// Create a new stateful precompile.
102    pub fn new_stateful<P: StatefulPrecompile + 'static>(p: P) -> Self {
103        Self::Stateful(Arc::new(p))
104    }
105
106    /// Create a new mutable stateful precompile.
107    pub fn new_stateful_mut<P: StatefulPrecompileMut + 'static>(p: P) -> Self {
108        Self::StatefulMut(Box::new(p))
109    }
110
111    /// Call the precompile with the given input and gas limit and return the result.
112    pub fn call(&mut self, bytes: &Bytes, gas_limit: u64, env: &Env) -> PrecompileResult {
113        match *self {
114            Precompile::Standard(p) => p(bytes, gas_limit),
115            Precompile::Env(p) => p(bytes, gas_limit, env),
116            Precompile::Stateful(ref p) => p.call(bytes, gas_limit, env),
117            Precompile::StatefulMut(ref mut p) => p.call_mut(bytes, gas_limit, env),
118        }
119    }
120
121    /// Call the precompile with the given input and gas limit and return the result.
122    ///
123    /// Returns an error if the precompile is mutable.
124    pub fn call_ref(&self, bytes: &Bytes, gas_limit: u64, env: &Env) -> PrecompileResult {
125        match *self {
126            Precompile::Standard(p) => p(bytes, gas_limit),
127            Precompile::Env(p) => p(bytes, gas_limit, env),
128            Precompile::Stateful(ref p) => p.call(bytes, gas_limit, env),
129            Precompile::StatefulMut(_) => Err(PrecompileErrors::Fatal {
130                msg: "call_ref on mutable stateful precompile".into(),
131            }),
132        }
133    }
134}
135
136/// Precompile errors.
137#[derive(Clone, Debug, PartialEq, Eq, Hash)]
138pub enum PrecompileErrors {
139    Error(PrecompileError),
140    Fatal { msg: String },
141}
142
143#[cfg(feature = "std")]
144impl std::error::Error for PrecompileErrors {}
145
146impl fmt::Display for PrecompileErrors {
147    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
148        match self {
149            Self::Error(e) => e.fmt(f),
150            Self::Fatal { msg } => f.write_str(msg),
151        }
152    }
153}
154
155#[derive(Clone, Debug, PartialEq, Eq, Hash)]
156pub enum PrecompileError {
157    /// out of gas is the main error. Others are here just for completeness
158    OutOfGas,
159    // Blake2 errors
160    Blake2WrongLength,
161    Blake2WrongFinalIndicatorFlag,
162    // Modexp errors
163    ModexpExpOverflow,
164    ModexpBaseOverflow,
165    ModexpModOverflow,
166    // Bn128 errors
167    Bn128FieldPointNotAMember,
168    Bn128AffineGFailedToCreate,
169    Bn128PairLength,
170    // Blob errors
171    /// The input length is not exactly 192 bytes.
172    BlobInvalidInputLength,
173    /// The commitment does not match the versioned hash.
174    BlobMismatchedVersion,
175    /// The proof verification failed.
176    BlobVerifyKzgProofFailed,
177    /// Catch-all variant for other errors.
178    Other(String),
179}
180
181impl PrecompileError {
182    /// Returns an other error with the given message.
183    pub fn other(err: impl Into<String>) -> Self {
184        Self::Other(err.into())
185    }
186
187    /// Returns true if the error is out of gas.
188    pub fn is_oog(&self) -> bool {
189        matches!(self, Self::OutOfGas)
190    }
191}
192
193impl From<PrecompileError> for PrecompileErrors {
194    fn from(err: PrecompileError) -> Self {
195        PrecompileErrors::Error(err)
196    }
197}
198
199#[cfg(feature = "std")]
200impl std::error::Error for PrecompileError {}
201
202impl fmt::Display for PrecompileError {
203    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
204        let s = match self {
205            Self::OutOfGas => "out of gas",
206            Self::Blake2WrongLength => "wrong input length for blake2",
207            Self::Blake2WrongFinalIndicatorFlag => "wrong final indicator flag for blake2",
208            Self::ModexpExpOverflow => "modexp exp overflow",
209            Self::ModexpBaseOverflow => "modexp base overflow",
210            Self::ModexpModOverflow => "modexp mod overflow",
211            Self::Bn128FieldPointNotAMember => "field point not a member of bn128 curve",
212            Self::Bn128AffineGFailedToCreate => "failed to create affine g point for bn128 curve",
213            Self::Bn128PairLength => "bn128 invalid pair length",
214            Self::BlobInvalidInputLength => "invalid blob input length",
215            Self::BlobMismatchedVersion => "mismatched blob version",
216            Self::BlobVerifyKzgProofFailed => "verifying blob kzg proof failed",
217            Self::Other(s) => s,
218        };
219        f.write_str(s)
220    }
221}
222
223#[cfg(test)]
224mod test {
225    use super::*;
226
227    #[test]
228    fn stateful_precompile_mut() {
229        #[derive(Default, Clone)]
230        struct MyPrecompile {}
231
232        impl StatefulPrecompileMut for MyPrecompile {
233            fn call_mut(
234                &mut self,
235                _bytes: &Bytes,
236                _gas_limit: u64,
237                _env: &Env,
238            ) -> PrecompileResult {
239                Err(PrecompileError::OutOfGas.into())
240            }
241        }
242
243        let mut p = Precompile::new_stateful_mut(MyPrecompile::default());
244        match &mut p {
245            Precompile::StatefulMut(p) => {
246                let _ = p.call_mut(&Bytes::new(), 0, &Env::default());
247            }
248            _ => panic!("not a state"),
249        }
250    }
251}