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 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 hasher.finalize_into((&mut output[12..]).into());
216 output
217 }
218
219 #[inline]
221 fn bn254_g1_add(&self, p1: &[u8], p2: &[u8]) -> Result<[u8; 64], PrecompileHalt> {
222 crate::bn254::crypto_backend::g1_point_add(p1, p2)
223 }
224
225 #[inline]
227 fn bn254_g1_mul(&self, point: &[u8], scalar: &[u8]) -> Result<[u8; 64], PrecompileHalt> {
228 crate::bn254::crypto_backend::g1_point_mul(point, scalar)
229 }
230
231 #[inline]
233 fn bn254_pairing_check(&self, pairs: &[(&[u8], &[u8])]) -> Result<bool, PrecompileHalt> {
234 crate::bn254::crypto_backend::pairing_check(pairs)
235 }
236
237 #[inline]
239 fn secp256k1_ecrecover(
240 &self,
241 sig: &[u8; 64],
242 recid: u8,
243 msg: &[u8; 32],
244 ) -> Result<[u8; 32], PrecompileHalt> {
245 crate::secp256k1::ecrecover_bytes(sig, recid, msg)
246 .ok_or(PrecompileHalt::Secp256k1RecoverFailed)
247 }
248
249 #[inline]
251 fn modexp(&self, base: &[u8], exp: &[u8], modulus: &[u8]) -> Result<Vec<u8>, PrecompileHalt> {
252 Ok(crate::modexp::modexp(base, exp, modulus))
253 }
254
255 #[inline]
257 fn blake2_compress(&self, rounds: u32, h: &mut [u64; 8], m: &[u64; 16], t: &[u64; 2], f: bool) {
258 crate::blake2::algo::compress(rounds as usize, h, m, t, f);
259 }
260
261 #[inline]
263 fn secp256r1_verify_signature(&self, msg: &[u8; 32], sig: &[u8; 64], pk: &[u8; 64]) -> bool {
264 crate::secp256r1::verify_signature(msg, sig, pk).is_some()
265 }
266
267 #[inline]
269 fn verify_kzg_proof(
270 &self,
271 z: &[u8; 32],
272 y: &[u8; 32],
273 commitment: &[u8; 48],
274 proof: &[u8; 48],
275 ) -> Result<(), PrecompileHalt> {
276 if !crate::kzg_point_evaluation::verify_kzg_proof(commitment, z, y, proof) {
277 return Err(PrecompileHalt::BlobVerifyKzgProofFailed);
278 }
279
280 Ok(())
281 }
282
283 fn bls12_381_g1_add(&self, a: G1Point, b: G1Point) -> Result<[u8; 96], PrecompileHalt> {
285 crate::bls12_381::crypto_backend::p1_add_affine_bytes(a, b)
286 }
287
288 fn bls12_381_g1_msm(
290 &self,
291 pairs: &mut dyn Iterator<Item = Result<G1PointScalar, PrecompileHalt>>,
292 ) -> Result<[u8; 96], PrecompileHalt> {
293 crate::bls12_381::crypto_backend::p1_msm_bytes(pairs)
294 }
295
296 fn bls12_381_g2_add(&self, a: G2Point, b: G2Point) -> Result<[u8; 192], PrecompileHalt> {
298 crate::bls12_381::crypto_backend::p2_add_affine_bytes(a, b)
299 }
300
301 fn bls12_381_g2_msm(
303 &self,
304 pairs: &mut dyn Iterator<Item = Result<G2PointScalar, PrecompileHalt>>,
305 ) -> Result<[u8; 192], PrecompileHalt> {
306 crate::bls12_381::crypto_backend::p2_msm_bytes(pairs)
307 }
308
309 fn bls12_381_pairing_check(
311 &self,
312 pairs: &[(G1Point, G2Point)],
313 ) -> Result<bool, PrecompileHalt> {
314 crate::bls12_381::crypto_backend::pairing_check_bytes(pairs)
315 }
316
317 fn bls12_381_fp_to_g1(&self, fp: &[u8; 48]) -> Result<[u8; 96], PrecompileHalt> {
319 crate::bls12_381::crypto_backend::map_fp_to_g1_bytes(fp)
320 }
321
322 fn bls12_381_fp2_to_g2(&self, fp2: ([u8; 48], [u8; 48])) -> Result<[u8; 192], PrecompileHalt> {
324 crate::bls12_381::crypto_backend::map_fp2_to_g2_bytes(&fp2.0, &fp2.1)
325 }
326}
327
328pub type PrecompileEthFn = fn(&[u8], u64) -> EthPrecompileResult;
333
334pub type PrecompileFn = fn(&[u8], u64, u64) -> PrecompileResult;
339
340#[macro_export]
353macro_rules! eth_precompile_fn {
354 ($name:ident, $eth_fn:expr) => {
355 fn $name(input: &[u8], gas_limit: u64, reservoir: u64) -> $crate::PrecompileResult {
356 Ok($crate::call_eth_precompile(
357 $eth_fn, input, gas_limit, reservoir,
358 ))
359 }
360 };
361}
362
363#[inline]
372pub fn call_eth_precompile(
373 f: PrecompileEthFn,
374 input: &[u8],
375 gas_limit: u64,
376 reservoir: u64,
377) -> PrecompileOutput {
378 match f(input, gas_limit) {
379 Ok(output) => PrecompileOutput::new(output.gas_used, output.bytes, reservoir),
380 Err(halt) => PrecompileOutput::halt(halt, reservoir),
381 }
382}
383
384#[derive(Clone, Debug, PartialEq, Eq, Hash)]
390pub enum PrecompileHalt {
391 OutOfGas,
393 Blake2WrongLength,
395 Blake2WrongFinalIndicatorFlag,
397 ModexpExpOverflow,
399 ModexpBaseOverflow,
401 ModexpModOverflow,
403 ModexpEip7823LimitSize,
405 Bn254FieldPointNotAMember,
407 Bn254AffineGFailedToCreate,
409 Bn254PairLength,
411 BlobInvalidInputLength,
414 BlobMismatchedVersion,
416 BlobVerifyKzgProofFailed,
418 NonCanonicalFp,
420 Bls12381G1NotOnCurve,
422 Bls12381G1NotInSubgroup,
424 Bls12381G2NotOnCurve,
426 Bls12381G2NotInSubgroup,
428 Bls12381ScalarInputLength,
430 Bls12381G1AddInputLength,
432 Bls12381G1MsmInputLength,
434 Bls12381G2AddInputLength,
436 Bls12381G2MsmInputLength,
438 Bls12381PairingInputLength,
440 Bls12381MapFpToG1InputLength,
442 Bls12381MapFp2ToG2InputLength,
444 Bls12381FpPaddingInvalid,
446 Bls12381FpPaddingLength,
448 Bls12381G1PaddingLength,
450 Bls12381G2PaddingLength,
452 KzgInvalidG1Point,
454 KzgG1PointNotOnCurve,
456 KzgG1PointNotInSubgroup,
458 KzgInvalidInputLength,
460 Secp256k1RecoverFailed,
462 Other(Cow<'static, str>),
464}
465
466impl PrecompileHalt {
467 pub fn other(err: impl Into<String>) -> Self {
469 Self::Other(Cow::Owned(err.into()))
470 }
471
472 pub const fn other_static(err: &'static str) -> Self {
474 Self::Other(Cow::Borrowed(err))
475 }
476
477 pub const fn is_oog(&self) -> bool {
479 matches!(self, Self::OutOfGas)
480 }
481}
482
483impl core::error::Error for PrecompileHalt {}
484
485impl fmt::Display for PrecompileHalt {
486 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
487 let s = match self {
488 Self::OutOfGas => "out of gas",
489 Self::Blake2WrongLength => "wrong input length for blake2",
490 Self::Blake2WrongFinalIndicatorFlag => "wrong final indicator flag for blake2",
491 Self::ModexpExpOverflow => "modexp exp overflow",
492 Self::ModexpBaseOverflow => "modexp base overflow",
493 Self::ModexpModOverflow => "modexp mod overflow",
494 Self::ModexpEip7823LimitSize => "Modexp limit all input sizes.",
495 Self::Bn254FieldPointNotAMember => "field point not a member of bn254 curve",
496 Self::Bn254AffineGFailedToCreate => "failed to create affine g point for bn254 curve",
497 Self::Bn254PairLength => "bn254 invalid pair length",
498 Self::BlobInvalidInputLength => "invalid blob input length",
499 Self::BlobMismatchedVersion => "mismatched blob version",
500 Self::BlobVerifyKzgProofFailed => "verifying blob kzg proof failed",
501 Self::NonCanonicalFp => "non-canonical field element",
502 Self::Bls12381G1NotOnCurve => "bls12-381 g1 point not on curve",
503 Self::Bls12381G1NotInSubgroup => "bls12-381 g1 point not in correct subgroup",
504 Self::Bls12381G2NotOnCurve => "bls12-381 g2 point not on curve",
505 Self::Bls12381G2NotInSubgroup => "bls12-381 g2 point not in correct subgroup",
506 Self::Bls12381ScalarInputLength => "bls12-381 scalar input length error",
507 Self::Bls12381G1AddInputLength => "bls12-381 g1 add input length error",
508 Self::Bls12381G1MsmInputLength => "bls12-381 g1 msm input length error",
509 Self::Bls12381G2AddInputLength => "bls12-381 g2 add input length error",
510 Self::Bls12381G2MsmInputLength => "bls12-381 g2 msm input length error",
511 Self::Bls12381PairingInputLength => "bls12-381 pairing input length error",
512 Self::Bls12381MapFpToG1InputLength => "bls12-381 map fp to g1 input length error",
513 Self::Bls12381MapFp2ToG2InputLength => "bls12-381 map fp2 to g2 input length error",
514 Self::Bls12381FpPaddingInvalid => "bls12-381 fp 64 top bytes of input are not zero",
515 Self::Bls12381FpPaddingLength => "bls12-381 fp padding length error",
516 Self::Bls12381G1PaddingLength => "bls12-381 g1 padding length error",
517 Self::Bls12381G2PaddingLength => "bls12-381 g2 padding length error",
518 Self::KzgInvalidG1Point => "kzg invalid g1 point",
519 Self::KzgG1PointNotOnCurve => "kzg g1 point not on curve",
520 Self::KzgG1PointNotInSubgroup => "kzg g1 point not in correct subgroup",
521 Self::KzgInvalidInputLength => "kzg invalid input length",
522 Self::Secp256k1RecoverFailed => "secp256k1 signature recovery failed",
523 Self::Other(s) => s,
524 };
525 f.write_str(s)
526 }
527}
528
529#[derive(Clone, Debug, PartialEq, Eq, Hash)]
537pub enum PrecompileError {
538 Fatal(String),
540 FatalAny(AnyError),
542}
543
544impl PrecompileError {
545 pub const fn is_fatal(&self) -> bool {
547 true
548 }
549}
550
551impl core::error::Error for PrecompileError {}
552
553impl fmt::Display for PrecompileError {
554 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
555 match self {
556 Self::Fatal(s) => write!(f, "fatal: {s}"),
557 Self::FatalAny(s) => write!(f, "fatal: {s}"),
558 }
559 }
560}
561
562#[derive(Clone, Debug)]
564pub struct DefaultCrypto;
565
566impl Crypto for DefaultCrypto {}