use core::fmt::{self, Debug};
use primitives::{Bytes, OnceLock};
use std::{boxed::Box, string::String, vec::Vec};
use crate::bls12_381::{G1Point, G1PointScalar, G2Point, G2PointScalar};
static CRYPTO: OnceLock<Box<dyn Crypto>> = OnceLock::new();
pub fn install_crypto<C: Crypto + 'static>(crypto: C) -> bool {
CRYPTO.set(Box::new(crypto)).is_ok()
}
pub fn crypto() -> &'static dyn Crypto {
CRYPTO.get_or_init(|| Box::new(DefaultCrypto)).as_ref()
}
pub type PrecompileResult = Result<PrecompileOutput, PrecompileError>;
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct PrecompileOutput {
pub gas_used: u64,
pub bytes: Bytes,
pub reverted: bool,
}
impl PrecompileOutput {
pub fn new(gas_used: u64, bytes: Bytes) -> Self {
Self {
gas_used,
bytes,
reverted: false,
}
}
pub fn new_reverted(gas_used: u64, bytes: Bytes) -> Self {
Self {
gas_used,
bytes,
reverted: true,
}
}
pub fn reverted(mut self) -> Self {
self.reverted = true;
self
}
}
pub trait Crypto: Send + Sync + Debug {
#[inline]
fn sha256(&self, input: &[u8]) -> [u8; 32] {
use sha2::Digest;
let output = sha2::Sha256::digest(input);
output.into()
}
#[inline]
fn ripemd160(&self, input: &[u8]) -> [u8; 32] {
use ripemd::Digest;
let mut hasher = ripemd::Ripemd160::new();
hasher.update(input);
let mut output = [0u8; 32];
hasher.finalize_into((&mut output[12..]).into());
output
}
#[inline]
fn bn254_g1_add(&self, p1: &[u8], p2: &[u8]) -> Result<[u8; 64], PrecompileError> {
crate::bn254::crypto_backend::g1_point_add(p1, p2)
}
#[inline]
fn bn254_g1_mul(&self, point: &[u8], scalar: &[u8]) -> Result<[u8; 64], PrecompileError> {
crate::bn254::crypto_backend::g1_point_mul(point, scalar)
}
#[inline]
fn bn254_pairing_check(&self, pairs: &[(&[u8], &[u8])]) -> Result<bool, PrecompileError> {
crate::bn254::crypto_backend::pairing_check(pairs)
}
#[inline]
fn secp256k1_ecrecover(
&self,
sig: &[u8; 64],
recid: u8,
msg: &[u8; 32],
) -> Result<[u8; 32], PrecompileError> {
crate::secp256k1::ecrecover_bytes(*sig, recid, *msg)
.ok_or(PrecompileError::Secp256k1RecoverFailed)
}
#[inline]
fn modexp(&self, base: &[u8], exp: &[u8], modulus: &[u8]) -> Result<Vec<u8>, PrecompileError> {
Ok(crate::modexp::modexp(base, exp, modulus))
}
#[inline]
fn blake2_compress(&self, rounds: u32, h: &mut [u64; 8], m: [u64; 16], t: [u64; 2], f: bool) {
crate::blake2::algo::compress(rounds as usize, h, m, t, f);
}
#[inline]
fn secp256r1_verify_signature(&self, msg: &[u8; 32], sig: &[u8; 64], pk: &[u8; 64]) -> bool {
crate::secp256r1::verify_signature(*msg, *sig, *pk).is_some()
}
#[inline]
fn verify_kzg_proof(
&self,
z: &[u8; 32],
y: &[u8; 32],
commitment: &[u8; 48],
proof: &[u8; 48],
) -> Result<(), PrecompileError> {
if !crate::kzg_point_evaluation::verify_kzg_proof(commitment, z, y, proof) {
return Err(PrecompileError::BlobVerifyKzgProofFailed);
}
Ok(())
}
fn bls12_381_g1_add(&self, a: G1Point, b: G1Point) -> Result<[u8; 96], PrecompileError> {
crate::bls12_381::crypto_backend::p1_add_affine_bytes(a, b)
}
fn bls12_381_g1_msm(
&self,
pairs: &mut dyn Iterator<Item = Result<G1PointScalar, PrecompileError>>,
) -> Result<[u8; 96], PrecompileError> {
crate::bls12_381::crypto_backend::p1_msm_bytes(pairs)
}
fn bls12_381_g2_add(&self, a: G2Point, b: G2Point) -> Result<[u8; 192], PrecompileError> {
crate::bls12_381::crypto_backend::p2_add_affine_bytes(a, b)
}
fn bls12_381_g2_msm(
&self,
pairs: &mut dyn Iterator<Item = Result<G2PointScalar, PrecompileError>>,
) -> Result<[u8; 192], PrecompileError> {
crate::bls12_381::crypto_backend::p2_msm_bytes(pairs)
}
fn bls12_381_pairing_check(
&self,
pairs: &[(G1Point, G2Point)],
) -> Result<bool, PrecompileError> {
crate::bls12_381::crypto_backend::pairing_check_bytes(pairs)
}
fn bls12_381_fp_to_g1(&self, fp: &[u8; 48]) -> Result<[u8; 96], PrecompileError> {
crate::bls12_381::crypto_backend::map_fp_to_g1_bytes(fp)
}
fn bls12_381_fp2_to_g2(&self, fp2: ([u8; 48], [u8; 48])) -> Result<[u8; 192], PrecompileError> {
crate::bls12_381::crypto_backend::map_fp2_to_g2_bytes(&fp2.0, &fp2.1)
}
}
pub type PrecompileFn = fn(&[u8], u64) -> PrecompileResult;
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum PrecompileError {
OutOfGas,
Blake2WrongLength,
Blake2WrongFinalIndicatorFlag,
ModexpExpOverflow,
ModexpBaseOverflow,
ModexpModOverflow,
ModexpEip7823LimitSize,
Bn254FieldPointNotAMember,
Bn254AffineGFailedToCreate,
Bn254PairLength,
BlobInvalidInputLength,
BlobMismatchedVersion,
BlobVerifyKzgProofFailed,
NonCanonicalFp,
Bls12381G1NotOnCurve,
Bls12381G1NotInSubgroup,
Bls12381G2NotOnCurve,
Bls12381G2NotInSubgroup,
Bls12381ScalarInputLength,
Bls12381G1AddInputLength,
Bls12381G1MsmInputLength,
Bls12381G2AddInputLength,
Bls12381G2MsmInputLength,
Bls12381PairingInputLength,
Bls12381MapFpToG1InputLength,
Bls12381MapFp2ToG2InputLength,
Bls12381FpPaddingInvalid,
Bls12381FpPaddingLength,
Bls12381G1PaddingLength,
Bls12381G2PaddingLength,
KzgInvalidG1Point,
KzgG1PointNotOnCurve,
KzgG1PointNotInSubgroup,
KzgInvalidInputLength,
Secp256k1RecoverFailed,
Fatal(String),
Other(String),
}
impl PrecompileError {
pub fn other(err: impl Into<String>) -> Self {
Self::Other(err.into())
}
pub fn is_oog(&self) -> bool {
matches!(self, Self::OutOfGas)
}
}
impl core::error::Error for PrecompileError {}
impl fmt::Display for PrecompileError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
Self::OutOfGas => "out of gas",
Self::Blake2WrongLength => "wrong input length for blake2",
Self::Blake2WrongFinalIndicatorFlag => "wrong final indicator flag for blake2",
Self::ModexpExpOverflow => "modexp exp overflow",
Self::ModexpBaseOverflow => "modexp base overflow",
Self::ModexpModOverflow => "modexp mod overflow",
Self::ModexpEip7823LimitSize => "Modexp limit all input sizes.",
Self::Bn254FieldPointNotAMember => "field point not a member of bn254 curve",
Self::Bn254AffineGFailedToCreate => "failed to create affine g point for bn254 curve",
Self::Bn254PairLength => "bn254 invalid pair length",
Self::BlobInvalidInputLength => "invalid blob input length",
Self::BlobMismatchedVersion => "mismatched blob version",
Self::BlobVerifyKzgProofFailed => "verifying blob kzg proof failed",
Self::NonCanonicalFp => "non-canonical field element",
Self::Bls12381G1NotOnCurve => "bls12-381 g1 point not on curve",
Self::Bls12381G1NotInSubgroup => "bls12-381 g1 point not in correct subgroup",
Self::Bls12381G2NotOnCurve => "bls12-381 g2 point not on curve",
Self::Bls12381G2NotInSubgroup => "bls12-381 g2 point not in correct subgroup",
Self::Bls12381ScalarInputLength => "bls12-381 scalar input length error",
Self::Bls12381G1AddInputLength => "bls12-381 g1 add input length error",
Self::Bls12381G1MsmInputLength => "bls12-381 g1 msm input length error",
Self::Bls12381G2AddInputLength => "bls12-381 g2 add input length error",
Self::Bls12381G2MsmInputLength => "bls12-381 g2 msm input length error",
Self::Bls12381PairingInputLength => "bls12-381 pairing input length error",
Self::Bls12381MapFpToG1InputLength => "bls12-381 map fp to g1 input length error",
Self::Bls12381MapFp2ToG2InputLength => "bls12-381 map fp2 to g2 input length error",
Self::Bls12381FpPaddingInvalid => "bls12-381 fp 64 top bytes of input are not zero",
Self::Bls12381FpPaddingLength => "bls12-381 fp padding length error",
Self::Bls12381G1PaddingLength => "bls12-381 g1 padding length error",
Self::Bls12381G2PaddingLength => "bls12-381 g2 padding length error",
Self::KzgInvalidG1Point => "kzg invalid g1 point",
Self::KzgG1PointNotOnCurve => "kzg g1 point not on curve",
Self::KzgG1PointNotInSubgroup => "kzg g1 point not in correct subgroup",
Self::KzgInvalidInputLength => "kzg invalid input length",
Self::Secp256k1RecoverFailed => "secp256k1 signature recovery failed",
Self::Fatal(s) => s,
Self::Other(s) => s,
};
f.write_str(s)
}
}
#[derive(Clone, Debug)]
pub struct DefaultCrypto;
impl Crypto for DefaultCrypto {}