revm_precompile/
kzg_point_evaluation.rs1use crate::{
4    crypto, Address, Precompile, PrecompileError, PrecompileId, PrecompileOutput, PrecompileResult,
5};
6pub mod arkworks;
7
8#[cfg(feature = "blst")]
9pub mod blst;
10
11use primitives::hex_literal::hex;
12
13pub const POINT_EVALUATION: Precompile =
15    Precompile::new(PrecompileId::KzgPointEvaluation, ADDRESS, run);
16
17pub const ADDRESS: Address = crate::u64_to_address(0x0A);
19
20pub const GAS_COST: u64 = 50_000;
22
23pub const VERSIONED_HASH_VERSION_KZG: u8 = 0x01;
25
26pub const RETURN_VALUE: &[u8; 64] = &hex!(
28    "0000000000000000000000000000000000000000000000000000000000001000"
29    "73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001"
30);
31
32pub fn run(input: &[u8], gas_limit: u64) -> PrecompileResult {
41    if gas_limit < GAS_COST {
42        return Err(PrecompileError::OutOfGas);
43    }
44
45    if input.len() != 192 {
47        return Err(PrecompileError::BlobInvalidInputLength);
48    }
49
50    let versioned_hash = &input[..32];
52    let commitment = &input[96..144];
53    if kzg_to_versioned_hash(commitment) != versioned_hash {
54        return Err(PrecompileError::BlobMismatchedVersion);
55    }
56
57    let commitment: &[u8; 48] = commitment.try_into().unwrap();
59    let z = input[32..64].try_into().unwrap();
60    let y = input[64..96].try_into().unwrap();
61    let proof = input[144..192].try_into().unwrap();
62    crypto().verify_kzg_proof(z, y, commitment, proof)?;
63
64    Ok(PrecompileOutput::new(GAS_COST, RETURN_VALUE.into()))
66}
67
68#[inline]
70pub fn kzg_to_versioned_hash(commitment: &[u8]) -> [u8; 32] {
71    let mut hash = crypto().sha256(commitment);
72    hash[0] = VERSIONED_HASH_VERSION_KZG;
73    hash
74}
75
76#[inline]
78pub fn verify_kzg_proof(
79    commitment: &[u8; 48],
80    z: &[u8; 32],
81    y: &[u8; 32],
82    proof: &[u8; 48],
83) -> bool {
84    cfg_if::cfg_if! {
85        if #[cfg(feature = "c-kzg")] {
86            use c_kzg::{Bytes48, Bytes32};
87
88            let as_bytes48 = |bytes: &[u8; 48]| -> &Bytes48 { unsafe { &*bytes.as_ptr().cast() } };
89            let as_bytes32 = |bytes: &[u8; 32]| -> &Bytes32 { unsafe { &*bytes.as_ptr().cast() } };
90
91            let kzg_settings = c_kzg::ethereum_kzg_settings(8);
92            kzg_settings.verify_kzg_proof(as_bytes48(commitment), as_bytes32(z), as_bytes32(y), as_bytes48(proof)).unwrap_or(false)
93        } else if #[cfg(feature = "blst")] {
94            blst::verify_kzg_proof(commitment, z, y, proof)
95        } else {
96            arkworks::verify_kzg_proof(commitment, z, y, proof)
97        }
98    }
99}
100#[cfg(test)]
101mod tests {
102    use super::*;
103
104    #[test]
105    fn basic_test() {
106        let commitment = hex!("8f59a8d2a1a625a17f3fea0fe5eb8c896db3764f3185481bc22f91b4aaffcca25f26936857bc3a7c2539ea8ec3a952b7").to_vec();
109        let crypto = &crate::DefaultCrypto;
110        let mut versioned_hash = crate::Crypto::sha256(crypto, &commitment).to_vec();
111        versioned_hash[0] = VERSIONED_HASH_VERSION_KZG;
112        let z = hex!("73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000000").to_vec();
113        let y = hex!("1522a4a7f34e1ea350ae07c29c96c7e79655aa926122e95fe69fcbd932ca49e9").to_vec();
114        let proof = hex!("a62ad71d14c5719385c0686f1871430475bf3a00f0aa3f7b8dd99a9abc2160744faf0070725e00b60ad9a026a15b1a8c").to_vec();
115
116        let input = [versioned_hash, z, y, commitment, proof].concat();
117
118        let expected_output = hex!("000000000000000000000000000000000000000000000000000000000000100073eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001");
119        let gas = 50000;
120        let output = run(&input, gas).unwrap();
121        assert_eq!(output.gas_used, gas);
122        assert_eq!(output.bytes[..], expected_output);
123    }
124
125    #[test]
126    fn test_invalid_input() {
127        let commitment = hex!("c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
128        let z = hex!("0000000000000000000000000000000000000000000000000000000000000000");
129        let y = hex!("73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001");
130        let proof = hex!("c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
131
132        let t = verify_kzg_proof(&commitment, &z, &y, &proof);
133        assert!(!t);
134    }
135
136    #[test]
137    fn test_valid_input() {
138        let commitment = hex!("c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
139        let z = hex!("73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000000");
140        let y = hex!("0000000000000000000000000000000000000000000000000000000000000000");
141        let proof = hex!("c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
142
143        let t = verify_kzg_proof(&commitment, &z, &y, &proof);
144        assert!(t);
145    }
146}