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: i64,
119 pub reservoir: u64,
121 pub bytes: Bytes,
123}
124
125impl PrecompileOutput {
126 pub fn from_eth_result(result: EthPrecompileResult, reservoir: u64) -> Self {
128 match result {
129 Ok(output) => Self::new(output.gas_used, output.bytes, reservoir),
130 Err(halt) => Self::halt(halt, reservoir),
131 }
132 }
133 pub const fn new(gas_used: u64, bytes: Bytes, reservoir: u64) -> Self {
135 Self {
136 status: PrecompileStatus::Success,
137 gas_used,
138 gas_refunded: 0,
139 state_gas_used: 0,
140 reservoir,
141 bytes,
142 }
143 }
144
145 pub const fn halt(reason: PrecompileHalt, reservoir: u64) -> Self {
147 Self {
148 status: PrecompileStatus::Halt(reason),
149 gas_used: 0,
150 gas_refunded: 0,
151 state_gas_used: 0,
152 reservoir,
153 bytes: Bytes::new(),
154 }
155 }
156
157 pub const fn revert(gas_used: u64, bytes: Bytes, reservoir: u64) -> Self {
159 Self {
160 status: PrecompileStatus::Revert,
161 gas_used,
162 gas_refunded: 0,
163 state_gas_used: 0,
164 reservoir,
165 bytes,
166 }
167 }
168
169 pub const fn is_success(&self) -> bool {
171 matches!(self.status, PrecompileStatus::Success)
172 }
173
174 #[deprecated(note = "use `is_success` instead")]
176 pub const fn is_ok(&self) -> bool {
177 self.is_success()
178 }
179
180 pub const fn is_revert(&self) -> bool {
182 matches!(self.status, PrecompileStatus::Revert)
183 }
184
185 pub const fn is_halt(&self) -> bool {
187 matches!(self.status, PrecompileStatus::Halt(_))
188 }
189
190 #[inline]
192 pub const fn halt_reason(&self) -> Option<&PrecompileHalt> {
193 self.status.halt_reason()
194 }
195}
196
197pub trait Crypto: Send + Sync + Debug {
199 #[inline]
201 fn sha256(&self, input: &[u8]) -> [u8; 32] {
202 use sha2::Digest;
203 let output = sha2::Sha256::digest(input);
204 output.into()
205 }
206
207 #[inline]
209 fn ripemd160(&self, input: &[u8]) -> [u8; 32] {
210 use ripemd::Digest;
211 let mut hasher = ripemd::Ripemd160::new();
212 hasher.update(input);
213
214 let mut output = [0u8; 32];
215 let hash: &mut [u8; 20] = (&mut output[12..]).try_into().unwrap();
216 hasher.finalize_into(hash.into());
217 output
218 }
219
220 #[inline]
222 fn bn254_g1_add(&self, p1: &[u8], p2: &[u8]) -> Result<[u8; 64], PrecompileHalt> {
223 crate::bn254::crypto_backend::g1_point_add(p1, p2)
224 }
225
226 #[inline]
228 fn bn254_g1_mul(&self, point: &[u8], scalar: &[u8]) -> Result<[u8; 64], PrecompileHalt> {
229 crate::bn254::crypto_backend::g1_point_mul(point, scalar)
230 }
231
232 #[inline]
234 fn bn254_pairing_check(&self, pairs: &[(&[u8], &[u8])]) -> Result<bool, PrecompileHalt> {
235 crate::bn254::crypto_backend::pairing_check(pairs)
236 }
237
238 #[inline]
240 fn secp256k1_ecrecover(
241 &self,
242 sig: &[u8; 64],
243 recid: u8,
244 msg: &[u8; 32],
245 ) -> Result<[u8; 32], PrecompileHalt> {
246 crate::secp256k1::ecrecover_bytes(sig, recid, msg)
247 .ok_or(PrecompileHalt::Secp256k1RecoverFailed)
248 }
249
250 #[inline]
252 fn modexp(&self, base: &[u8], exp: &[u8], modulus: &[u8]) -> Result<Vec<u8>, PrecompileHalt> {
253 Ok(crate::modexp::modexp(base, exp, modulus))
254 }
255
256 #[inline]
258 fn blake2_compress(&self, rounds: u32, h: &mut [u64; 8], m: &[u64; 16], t: &[u64; 2], f: bool) {
259 crate::blake2::compress(rounds, h, m, t, f);
260 }
261
262 #[inline]
264 fn secp256r1_verify_signature(&self, msg: &[u8; 32], sig: &[u8; 64], pk: &[u8; 64]) -> bool {
265 crate::secp256r1::verify_signature(msg, sig, pk).is_some()
266 }
267
268 #[inline]
270 fn verify_kzg_proof(
271 &self,
272 z: &[u8; 32],
273 y: &[u8; 32],
274 commitment: &[u8; 48],
275 proof: &[u8; 48],
276 ) -> Result<(), PrecompileHalt> {
277 if !crate::kzg_point_evaluation::verify_kzg_proof(commitment, z, y, proof) {
278 return Err(PrecompileHalt::BlobVerifyKzgProofFailed);
279 }
280
281 Ok(())
282 }
283
284 fn bls12_381_g1_add(&self, a: G1Point, b: G1Point) -> Result<[u8; 96], PrecompileHalt> {
286 crate::bls12_381::crypto_backend::p1_add_affine_bytes(a, b)
287 }
288
289 fn bls12_381_g1_msm(
291 &self,
292 pairs: &mut dyn Iterator<Item = Result<G1PointScalar, PrecompileHalt>>,
293 ) -> Result<[u8; 96], PrecompileHalt> {
294 crate::bls12_381::crypto_backend::p1_msm_bytes(pairs)
295 }
296
297 fn bls12_381_g2_add(&self, a: G2Point, b: G2Point) -> Result<[u8; 192], PrecompileHalt> {
299 crate::bls12_381::crypto_backend::p2_add_affine_bytes(a, b)
300 }
301
302 fn bls12_381_g2_msm(
304 &self,
305 pairs: &mut dyn Iterator<Item = Result<G2PointScalar, PrecompileHalt>>,
306 ) -> Result<[u8; 192], PrecompileHalt> {
307 crate::bls12_381::crypto_backend::p2_msm_bytes(pairs)
308 }
309
310 fn bls12_381_pairing_check(
312 &self,
313 pairs: &[(G1Point, G2Point)],
314 ) -> Result<bool, PrecompileHalt> {
315 crate::bls12_381::crypto_backend::pairing_check_bytes(pairs)
316 }
317
318 fn bls12_381_fp_to_g1(&self, fp: &[u8; 48]) -> Result<[u8; 96], PrecompileHalt> {
320 crate::bls12_381::crypto_backend::map_fp_to_g1_bytes(fp)
321 }
322
323 fn bls12_381_fp2_to_g2(&self, fp2: ([u8; 48], [u8; 48])) -> Result<[u8; 192], PrecompileHalt> {
325 crate::bls12_381::crypto_backend::map_fp2_to_g2_bytes(&fp2.0, &fp2.1)
326 }
327}
328
329pub type PrecompileEthFn = fn(&[u8], u64) -> EthPrecompileResult;
334
335pub type PrecompileFn = fn(&[u8], u64, u64) -> PrecompileResult;
340
341#[macro_export]
354macro_rules! eth_precompile_fn {
355 ($name:ident, $eth_fn:expr) => {
356 fn $name(input: &[u8], gas_limit: u64, reservoir: u64) -> $crate::PrecompileResult {
357 Ok($crate::call_eth_precompile(
358 $eth_fn, input, gas_limit, reservoir,
359 ))
360 }
361 };
362}
363
364#[inline]
373pub fn call_eth_precompile(
374 f: PrecompileEthFn,
375 input: &[u8],
376 gas_limit: u64,
377 reservoir: u64,
378) -> PrecompileOutput {
379 match f(input, gas_limit) {
380 Ok(output) => PrecompileOutput::new(output.gas_used, output.bytes, reservoir),
381 Err(halt) => PrecompileOutput::halt(halt, reservoir),
382 }
383}
384
385#[derive(Clone, Debug, PartialEq, Eq, Hash)]
391pub enum PrecompileHalt {
392 OutOfGas,
394 Blake2WrongLength,
396 Blake2WrongFinalIndicatorFlag,
398 ModexpExpOverflow,
400 ModexpBaseOverflow,
402 ModexpModOverflow,
404 ModexpEip7823LimitSize,
406 Bn254FieldPointNotAMember,
408 Bn254AffineGFailedToCreate,
410 Bn254PairLength,
412 BlobInvalidInputLength,
415 BlobMismatchedVersion,
417 BlobVerifyKzgProofFailed,
419 NonCanonicalFp,
421 Bls12381G1NotOnCurve,
423 Bls12381G1NotInSubgroup,
425 Bls12381G2NotOnCurve,
427 Bls12381G2NotInSubgroup,
429 Bls12381ScalarInputLength,
431 Bls12381G1AddInputLength,
433 Bls12381G1MsmInputLength,
435 Bls12381G2AddInputLength,
437 Bls12381G2MsmInputLength,
439 Bls12381PairingInputLength,
441 Bls12381MapFpToG1InputLength,
443 Bls12381MapFp2ToG2InputLength,
445 Bls12381FpPaddingInvalid,
447 Bls12381FpPaddingLength,
449 Bls12381G1PaddingLength,
451 Bls12381G2PaddingLength,
453 KzgInvalidG1Point,
455 KzgG1PointNotOnCurve,
457 KzgG1PointNotInSubgroup,
459 KzgInvalidInputLength,
461 Secp256k1RecoverFailed,
463 Other(Cow<'static, str>),
465}
466
467impl PrecompileHalt {
468 pub fn other(err: impl Into<String>) -> Self {
470 Self::Other(Cow::Owned(err.into()))
471 }
472
473 pub const fn other_static(err: &'static str) -> Self {
475 Self::Other(Cow::Borrowed(err))
476 }
477
478 pub const fn is_oog(&self) -> bool {
480 matches!(self, Self::OutOfGas)
481 }
482}
483
484impl core::error::Error for PrecompileHalt {}
485
486impl fmt::Display for PrecompileHalt {
487 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
488 let s = match self {
489 Self::OutOfGas => "out of gas",
490 Self::Blake2WrongLength => "wrong input length for blake2",
491 Self::Blake2WrongFinalIndicatorFlag => "wrong final indicator flag for blake2",
492 Self::ModexpExpOverflow => "modexp exp overflow",
493 Self::ModexpBaseOverflow => "modexp base overflow",
494 Self::ModexpModOverflow => "modexp mod overflow",
495 Self::ModexpEip7823LimitSize => "Modexp limit all input sizes.",
496 Self::Bn254FieldPointNotAMember => "field point not a member of bn254 curve",
497 Self::Bn254AffineGFailedToCreate => "failed to create affine g point for bn254 curve",
498 Self::Bn254PairLength => "bn254 invalid pair length",
499 Self::BlobInvalidInputLength => "invalid blob input length",
500 Self::BlobMismatchedVersion => "mismatched blob version",
501 Self::BlobVerifyKzgProofFailed => "verifying blob kzg proof failed",
502 Self::NonCanonicalFp => "non-canonical field element",
503 Self::Bls12381G1NotOnCurve => "bls12-381 g1 point not on curve",
504 Self::Bls12381G1NotInSubgroup => "bls12-381 g1 point not in correct subgroup",
505 Self::Bls12381G2NotOnCurve => "bls12-381 g2 point not on curve",
506 Self::Bls12381G2NotInSubgroup => "bls12-381 g2 point not in correct subgroup",
507 Self::Bls12381ScalarInputLength => "bls12-381 scalar input length error",
508 Self::Bls12381G1AddInputLength => "bls12-381 g1 add input length error",
509 Self::Bls12381G1MsmInputLength => "bls12-381 g1 msm input length error",
510 Self::Bls12381G2AddInputLength => "bls12-381 g2 add input length error",
511 Self::Bls12381G2MsmInputLength => "bls12-381 g2 msm input length error",
512 Self::Bls12381PairingInputLength => "bls12-381 pairing input length error",
513 Self::Bls12381MapFpToG1InputLength => "bls12-381 map fp to g1 input length error",
514 Self::Bls12381MapFp2ToG2InputLength => "bls12-381 map fp2 to g2 input length error",
515 Self::Bls12381FpPaddingInvalid => "bls12-381 fp 64 top bytes of input are not zero",
516 Self::Bls12381FpPaddingLength => "bls12-381 fp padding length error",
517 Self::Bls12381G1PaddingLength => "bls12-381 g1 padding length error",
518 Self::Bls12381G2PaddingLength => "bls12-381 g2 padding length error",
519 Self::KzgInvalidG1Point => "kzg invalid g1 point",
520 Self::KzgG1PointNotOnCurve => "kzg g1 point not on curve",
521 Self::KzgG1PointNotInSubgroup => "kzg g1 point not in correct subgroup",
522 Self::KzgInvalidInputLength => "kzg invalid input length",
523 Self::Secp256k1RecoverFailed => "secp256k1 signature recovery failed",
524 Self::Other(s) => s,
525 };
526 f.write_str(s)
527 }
528}
529
530#[derive(Clone, Debug, PartialEq, Eq, Hash)]
538pub enum PrecompileError {
539 Fatal(String),
541 FatalAny(AnyError),
543}
544
545impl PrecompileError {
546 pub const fn is_fatal(&self) -> bool {
548 true
549 }
550}
551
552impl core::error::Error for PrecompileError {}
553
554impl fmt::Display for PrecompileError {
555 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
556 match self {
557 Self::Fatal(s) => write!(f, "fatal: {s}"),
558 Self::FatalAny(s) => write!(f, "fatal: {s}"),
559 }
560 }
561}
562
563#[derive(Clone, Debug)]
565pub struct DefaultCrypto;
566
567impl Crypto for DefaultCrypto {}