revm_precompile/
interface.rs

1//! Interface for the precompiles. It contains the precompile result type,
2//! the precompile output type, and the precompile error type.
3use core::fmt::{self, Debug};
4use primitives::{Bytes, OnceLock};
5use std::{borrow::Cow, boxed::Box, string::String, vec::Vec};
6
7use crate::bls12_381::{G1Point, G1PointScalar, G2Point, G2PointScalar};
8
9/// Global crypto provider instance
10static CRYPTO: OnceLock<Box<dyn Crypto>> = OnceLock::new();
11
12/// Install a custom crypto provider globally.
13pub fn install_crypto<C: Crypto + 'static>(crypto: C) -> bool {
14    CRYPTO.set(Box::new(crypto)).is_ok()
15}
16
17/// Get the installed crypto provider, or the default if none is installed.
18pub fn crypto() -> &'static dyn Crypto {
19    CRYPTO.get_or_init(|| Box::new(DefaultCrypto)).as_ref()
20}
21
22/// A precompile operation result type
23///
24/// Returns either `Ok((gas_used, return_bytes))` or `Err(error)`.
25pub type PrecompileResult = Result<PrecompileOutput, PrecompileError>;
26
27/// Precompile execution output
28#[derive(Clone, Debug, PartialEq, Eq, Hash)]
29pub struct PrecompileOutput {
30    /// Gas used by the precompile
31    pub gas_used: u64,
32    /// Gas refunded by the precompile.
33    pub gas_refunded: i64,
34    /// Output bytes
35    pub bytes: Bytes,
36    /// Whether the precompile reverted
37    pub reverted: bool,
38}
39
40impl PrecompileOutput {
41    /// Returns new precompile output with the given gas used and output bytes.
42    pub fn new(gas_used: u64, bytes: Bytes) -> Self {
43        Self {
44            gas_used,
45            gas_refunded: 0,
46            bytes,
47            reverted: false,
48        }
49    }
50
51    /// Returns new precompile revert with the given gas used and output bytes.
52    pub fn new_reverted(gas_used: u64, bytes: Bytes) -> Self {
53        Self {
54            gas_used,
55            gas_refunded: 0,
56            bytes,
57            reverted: true,
58        }
59    }
60
61    /// Flips [`Self::reverted`] to `true`.
62    pub fn reverted(mut self) -> Self {
63        self.reverted = true;
64        self
65    }
66}
67
68/// Crypto operations trait for precompiles.
69pub trait Crypto: Send + Sync + Debug {
70    /// Compute SHA-256 hash
71    #[inline]
72    fn sha256(&self, input: &[u8]) -> [u8; 32] {
73        use sha2::Digest;
74        let output = sha2::Sha256::digest(input);
75        output.into()
76    }
77
78    /// Compute RIPEMD-160 hash
79    #[inline]
80    fn ripemd160(&self, input: &[u8]) -> [u8; 32] {
81        use ripemd::Digest;
82        let mut hasher = ripemd::Ripemd160::new();
83        hasher.update(input);
84
85        let mut output = [0u8; 32];
86        hasher.finalize_into((&mut output[12..]).into());
87        output
88    }
89
90    /// BN254 elliptic curve addition.
91    #[inline]
92    fn bn254_g1_add(&self, p1: &[u8], p2: &[u8]) -> Result<[u8; 64], PrecompileError> {
93        crate::bn254::crypto_backend::g1_point_add(p1, p2)
94    }
95
96    /// BN254 elliptic curve scalar multiplication.
97    #[inline]
98    fn bn254_g1_mul(&self, point: &[u8], scalar: &[u8]) -> Result<[u8; 64], PrecompileError> {
99        crate::bn254::crypto_backend::g1_point_mul(point, scalar)
100    }
101
102    /// BN254 pairing check.
103    #[inline]
104    fn bn254_pairing_check(&self, pairs: &[(&[u8], &[u8])]) -> Result<bool, PrecompileError> {
105        crate::bn254::crypto_backend::pairing_check(pairs)
106    }
107
108    /// secp256k1 ECDSA signature recovery.
109    #[inline]
110    fn secp256k1_ecrecover(
111        &self,
112        sig: &[u8; 64],
113        recid: u8,
114        msg: &[u8; 32],
115    ) -> Result<[u8; 32], PrecompileError> {
116        crate::secp256k1::ecrecover_bytes(*sig, recid, *msg)
117            .ok_or(PrecompileError::Secp256k1RecoverFailed)
118    }
119
120    /// Modular exponentiation.
121    #[inline]
122    fn modexp(&self, base: &[u8], exp: &[u8], modulus: &[u8]) -> Result<Vec<u8>, PrecompileError> {
123        Ok(crate::modexp::modexp(base, exp, modulus))
124    }
125
126    /// Blake2 compression function.
127    #[inline]
128    fn blake2_compress(&self, rounds: u32, h: &mut [u64; 8], m: [u64; 16], t: [u64; 2], f: bool) {
129        crate::blake2::algo::compress(rounds as usize, h, m, t, f);
130    }
131
132    /// secp256r1 (P-256) signature verification.
133    #[inline]
134    fn secp256r1_verify_signature(&self, msg: &[u8; 32], sig: &[u8; 64], pk: &[u8; 64]) -> bool {
135        crate::secp256r1::verify_signature(*msg, *sig, *pk).is_some()
136    }
137
138    /// KZG point evaluation.
139    #[inline]
140    fn verify_kzg_proof(
141        &self,
142        z: &[u8; 32],
143        y: &[u8; 32],
144        commitment: &[u8; 48],
145        proof: &[u8; 48],
146    ) -> Result<(), PrecompileError> {
147        if !crate::kzg_point_evaluation::verify_kzg_proof(commitment, z, y, proof) {
148            return Err(PrecompileError::BlobVerifyKzgProofFailed);
149        }
150
151        Ok(())
152    }
153
154    /// BLS12-381 G1 addition (returns 96-byte unpadded G1 point)
155    fn bls12_381_g1_add(&self, a: G1Point, b: G1Point) -> Result<[u8; 96], PrecompileError> {
156        crate::bls12_381::crypto_backend::p1_add_affine_bytes(a, b)
157    }
158
159    /// BLS12-381 G1 multi-scalar multiplication (returns 96-byte unpadded G1 point)
160    fn bls12_381_g1_msm(
161        &self,
162        pairs: &mut dyn Iterator<Item = Result<G1PointScalar, PrecompileError>>,
163    ) -> Result<[u8; 96], PrecompileError> {
164        crate::bls12_381::crypto_backend::p1_msm_bytes(pairs)
165    }
166
167    /// BLS12-381 G2 addition (returns 192-byte unpadded G2 point)
168    fn bls12_381_g2_add(&self, a: G2Point, b: G2Point) -> Result<[u8; 192], PrecompileError> {
169        crate::bls12_381::crypto_backend::p2_add_affine_bytes(a, b)
170    }
171
172    /// BLS12-381 G2 multi-scalar multiplication (returns 192-byte unpadded G2 point)
173    fn bls12_381_g2_msm(
174        &self,
175        pairs: &mut dyn Iterator<Item = Result<G2PointScalar, PrecompileError>>,
176    ) -> Result<[u8; 192], PrecompileError> {
177        crate::bls12_381::crypto_backend::p2_msm_bytes(pairs)
178    }
179
180    /// BLS12-381 pairing check.
181    fn bls12_381_pairing_check(
182        &self,
183        pairs: &[(G1Point, G2Point)],
184    ) -> Result<bool, PrecompileError> {
185        crate::bls12_381::crypto_backend::pairing_check_bytes(pairs)
186    }
187
188    /// BLS12-381 map field element to G1.
189    fn bls12_381_fp_to_g1(&self, fp: &[u8; 48]) -> Result<[u8; 96], PrecompileError> {
190        crate::bls12_381::crypto_backend::map_fp_to_g1_bytes(fp)
191    }
192
193    /// BLS12-381 map field element to G2.
194    fn bls12_381_fp2_to_g2(&self, fp2: ([u8; 48], [u8; 48])) -> Result<[u8; 192], PrecompileError> {
195        crate::bls12_381::crypto_backend::map_fp2_to_g2_bytes(&fp2.0, &fp2.1)
196    }
197}
198
199/// Precompile function type. Takes input, gas limit, and crypto implementation and returns precompile result.
200pub type PrecompileFn = fn(&[u8], u64) -> PrecompileResult;
201
202/// Precompile error type.
203#[derive(Clone, Debug, PartialEq, Eq, Hash)]
204pub enum PrecompileError {
205    /// out of gas is the main error. Others are here just for completeness
206    OutOfGas,
207    /// Blake2 errors
208    Blake2WrongLength,
209    /// Blake2 wrong final indicator flag
210    Blake2WrongFinalIndicatorFlag,
211    /// Modexp errors
212    ModexpExpOverflow,
213    /// Modexp base overflow
214    ModexpBaseOverflow,
215    /// Modexp mod overflow
216    ModexpModOverflow,
217    /// Modexp limit all input sizes.
218    ModexpEip7823LimitSize,
219    /// Bn254 errors
220    Bn254FieldPointNotAMember,
221    /// Bn254 affine g failed to create
222    Bn254AffineGFailedToCreate,
223    /// Bn254 pair length
224    Bn254PairLength,
225    // Blob errors
226    /// The input length is not exactly 192 bytes
227    BlobInvalidInputLength,
228    /// The commitment does not match the versioned hash
229    BlobMismatchedVersion,
230    /// The proof verification failed
231    BlobVerifyKzgProofFailed,
232    /// Non-canonical field element
233    NonCanonicalFp,
234    /// BLS12-381 G1 point not on curve
235    Bls12381G1NotOnCurve,
236    /// BLS12-381 G1 point not in correct subgroup
237    Bls12381G1NotInSubgroup,
238    /// BLS12-381 G2 point not on curve
239    Bls12381G2NotOnCurve,
240    /// BLS12-381 G2 point not in correct subgroup
241    Bls12381G2NotInSubgroup,
242    /// BLS12-381 scalar input length error
243    Bls12381ScalarInputLength,
244    /// BLS12-381 G1 add input length error
245    Bls12381G1AddInputLength,
246    /// BLS12-381 G1 msm input length error
247    Bls12381G1MsmInputLength,
248    /// BLS12-381 G2 add input length error
249    Bls12381G2AddInputLength,
250    /// BLS12-381 G2 msm input length error
251    Bls12381G2MsmInputLength,
252    /// BLS12-381 pairing input length error
253    Bls12381PairingInputLength,
254    /// BLS12-381 map fp to g1 input length error
255    Bls12381MapFpToG1InputLength,
256    /// BLS12-381 map fp2 to g2 input length error
257    Bls12381MapFp2ToG2InputLength,
258    /// BLS12-381 padding error
259    Bls12381FpPaddingInvalid,
260    /// BLS12-381 fp padding length error
261    Bls12381FpPaddingLength,
262    /// BLS12-381 g1 padding length error
263    Bls12381G1PaddingLength,
264    /// BLS12-381 g2 padding length error
265    Bls12381G2PaddingLength,
266    /// KZG invalid G1 point
267    KzgInvalidG1Point,
268    /// KZG G1 point not on curve
269    KzgG1PointNotOnCurve,
270    /// KZG G1 point not in correct subgroup
271    KzgG1PointNotInSubgroup,
272    /// KZG input length error
273    KzgInvalidInputLength,
274    /// secp256k1 ecrecover failed
275    Secp256k1RecoverFailed,
276    /// Fatal error with a custom error message
277    Fatal(String),
278    /// Catch-all variant with a custom error message
279    Other(Cow<'static, str>),
280}
281
282impl PrecompileError {
283    /// Returns another error with the given message.
284    pub fn other(err: impl Into<String>) -> Self {
285        Self::Other(Cow::Owned(err.into()))
286    }
287
288    /// Returns another error with the given static string.
289    pub const fn other_static(err: &'static str) -> Self {
290        Self::Other(Cow::Borrowed(err))
291    }
292
293    /// Returns `true` if the error is out of gas.
294    pub fn is_oog(&self) -> bool {
295        matches!(self, Self::OutOfGas)
296    }
297}
298
299impl core::error::Error for PrecompileError {}
300
301impl fmt::Display for PrecompileError {
302    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
303        let s = match self {
304            Self::OutOfGas => "out of gas",
305            Self::Blake2WrongLength => "wrong input length for blake2",
306            Self::Blake2WrongFinalIndicatorFlag => "wrong final indicator flag for blake2",
307            Self::ModexpExpOverflow => "modexp exp overflow",
308            Self::ModexpBaseOverflow => "modexp base overflow",
309            Self::ModexpModOverflow => "modexp mod overflow",
310            Self::ModexpEip7823LimitSize => "Modexp limit all input sizes.",
311            Self::Bn254FieldPointNotAMember => "field point not a member of bn254 curve",
312            Self::Bn254AffineGFailedToCreate => "failed to create affine g point for bn254 curve",
313            Self::Bn254PairLength => "bn254 invalid pair length",
314            Self::BlobInvalidInputLength => "invalid blob input length",
315            Self::BlobMismatchedVersion => "mismatched blob version",
316            Self::BlobVerifyKzgProofFailed => "verifying blob kzg proof failed",
317            Self::NonCanonicalFp => "non-canonical field element",
318            Self::Bls12381G1NotOnCurve => "bls12-381 g1 point not on curve",
319            Self::Bls12381G1NotInSubgroup => "bls12-381 g1 point not in correct subgroup",
320            Self::Bls12381G2NotOnCurve => "bls12-381 g2 point not on curve",
321            Self::Bls12381G2NotInSubgroup => "bls12-381 g2 point not in correct subgroup",
322            Self::Bls12381ScalarInputLength => "bls12-381 scalar input length error",
323            Self::Bls12381G1AddInputLength => "bls12-381 g1 add input length error",
324            Self::Bls12381G1MsmInputLength => "bls12-381 g1 msm input length error",
325            Self::Bls12381G2AddInputLength => "bls12-381 g2 add input length error",
326            Self::Bls12381G2MsmInputLength => "bls12-381 g2 msm input length error",
327            Self::Bls12381PairingInputLength => "bls12-381 pairing input length error",
328            Self::Bls12381MapFpToG1InputLength => "bls12-381 map fp to g1 input length error",
329            Self::Bls12381MapFp2ToG2InputLength => "bls12-381 map fp2 to g2 input length error",
330            Self::Bls12381FpPaddingInvalid => "bls12-381 fp 64 top bytes of input are not zero",
331            Self::Bls12381FpPaddingLength => "bls12-381 fp padding length error",
332            Self::Bls12381G1PaddingLength => "bls12-381 g1 padding length error",
333            Self::Bls12381G2PaddingLength => "bls12-381 g2 padding length error",
334            Self::KzgInvalidG1Point => "kzg invalid g1 point",
335            Self::KzgG1PointNotOnCurve => "kzg g1 point not on curve",
336            Self::KzgG1PointNotInSubgroup => "kzg g1 point not in correct subgroup",
337            Self::KzgInvalidInputLength => "kzg invalid input length",
338            Self::Secp256k1RecoverFailed => "secp256k1 signature recovery failed",
339            Self::Fatal(s) => s,
340            Self::Other(s) => s,
341        };
342        f.write_str(s)
343    }
344}
345
346/// Default implementation of the Crypto trait using the existing crypto libraries.
347#[derive(Clone, Debug)]
348pub struct DefaultCrypto;
349
350impl Crypto for DefaultCrypto {}