use ark_bls12_381::{Bls12_381, Fr, G1Affine, G2Affine};
use ark_ec::{AffineRepr, CurveGroup, pairing::Pairing};
use ark_ff::{BigInteger, One, PrimeField};
use ark_serialize::CanonicalDeserialize;
use core::ops::Neg;
use evm::{
GasMutState,
interpreter::{ExitError, ExitException, ExitResult, ExitSucceed},
};
use primitive_types::U256;
use sha2::Digest;
use crate::PurePrecompile;
pub const GAS_COST: u64 = 50_000;
pub const VERSIONED_HASH_VERSION_KZG: u8 = 0x01;
pub const RETURN_VALUE: &[u8; 64] = &hex_literal::hex!(
"0000000000000000000000000000000000000000000000000000000000001000"
"73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001"
);
pub struct KZGPointEvaluation;
impl<G: GasMutState> PurePrecompile<G> for KZGPointEvaluation {
fn execute(&self, input: &[u8], gasometer: &mut G) -> (ExitResult, Vec<u8>) {
try_some!(gasometer.record_gas(U256::from(GAS_COST)));
match run(input) {
Ok(ret) => (ExitSucceed::Returned.into(), ret),
Err(e) => (e.into(), Vec::new()),
}
}
}
pub fn run(input: &[u8]) -> Result<Vec<u8>, ExitError> {
if input.len() != 192 {
return Err(ExitException::Other("BlobInvalidInputLength".into()).into());
}
let versioned_hash = &input[..32];
let commitment = &input[96..144];
if kzg_to_versioned_hash(commitment) != versioned_hash {
return Err(ExitException::Other("BlobMismatchedVersion".into()).into());
}
let commitment: &[u8; 48] = commitment.try_into().unwrap();
let z = input[32..64].try_into().unwrap();
let y = input[64..96].try_into().unwrap();
let proof = input[144..192].try_into().unwrap();
if verify_kzg_proof(commitment, z, y, proof) {
Ok(RETURN_VALUE.into())
} else {
Err(ExitException::Other("BlobVerifyKzgProofFailed".into()).into())
}
}
#[inline]
pub fn kzg_to_versioned_hash(commitment: &[u8]) -> [u8; 32] {
let mut hash: [u8; 32] = sha2::Sha256::digest(commitment).into();
hash[0] = VERSIONED_HASH_VERSION_KZG;
hash
}
#[inline]
pub fn verify_kzg_proof(
commitment: &[u8; 48],
z: &[u8; 32],
y: &[u8; 32],
proof: &[u8; 48],
) -> bool {
let Ok(commitment_point) = parse_g1_compressed(commitment) else {
return false;
};
let Ok(proof_point) = parse_g1_compressed(proof) else {
return false;
};
let Ok(z_fr) = read_scalar_canonical(z) else {
return false;
};
let Ok(y_fr) = read_scalar_canonical(y) else {
return false;
};
let tau_g2 = get_trusted_setup_g2();
let g1 = get_g1_generator();
let g2 = get_g2_generator();
let y_g1 = p1_scalar_mul(&g1, &y_fr);
let p_minus_y = p1_sub_affine(&commitment_point, &y_g1);
let z_g2 = p2_scalar_mul(&g2, &z_fr);
let x_minus_z = p2_sub_affine(&tau_g2, &z_g2);
let neg_g2 = p2_neg(&g2);
pairing_check(&[(p_minus_y, neg_g2), (proof_point, x_minus_z)])
}
fn get_trusted_setup_g2() -> G2Affine {
G2Affine::deserialize_compressed_unchecked(&TRUSTED_SETUP_TAU_G2_BYTES[..])
.expect("Failed to parse trusted setup G2 point")
}
fn parse_g1_compressed(bytes: &[u8; 48]) -> Result<G1Affine, ExitError> {
let g1 = G1Affine::deserialize_compressed(&bytes[..])
.map_err(|_| ExitException::Other("Invalid compressed G1 point".into()))?;
Ok(g1)
}
fn read_scalar_canonical(bytes: &[u8; 32]) -> Result<Fr, ExitError> {
let fr = Fr::from_be_bytes_mod_order(bytes);
let bytes_roundtrip = fr.into_bigint().to_bytes_be();
if bytes_roundtrip.as_slice() != bytes {
return Err(ExitException::Other("Non-canonical scalar field element".into()).into());
}
Ok(fr)
}
#[inline]
fn get_g1_generator() -> G1Affine {
G1Affine::generator()
}
#[inline]
fn get_g2_generator() -> G2Affine {
G2Affine::generator()
}
#[inline]
fn p1_scalar_mul(point: &G1Affine, scalar: &Fr) -> G1Affine {
point.mul_bigint(scalar.into_bigint()).into_affine()
}
#[inline]
fn p2_scalar_mul(point: &G2Affine, scalar: &Fr) -> G2Affine {
point.mul_bigint(scalar.into_bigint()).into_affine()
}
#[inline]
fn p1_sub_affine(a: &G1Affine, b: &G1Affine) -> G1Affine {
(a.into_group() - b.into_group()).into_affine()
}
#[inline]
fn p2_sub_affine(a: &G2Affine, b: &G2Affine) -> G2Affine {
(a.into_group() - b.into_group()).into_affine()
}
#[inline]
fn p2_neg(p: &G2Affine) -> G2Affine {
p.neg()
}
const TRUSTED_SETUP_TAU_G2_BYTES: [u8; 96] = hex_literal::hex!(
"b5bfd7dd8cdeb128843bc287230af38926187075cbfbefa81009a2ce615ac53d2914e5870cb452d2afaaab24f3499f72185cbfee53492714734429b7b38608e23926c911cceceac9a36851477ba4c60b087041de621000edc98edada20c1def2"
);
#[inline]
fn pairing_check(pairs: &[(G1Affine, G2Affine)]) -> bool {
if pairs.is_empty() {
return true;
}
let (g1_points, g2_points): (Vec<G1Affine>, Vec<G2Affine>) = pairs.iter().copied().unzip();
let pairing_result = Bls12_381::multi_pairing(&g1_points, &g2_points);
pairing_result.0.is_one()
}