revm_precompile/
kzg_point_evaluation.rs1use crate::{Address, PrecompileError, PrecompileOutput, PrecompileResult, PrecompileWithAddress};
2cfg_if::cfg_if! {
3 if #[cfg(feature = "c-kzg")] {
4 use c_kzg::{Bytes32, Bytes48, KzgProof};
5 } else if #[cfg(feature = "kzg-rs")] {
6 use kzg_rs::{Bytes32, Bytes48, KzgProof};
7 }
8}
9use primitives::{hex_literal::hex, Bytes};
10use sha2::{Digest, Sha256};
11
12pub const POINT_EVALUATION: PrecompileWithAddress = PrecompileWithAddress(ADDRESS, run);
13
14pub const ADDRESS: Address = crate::u64_to_address(0x0A);
15pub const GAS_COST: u64 = 50_000;
16pub const VERSIONED_HASH_VERSION_KZG: u8 = 0x01;
17
18pub const RETURN_VALUE: &[u8; 64] = &hex!(
20 "0000000000000000000000000000000000000000000000000000000000001000"
21 "73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001"
22);
23
24pub fn run(input: &Bytes, gas_limit: u64) -> PrecompileResult {
33 if gas_limit < GAS_COST {
34 return Err(PrecompileError::OutOfGas);
35 }
36
37 if input.len() != 192 {
39 return Err(PrecompileError::BlobInvalidInputLength);
40 }
41
42 let versioned_hash = &input[..32];
44 let commitment = &input[96..144];
45 if kzg_to_versioned_hash(commitment) != versioned_hash {
46 return Err(PrecompileError::BlobMismatchedVersion);
47 }
48
49 let commitment = as_bytes48(commitment);
51 let z = as_bytes32(&input[32..64]);
52 let y = as_bytes32(&input[64..96]);
53 let proof = as_bytes48(&input[144..192]);
54 if !verify_kzg_proof(commitment, z, y, proof) {
55 return Err(PrecompileError::BlobVerifyKzgProofFailed);
56 }
57
58 Ok(PrecompileOutput::new(GAS_COST, RETURN_VALUE.into()))
60}
61
62#[inline]
64pub fn kzg_to_versioned_hash(commitment: &[u8]) -> [u8; 32] {
65 let mut hash: [u8; 32] = Sha256::digest(commitment).into();
66 hash[0] = VERSIONED_HASH_VERSION_KZG;
67 hash
68}
69
70#[inline]
71pub fn verify_kzg_proof(commitment: &Bytes48, z: &Bytes32, y: &Bytes32, proof: &Bytes48) -> bool {
72 cfg_if::cfg_if! {
73 if #[cfg(feature = "c-kzg")] {
74 let kzg_settings = c_kzg::ethereum_kzg_settings();
75 } else if #[cfg(feature = "kzg-rs")] {
76 let env = kzg_rs::EnvKzgSettings::default();
77 let kzg_settings = env.get();
78 }
79 }
80 KzgProof::verify_kzg_proof(commitment, z, y, proof, kzg_settings).unwrap_or(false)
81}
82
83#[inline]
84#[track_caller]
85pub fn as_array<const N: usize>(bytes: &[u8]) -> &[u8; N] {
86 bytes.try_into().expect("slice with incorrect length")
87}
88
89#[inline]
90#[track_caller]
91pub fn as_bytes32(bytes: &[u8]) -> &Bytes32 {
92 unsafe { &*as_array::<32>(bytes).as_ptr().cast() }
94}
95
96#[inline]
97#[track_caller]
98pub fn as_bytes48(bytes: &[u8]) -> &Bytes48 {
99 unsafe { &*as_array::<48>(bytes).as_ptr().cast() }
101}
102
103#[cfg(test)]
104mod tests {
105 use super::*;
106
107 #[test]
108 fn basic_test() {
109 let commitment = hex!("8f59a8d2a1a625a17f3fea0fe5eb8c896db3764f3185481bc22f91b4aaffcca25f26936857bc3a7c2539ea8ec3a952b7").to_vec();
112 let mut versioned_hash = Sha256::digest(&commitment).to_vec();
113 versioned_hash[0] = VERSIONED_HASH_VERSION_KZG;
114 let z = hex!("73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000000").to_vec();
115 let y = hex!("1522a4a7f34e1ea350ae07c29c96c7e79655aa926122e95fe69fcbd932ca49e9").to_vec();
116 let proof = hex!("a62ad71d14c5719385c0686f1871430475bf3a00f0aa3f7b8dd99a9abc2160744faf0070725e00b60ad9a026a15b1a8c").to_vec();
117
118 let input = [versioned_hash, z, y, commitment, proof].concat();
119
120 let expected_output = hex!("000000000000000000000000000000000000000000000000000000000000100073eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001");
121 let gas = 50000;
122 let output = run(&input.into(), gas).unwrap();
123 assert_eq!(output.gas_used, gas);
124 assert_eq!(output.bytes[..], expected_output);
125 }
126}