use context_interface::result::AnyError;
use core::fmt::{self, Debug};
use primitives::{Bytes, OnceLock};
use std::{borrow::Cow, 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 EthPrecompileResult = Result<EthPrecompileOutput, PrecompileHalt>;
pub type PrecompileResult = Result<PrecompileOutput, PrecompileError>;
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct EthPrecompileOutput {
pub gas_used: u64,
pub bytes: Bytes,
}
impl EthPrecompileOutput {
pub fn new(gas_used: u64, bytes: Bytes) -> Self {
Self { gas_used, bytes }
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum PrecompileStatus {
Success,
Revert,
Halt(PrecompileHalt),
}
impl PrecompileStatus {
#[inline]
pub fn is_success_or_revert(&self) -> bool {
matches!(self, PrecompileStatus::Success | PrecompileStatus::Revert)
}
#[inline]
pub fn is_revert_or_halt(&self) -> bool {
matches!(self, PrecompileStatus::Revert | PrecompileStatus::Halt(_))
}
#[inline]
pub fn halt_reason(&self) -> Option<&PrecompileHalt> {
match &self {
PrecompileStatus::Halt(reason) => Some(reason),
_ => None,
}
}
#[inline]
pub fn is_success(&self) -> bool {
matches!(self, PrecompileStatus::Success)
}
#[inline]
pub fn is_revert(&self) -> bool {
matches!(self, PrecompileStatus::Revert)
}
#[inline]
pub fn is_halt(&self) -> bool {
matches!(self, PrecompileStatus::Halt(_))
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct PrecompileOutput {
pub status: PrecompileStatus,
pub gas_used: u64,
pub gas_refunded: i64,
pub state_gas_used: u64,
pub reservoir: u64,
pub bytes: Bytes,
}
impl PrecompileOutput {
pub fn from_eth_result(result: EthPrecompileResult, reservoir: u64) -> Self {
match result {
Ok(output) => Self::new(output.gas_used, output.bytes, reservoir),
Err(halt) => Self::halt(halt, reservoir),
}
}
pub fn new(gas_used: u64, bytes: Bytes, reservoir: u64) -> Self {
Self {
status: PrecompileStatus::Success,
gas_used,
gas_refunded: 0,
state_gas_used: 0,
reservoir,
bytes,
}
}
pub fn halt(reason: PrecompileHalt, reservoir: u64) -> Self {
Self {
status: PrecompileStatus::Halt(reason),
gas_used: 0,
gas_refunded: 0,
state_gas_used: 0,
reservoir,
bytes: Bytes::new(),
}
}
pub fn revert(gas_used: u64, bytes: Bytes, reservoir: u64) -> Self {
Self {
status: PrecompileStatus::Revert,
gas_used,
gas_refunded: 0,
state_gas_used: 0,
reservoir,
bytes,
}
}
pub fn is_success(&self) -> bool {
matches!(self.status, PrecompileStatus::Success)
}
#[deprecated(note = "use `is_success` instead")]
pub fn is_ok(&self) -> bool {
self.is_success()
}
pub fn is_revert(&self) -> bool {
matches!(self.status, PrecompileStatus::Revert)
}
pub fn is_halt(&self) -> bool {
matches!(self.status, PrecompileStatus::Halt(_))
}
#[inline]
pub fn halt_reason(&self) -> Option<&PrecompileHalt> {
self.status.halt_reason()
}
}
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], PrecompileHalt> {
crate::bn254::crypto_backend::g1_point_add(p1, p2)
}
#[inline]
fn bn254_g1_mul(&self, point: &[u8], scalar: &[u8]) -> Result<[u8; 64], PrecompileHalt> {
crate::bn254::crypto_backend::g1_point_mul(point, scalar)
}
#[inline]
fn bn254_pairing_check(&self, pairs: &[(&[u8], &[u8])]) -> Result<bool, PrecompileHalt> {
crate::bn254::crypto_backend::pairing_check(pairs)
}
#[inline]
fn secp256k1_ecrecover(
&self,
sig: &[u8; 64],
recid: u8,
msg: &[u8; 32],
) -> Result<[u8; 32], PrecompileHalt> {
crate::secp256k1::ecrecover_bytes(sig, recid, msg)
.ok_or(PrecompileHalt::Secp256k1RecoverFailed)
}
#[inline]
fn modexp(&self, base: &[u8], exp: &[u8], modulus: &[u8]) -> Result<Vec<u8>, PrecompileHalt> {
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<(), PrecompileHalt> {
if !crate::kzg_point_evaluation::verify_kzg_proof(commitment, z, y, proof) {
return Err(PrecompileHalt::BlobVerifyKzgProofFailed);
}
Ok(())
}
fn bls12_381_g1_add(&self, a: G1Point, b: G1Point) -> Result<[u8; 96], PrecompileHalt> {
crate::bls12_381::crypto_backend::p1_add_affine_bytes(a, b)
}
fn bls12_381_g1_msm(
&self,
pairs: &mut dyn Iterator<Item = Result<G1PointScalar, PrecompileHalt>>,
) -> Result<[u8; 96], PrecompileHalt> {
crate::bls12_381::crypto_backend::p1_msm_bytes(pairs)
}
fn bls12_381_g2_add(&self, a: G2Point, b: G2Point) -> Result<[u8; 192], PrecompileHalt> {
crate::bls12_381::crypto_backend::p2_add_affine_bytes(a, b)
}
fn bls12_381_g2_msm(
&self,
pairs: &mut dyn Iterator<Item = Result<G2PointScalar, PrecompileHalt>>,
) -> Result<[u8; 192], PrecompileHalt> {
crate::bls12_381::crypto_backend::p2_msm_bytes(pairs)
}
fn bls12_381_pairing_check(
&self,
pairs: &[(G1Point, G2Point)],
) -> Result<bool, PrecompileHalt> {
crate::bls12_381::crypto_backend::pairing_check_bytes(pairs)
}
fn bls12_381_fp_to_g1(&self, fp: &[u8; 48]) -> Result<[u8; 96], PrecompileHalt> {
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], PrecompileHalt> {
crate::bls12_381::crypto_backend::map_fp2_to_g2_bytes(&fp2.0, &fp2.1)
}
}
pub type PrecompileEthFn = fn(&[u8], u64) -> EthPrecompileResult;
pub type PrecompileFn = fn(&[u8], u64, u64) -> PrecompileResult;
#[macro_export]
macro_rules! eth_precompile_fn {
($name:ident, $eth_fn:expr) => {
fn $name(input: &[u8], gas_limit: u64, reservoir: u64) -> $crate::PrecompileResult {
Ok($crate::call_eth_precompile(
$eth_fn, input, gas_limit, reservoir,
))
}
};
}
#[inline]
pub fn call_eth_precompile(
f: PrecompileEthFn,
input: &[u8],
gas_limit: u64,
reservoir: u64,
) -> PrecompileOutput {
match f(input, gas_limit) {
Ok(output) => PrecompileOutput::new(output.gas_used, output.bytes, reservoir),
Err(halt) => PrecompileOutput::halt(halt, reservoir),
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum PrecompileHalt {
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,
Other(Cow<'static, str>),
}
impl PrecompileHalt {
pub fn other(err: impl Into<String>) -> Self {
Self::Other(Cow::Owned(err.into()))
}
pub const fn other_static(err: &'static str) -> Self {
Self::Other(Cow::Borrowed(err))
}
pub fn is_oog(&self) -> bool {
matches!(self, Self::OutOfGas)
}
}
impl core::error::Error for PrecompileHalt {}
impl fmt::Display for PrecompileHalt {
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::Other(s) => s,
};
f.write_str(s)
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum PrecompileError {
Fatal(String),
FatalAny(AnyError),
}
impl PrecompileError {
pub fn is_fatal(&self) -> bool {
true
}
}
impl core::error::Error for PrecompileError {}
impl fmt::Display for PrecompileError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Fatal(s) => write!(f, "fatal: {s}"),
Self::FatalAny(s) => write!(f, "fatal: {s}"),
}
}
}
#[derive(Clone, Debug)]
pub struct DefaultCrypto;
impl Crypto for DefaultCrypto {}