rtvm_primitives/
precompile.rs

1use crate::{Bytes, Env};
2use core::fmt;
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<(u64, Bytes), PrecompileError>;
10
11pub type StandardPrecompileFn = fn(&Bytes, u64) -> PrecompileResult;
12pub type EnvPrecompileFn = fn(&Bytes, u64, env: &Env) -> PrecompileResult;
13
14/// Stateful precompile trait. It is used to create
15/// a arc precompile Precompile::Stateful.
16pub trait StatefulPrecompile: Sync + Send {
17    fn call(&self, bytes: &Bytes, gas_price: u64, env: &Env) -> PrecompileResult;
18}
19
20/// Mutable stateful precompile trait. It is used to create
21/// a boxed precompile in Precompile::StatefulMut.
22pub trait StatefulPrecompileMut: DynClone + Send + Sync {
23    fn call_mut(&mut self, bytes: &Bytes, gas_price: u64, env: &Env) -> PrecompileResult;
24}
25
26dyn_clone::clone_trait_object!(StatefulPrecompileMut);
27
28/// Arc over stateful precompile.
29pub type StatefulPrecompileArc = Arc<dyn StatefulPrecompile>;
30
31/// Box over mutable stateful precompile
32pub type StatefulPrecompileBox = Box<dyn StatefulPrecompileMut>;
33
34/// Precompile and its handlers.
35#[derive(Clone)]
36pub enum Precompile {
37    /// Standard simple precompile that takes input and gas limit.
38    Standard(StandardPrecompileFn),
39    /// Similar to Standard but takes reference to environment.
40    Env(EnvPrecompileFn),
41    /// Stateful precompile that is Arc over [`StatefulPrecompile`] trait.
42    /// It takes a reference to input, gas limit and environment.
43    Stateful(StatefulPrecompileArc),
44    /// Mutable stateful precompile that is Box over [`StatefulPrecompileMut`] trait.
45    /// It takes a reference to input, gas limit and environment.
46    StatefulMut(StatefulPrecompileBox),
47}
48
49impl From<StandardPrecompileFn> for Precompile {
50    fn from(p: StandardPrecompileFn) -> Self {
51        Precompile::Standard(p)
52    }
53}
54
55impl From<EnvPrecompileFn> for Precompile {
56    fn from(p: EnvPrecompileFn) -> Self {
57        Precompile::Env(p)
58    }
59}
60
61impl From<StatefulPrecompileArc> for Precompile {
62    fn from(p: StatefulPrecompileArc) -> Self {
63        Precompile::Stateful(p)
64    }
65}
66
67impl From<StatefulPrecompileBox> for Precompile {
68    fn from(p: StatefulPrecompileBox) -> Self {
69        Precompile::StatefulMut(p)
70    }
71}
72
73impl fmt::Debug for Precompile {
74    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
75        match self {
76            Precompile::Standard(_) => f.write_str("Standard"),
77            Precompile::Env(_) => f.write_str("Env"),
78            Precompile::Stateful(_) => f.write_str("Stateful"),
79            Precompile::StatefulMut(_) => f.write_str("StatefulMut"),
80        }
81    }
82}
83
84impl Precompile {
85    /// Create a new stateful precompile.
86    pub fn new_stateful<P: StatefulPrecompile + 'static>(p: P) -> Self {
87        Self::Stateful(Arc::new(p))
88    }
89
90    /// Create a new mutable stateful precompile.
91    pub fn new_stateful_mut<P: StatefulPrecompileMut + 'static>(p: P) -> Self {
92        Self::StatefulMut(Box::new(p))
93    }
94
95    /// Call the precompile with the given input and gas limit and return the result.
96    pub fn call(&mut self, bytes: &Bytes, gas_price: u64, env: &Env) -> PrecompileResult {
97        match self {
98            Precompile::Standard(p) => p(bytes, gas_price),
99            Precompile::Env(p) => p(bytes, gas_price, env),
100            Precompile::Stateful(p) => p.call(bytes, gas_price, env),
101            Precompile::StatefulMut(p) => p.call_mut(bytes, gas_price, env),
102        }
103    }
104}
105
106#[derive(Clone, Debug, PartialEq, Eq, Hash)]
107pub enum PrecompileError {
108    /// out of gas is the main error. Others are here just for completeness
109    OutOfGas,
110    // Blake2 errors
111    Blake2WrongLength,
112    Blake2WrongFinalIndicatorFlag,
113    // Modexp errors
114    ModexpExpOverflow,
115    ModexpBaseOverflow,
116    ModexpModOverflow,
117    // Bn128 errors
118    Bn128FieldPointNotAMember,
119    Bn128AffineGFailedToCreate,
120    Bn128PairLength,
121    // Blob errors
122    /// The input length is not exactly 192 bytes.
123    BlobInvalidInputLength,
124    /// The commitment does not match the versioned hash.
125    BlobMismatchedVersion,
126    /// The proof verification failed.
127    BlobVerifyKzgProofFailed,
128    /// Catch-all variant for other errors.
129    Other(String),
130}
131
132impl PrecompileError {
133    pub fn other(err: impl Into<String>) -> Self {
134        Self::Other(err.into())
135    }
136}
137
138#[cfg(feature = "std")]
139impl std::error::Error for PrecompileError {}
140
141impl fmt::Display for PrecompileError {
142    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
143        let s = match self {
144            Self::OutOfGas => "out of gas",
145            Self::Blake2WrongLength => "wrong input length for blake2",
146            Self::Blake2WrongFinalIndicatorFlag => "wrong final indicator flag for blake2",
147            Self::ModexpExpOverflow => "modexp exp overflow",
148            Self::ModexpBaseOverflow => "modexp base overflow",
149            Self::ModexpModOverflow => "modexp mod overflow",
150            Self::Bn128FieldPointNotAMember => "field point not a member of bn128 curve",
151            Self::Bn128AffineGFailedToCreate => "failed to create affine g point for bn128 curve",
152            Self::Bn128PairLength => "bn128 invalid pair length",
153            Self::BlobInvalidInputLength => "invalid blob input length",
154            Self::BlobMismatchedVersion => "mismatched blob version",
155            Self::BlobVerifyKzgProofFailed => "verifying blob kzg proof failed",
156            Self::Other(s) => s,
157        };
158        f.write_str(s)
159    }
160}
161
162#[cfg(test)]
163mod test {
164    use super::*;
165
166    #[test]
167    fn stateful_precompile_mut() {
168        #[derive(Default, Clone)]
169        struct MyPrecompile {}
170
171        impl StatefulPrecompileMut for MyPrecompile {
172            fn call_mut(
173                &mut self,
174                _bytes: &Bytes,
175                _gas_price: u64,
176                _env: &Env,
177            ) -> PrecompileResult {
178                PrecompileResult::Err(PrecompileError::OutOfGas)
179            }
180        }
181
182        let mut p = Precompile::new_stateful_mut(MyPrecompile::default());
183        match &mut p {
184            Precompile::StatefulMut(p) => {
185                let _ = p.call_mut(&Bytes::new(), 0, &Env::default());
186            }
187            _ => panic!("not a state"),
188        }
189    }
190}