1use context_interface::result::AnyError;
4use core::fmt::{self, Debug};
5use primitives::{Bytes, OnceLock};
6use std::{borrow::Cow, boxed::Box, string::String, vec::Vec};
7
8use crate::bls12_381::{G1Point, G1PointScalar, G2Point, G2PointScalar};
9
10static CRYPTO: OnceLock<Box<dyn Crypto>> = OnceLock::new();
12
13pub fn install_crypto<C: Crypto + 'static>(crypto: C) -> bool {
15 CRYPTO.set(Box::new(crypto)).is_ok()
16}
17
18pub fn crypto() -> &'static dyn Crypto {
20 CRYPTO.get_or_init(|| Box::new(DefaultCrypto)).as_ref()
21}
22
23pub type EthPrecompileResult = Result<EthPrecompileOutput, PrecompileHalt>;
27
28pub type PrecompileResult = Result<PrecompileOutput, PrecompileError>;
33
34#[derive(Clone, Debug, PartialEq, Eq, Hash)]
39pub struct EthPrecompileOutput {
40 pub gas_used: u64,
42 pub bytes: Bytes,
44}
45
46impl EthPrecompileOutput {
47 pub const fn new(gas_used: u64, bytes: Bytes) -> Self {
49 Self { gas_used, bytes }
50 }
51}
52
53#[derive(Clone, Debug, PartialEq, Eq, Hash)]
55pub enum PrecompileStatus {
56 Success,
58 Revert,
60 Halt(PrecompileHalt),
62}
63
64impl PrecompileStatus {
65 #[inline]
67 pub const fn is_success_or_revert(&self) -> bool {
68 matches!(self, PrecompileStatus::Success | PrecompileStatus::Revert)
69 }
70
71 #[inline]
73 pub const fn is_revert_or_halt(&self) -> bool {
74 matches!(self, PrecompileStatus::Revert | PrecompileStatus::Halt(_))
75 }
76
77 #[inline]
79 pub const fn halt_reason(&self) -> Option<&PrecompileHalt> {
80 match &self {
81 PrecompileStatus::Halt(reason) => Some(reason),
82 _ => None,
83 }
84 }
85
86 #[inline]
88 pub const fn is_success(&self) -> bool {
89 matches!(self, PrecompileStatus::Success)
90 }
91
92 #[inline]
94 pub const fn is_revert(&self) -> bool {
95 matches!(self, PrecompileStatus::Revert)
96 }
97
98 #[inline]
100 pub const fn is_halt(&self) -> bool {
101 matches!(self, PrecompileStatus::Halt(_))
102 }
103}
104
105#[derive(Clone, Debug, PartialEq, Eq, Hash)]
110pub struct PrecompileOutput {
111 pub status: PrecompileStatus,
113 pub gas_used: u64,
115 pub gas_refunded: i64,
117 pub state_gas_used: u64,
119 pub reservoir: u64,
121 pub refill_amount: u64,
124 pub bytes: Bytes,
126}
127
128impl PrecompileOutput {
129 pub fn from_eth_result(result: EthPrecompileResult, reservoir: u64) -> Self {
131 match result {
132 Ok(output) => Self::new(output.gas_used, output.bytes, reservoir),
133 Err(halt) => Self::halt(halt, reservoir),
134 }
135 }
136 pub const fn new(gas_used: u64, bytes: Bytes, reservoir: u64) -> Self {
138 Self {
139 status: PrecompileStatus::Success,
140 gas_used,
141 gas_refunded: 0,
142 state_gas_used: 0,
143 reservoir,
144 refill_amount: 0,
145 bytes,
146 }
147 }
148
149 pub const fn halt(reason: PrecompileHalt, reservoir: u64) -> Self {
151 Self {
152 status: PrecompileStatus::Halt(reason),
153 gas_used: 0,
154 gas_refunded: 0,
155 state_gas_used: 0,
156 reservoir,
157 refill_amount: 0,
158 bytes: Bytes::new(),
159 }
160 }
161
162 pub const fn revert(gas_used: u64, bytes: Bytes, reservoir: u64) -> Self {
164 Self {
165 status: PrecompileStatus::Revert,
166 gas_used,
167 gas_refunded: 0,
168 state_gas_used: 0,
169 reservoir,
170 refill_amount: 0,
171 bytes,
172 }
173 }
174
175 pub const fn is_success(&self) -> bool {
177 matches!(self.status, PrecompileStatus::Success)
178 }
179
180 #[deprecated(note = "use `is_success` instead")]
182 pub const fn is_ok(&self) -> bool {
183 self.is_success()
184 }
185
186 pub const fn is_revert(&self) -> bool {
188 matches!(self.status, PrecompileStatus::Revert)
189 }
190
191 pub const fn is_halt(&self) -> bool {
193 matches!(self.status, PrecompileStatus::Halt(_))
194 }
195
196 #[inline]
198 pub const fn halt_reason(&self) -> Option<&PrecompileHalt> {
199 self.status.halt_reason()
200 }
201}
202
203pub trait Crypto: Send + Sync + Debug {
205 #[inline]
207 fn sha256(&self, input: &[u8]) -> [u8; 32] {
208 use sha2::Digest;
209 let output = sha2::Sha256::digest(input);
210 output.into()
211 }
212
213 #[inline]
215 fn ripemd160(&self, input: &[u8]) -> [u8; 32] {
216 use ripemd::Digest;
217 let mut hasher = ripemd::Ripemd160::new();
218 hasher.update(input);
219
220 let mut output = [0u8; 32];
221 hasher.finalize_into((&mut output[12..]).into());
222 output
223 }
224
225 #[inline]
227 fn bn254_g1_add(&self, p1: &[u8], p2: &[u8]) -> Result<[u8; 64], PrecompileHalt> {
228 crate::bn254::crypto_backend::g1_point_add(p1, p2)
229 }
230
231 #[inline]
233 fn bn254_g1_mul(&self, point: &[u8], scalar: &[u8]) -> Result<[u8; 64], PrecompileHalt> {
234 crate::bn254::crypto_backend::g1_point_mul(point, scalar)
235 }
236
237 #[inline]
239 fn bn254_pairing_check(&self, pairs: &[(&[u8], &[u8])]) -> Result<bool, PrecompileHalt> {
240 crate::bn254::crypto_backend::pairing_check(pairs)
241 }
242
243 #[inline]
245 fn secp256k1_ecrecover(
246 &self,
247 sig: &[u8; 64],
248 recid: u8,
249 msg: &[u8; 32],
250 ) -> Result<[u8; 32], PrecompileHalt> {
251 crate::secp256k1::ecrecover_bytes(sig, recid, msg)
252 .ok_or(PrecompileHalt::Secp256k1RecoverFailed)
253 }
254
255 #[inline]
257 fn modexp(&self, base: &[u8], exp: &[u8], modulus: &[u8]) -> Result<Vec<u8>, PrecompileHalt> {
258 Ok(crate::modexp::modexp(base, exp, modulus))
259 }
260
261 #[inline]
263 fn blake2_compress(&self, rounds: u32, h: &mut [u64; 8], m: &[u64; 16], t: &[u64; 2], f: bool) {
264 crate::blake2::compress(rounds, h, m, t, f);
265 }
266
267 #[inline]
269 fn secp256r1_verify_signature(&self, msg: &[u8; 32], sig: &[u8; 64], pk: &[u8; 64]) -> bool {
270 crate::secp256r1::verify_signature(msg, sig, pk).is_some()
271 }
272
273 #[inline]
275 fn verify_kzg_proof(
276 &self,
277 z: &[u8; 32],
278 y: &[u8; 32],
279 commitment: &[u8; 48],
280 proof: &[u8; 48],
281 ) -> Result<(), PrecompileHalt> {
282 if !crate::kzg_point_evaluation::verify_kzg_proof(commitment, z, y, proof) {
283 return Err(PrecompileHalt::BlobVerifyKzgProofFailed);
284 }
285
286 Ok(())
287 }
288
289 fn bls12_381_g1_add(&self, a: G1Point, b: G1Point) -> Result<[u8; 96], PrecompileHalt> {
291 crate::bls12_381::crypto_backend::p1_add_affine_bytes(a, b)
292 }
293
294 fn bls12_381_g1_msm(
296 &self,
297 pairs: &mut dyn Iterator<Item = Result<G1PointScalar, PrecompileHalt>>,
298 ) -> Result<[u8; 96], PrecompileHalt> {
299 crate::bls12_381::crypto_backend::p1_msm_bytes(pairs)
300 }
301
302 fn bls12_381_g2_add(&self, a: G2Point, b: G2Point) -> Result<[u8; 192], PrecompileHalt> {
304 crate::bls12_381::crypto_backend::p2_add_affine_bytes(a, b)
305 }
306
307 fn bls12_381_g2_msm(
309 &self,
310 pairs: &mut dyn Iterator<Item = Result<G2PointScalar, PrecompileHalt>>,
311 ) -> Result<[u8; 192], PrecompileHalt> {
312 crate::bls12_381::crypto_backend::p2_msm_bytes(pairs)
313 }
314
315 fn bls12_381_pairing_check(
317 &self,
318 pairs: &[(G1Point, G2Point)],
319 ) -> Result<bool, PrecompileHalt> {
320 crate::bls12_381::crypto_backend::pairing_check_bytes(pairs)
321 }
322
323 fn bls12_381_fp_to_g1(&self, fp: &[u8; 48]) -> Result<[u8; 96], PrecompileHalt> {
325 crate::bls12_381::crypto_backend::map_fp_to_g1_bytes(fp)
326 }
327
328 fn bls12_381_fp2_to_g2(&self, fp2: ([u8; 48], [u8; 48])) -> Result<[u8; 192], PrecompileHalt> {
330 crate::bls12_381::crypto_backend::map_fp2_to_g2_bytes(&fp2.0, &fp2.1)
331 }
332}
333
334pub type PrecompileEthFn = fn(&[u8], u64) -> EthPrecompileResult;
339
340pub type PrecompileFn = fn(&[u8], u64, u64) -> PrecompileResult;
345
346#[macro_export]
359macro_rules! eth_precompile_fn {
360 ($name:ident, $eth_fn:expr) => {
361 fn $name(input: &[u8], gas_limit: u64, reservoir: u64) -> $crate::PrecompileResult {
362 Ok($crate::call_eth_precompile(
363 $eth_fn, input, gas_limit, reservoir,
364 ))
365 }
366 };
367}
368
369#[inline]
378pub fn call_eth_precompile(
379 f: PrecompileEthFn,
380 input: &[u8],
381 gas_limit: u64,
382 reservoir: u64,
383) -> PrecompileOutput {
384 match f(input, gas_limit) {
385 Ok(output) => PrecompileOutput::new(output.gas_used, output.bytes, reservoir),
386 Err(halt) => PrecompileOutput::halt(halt, reservoir),
387 }
388}
389
390#[derive(Clone, Debug, PartialEq, Eq, Hash)]
396pub enum PrecompileHalt {
397 OutOfGas,
399 Blake2WrongLength,
401 Blake2WrongFinalIndicatorFlag,
403 ModexpExpOverflow,
405 ModexpBaseOverflow,
407 ModexpModOverflow,
409 ModexpEip7823LimitSize,
411 Bn254FieldPointNotAMember,
413 Bn254AffineGFailedToCreate,
415 Bn254PairLength,
417 BlobInvalidInputLength,
420 BlobMismatchedVersion,
422 BlobVerifyKzgProofFailed,
424 NonCanonicalFp,
426 Bls12381G1NotOnCurve,
428 Bls12381G1NotInSubgroup,
430 Bls12381G2NotOnCurve,
432 Bls12381G2NotInSubgroup,
434 Bls12381ScalarInputLength,
436 Bls12381G1AddInputLength,
438 Bls12381G1MsmInputLength,
440 Bls12381G2AddInputLength,
442 Bls12381G2MsmInputLength,
444 Bls12381PairingInputLength,
446 Bls12381MapFpToG1InputLength,
448 Bls12381MapFp2ToG2InputLength,
450 Bls12381FpPaddingInvalid,
452 Bls12381FpPaddingLength,
454 Bls12381G1PaddingLength,
456 Bls12381G2PaddingLength,
458 KzgInvalidG1Point,
460 KzgG1PointNotOnCurve,
462 KzgG1PointNotInSubgroup,
464 KzgInvalidInputLength,
466 Secp256k1RecoverFailed,
468 Other(Cow<'static, str>),
470}
471
472impl PrecompileHalt {
473 pub fn other(err: impl Into<String>) -> Self {
475 Self::Other(Cow::Owned(err.into()))
476 }
477
478 pub const fn other_static(err: &'static str) -> Self {
480 Self::Other(Cow::Borrowed(err))
481 }
482
483 pub const fn is_oog(&self) -> bool {
485 matches!(self, Self::OutOfGas)
486 }
487}
488
489impl core::error::Error for PrecompileHalt {}
490
491impl fmt::Display for PrecompileHalt {
492 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
493 let s = match self {
494 Self::OutOfGas => "out of gas",
495 Self::Blake2WrongLength => "wrong input length for blake2",
496 Self::Blake2WrongFinalIndicatorFlag => "wrong final indicator flag for blake2",
497 Self::ModexpExpOverflow => "modexp exp overflow",
498 Self::ModexpBaseOverflow => "modexp base overflow",
499 Self::ModexpModOverflow => "modexp mod overflow",
500 Self::ModexpEip7823LimitSize => "Modexp limit all input sizes.",
501 Self::Bn254FieldPointNotAMember => "field point not a member of bn254 curve",
502 Self::Bn254AffineGFailedToCreate => "failed to create affine g point for bn254 curve",
503 Self::Bn254PairLength => "bn254 invalid pair length",
504 Self::BlobInvalidInputLength => "invalid blob input length",
505 Self::BlobMismatchedVersion => "mismatched blob version",
506 Self::BlobVerifyKzgProofFailed => "verifying blob kzg proof failed",
507 Self::NonCanonicalFp => "non-canonical field element",
508 Self::Bls12381G1NotOnCurve => "bls12-381 g1 point not on curve",
509 Self::Bls12381G1NotInSubgroup => "bls12-381 g1 point not in correct subgroup",
510 Self::Bls12381G2NotOnCurve => "bls12-381 g2 point not on curve",
511 Self::Bls12381G2NotInSubgroup => "bls12-381 g2 point not in correct subgroup",
512 Self::Bls12381ScalarInputLength => "bls12-381 scalar input length error",
513 Self::Bls12381G1AddInputLength => "bls12-381 g1 add input length error",
514 Self::Bls12381G1MsmInputLength => "bls12-381 g1 msm input length error",
515 Self::Bls12381G2AddInputLength => "bls12-381 g2 add input length error",
516 Self::Bls12381G2MsmInputLength => "bls12-381 g2 msm input length error",
517 Self::Bls12381PairingInputLength => "bls12-381 pairing input length error",
518 Self::Bls12381MapFpToG1InputLength => "bls12-381 map fp to g1 input length error",
519 Self::Bls12381MapFp2ToG2InputLength => "bls12-381 map fp2 to g2 input length error",
520 Self::Bls12381FpPaddingInvalid => "bls12-381 fp 64 top bytes of input are not zero",
521 Self::Bls12381FpPaddingLength => "bls12-381 fp padding length error",
522 Self::Bls12381G1PaddingLength => "bls12-381 g1 padding length error",
523 Self::Bls12381G2PaddingLength => "bls12-381 g2 padding length error",
524 Self::KzgInvalidG1Point => "kzg invalid g1 point",
525 Self::KzgG1PointNotOnCurve => "kzg g1 point not on curve",
526 Self::KzgG1PointNotInSubgroup => "kzg g1 point not in correct subgroup",
527 Self::KzgInvalidInputLength => "kzg invalid input length",
528 Self::Secp256k1RecoverFailed => "secp256k1 signature recovery failed",
529 Self::Other(s) => s,
530 };
531 f.write_str(s)
532 }
533}
534
535#[derive(Clone, Debug, PartialEq, Eq, Hash)]
543pub enum PrecompileError {
544 Fatal(String),
546 FatalAny(AnyError),
548}
549
550impl PrecompileError {
551 pub const fn is_fatal(&self) -> bool {
553 true
554 }
555}
556
557impl core::error::Error for PrecompileError {}
558
559impl fmt::Display for PrecompileError {
560 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
561 match self {
562 Self::Fatal(s) => write!(f, "fatal: {s}"),
563 Self::FatalAny(s) => write!(f, "fatal: {s}"),
564 }
565 }
566}
567
568#[derive(Clone, Debug)]
570pub struct DefaultCrypto;
571
572impl Crypto for DefaultCrypto {}