revm_precompile/kzg_point_evaluation/
blst.rs1use crate::{
3 bls12_381::blst::{
4 p1_add_or_double, p1_from_affine, p1_scalar_mul, p1_to_affine, p2_add_or_double,
5 p2_from_affine, p2_scalar_mul, p2_to_affine, pairing_check,
6 },
7 bls12_381_const::TRUSTED_SETUP_TAU_G2_BYTES,
8 PrecompileHalt,
9};
10use ::blst::{
11 blst_p1_affine, blst_p1_affine_in_g1, blst_p1_affine_is_inf, blst_p1_affine_on_curve,
12 blst_p2_affine, blst_p2_affine_is_inf, blst_scalar, blst_scalar_fr_check,
13 blst_scalar_from_bendian,
14};
15use primitives::OnceLock;
16use std::vec::Vec;
17
18#[inline]
23pub fn verify_kzg_proof(
24 commitment: &[u8; 48],
25 z: &[u8; 32],
26 y: &[u8; 32],
27 proof: &[u8; 48],
28) -> bool {
29 let Ok(commitment_point) = parse_g1_compressed(commitment) else {
31 return false;
32 };
33
34 let Ok(proof_point) = parse_g1_compressed(proof) else {
36 return false;
37 };
38
39 let Ok(z_scalar) = read_scalar_canonical(z) else {
41 return false;
42 };
43 let Ok(y_scalar) = read_scalar_canonical(y) else {
44 return false;
45 };
46
47 let tau_g2 = get_trusted_setup_g2();
49
50 let g1 = get_g1_generator();
52 let g2 = get_g2_generator();
53
54 let y_g1 = p1_scalar_mul(&g1, &y_scalar);
56 let p_minus_y = p1_sub_affine(&commitment_point, &y_g1);
57
58 let z_g2 = p2_scalar_mul(&g2, &z_scalar);
60 let x_minus_z = p2_sub_affine(tau_g2, &z_g2);
61
62 let neg_g2 = p2_neg(&g2);
65
66 let pairs: Vec<_> = [(p_minus_y, neg_g2), (proof_point, x_minus_z)]
71 .into_iter()
72 .filter(|(g1, g2)| unsafe { !blst_p1_affine_is_inf(g1) && !blst_p2_affine_is_inf(g2) })
74 .collect();
75
76 pairing_check(&pairs)
77}
78
79fn get_trusted_setup_g2() -> &'static blst_p2_affine {
82 static TAU_G2: OnceLock<blst_p2_affine> = OnceLock::new();
83 TAU_G2.get_or_init(|| {
84 let mut g2_affine = blst_p2_affine::default();
86 unsafe {
87 let result =
90 blst::blst_p2_uncompress(&mut g2_affine, TRUSTED_SETUP_TAU_G2_BYTES.as_ptr());
91 if result != blst::BLST_ERROR::BLST_SUCCESS {
92 panic!("Failed to deserialize trusted setup G2 point");
93 }
94 }
95 g2_affine
96 })
97}
98
99fn get_g1_generator() -> blst_p1_affine {
101 unsafe { ::blst::BLS12_381_G1 }
102}
103
104fn get_g2_generator() -> blst_p2_affine {
106 unsafe { ::blst::BLS12_381_G2 }
107}
108
109fn parse_g1_compressed(bytes: &[u8; 48]) -> Result<blst_p1_affine, PrecompileHalt> {
111 let mut point = blst_p1_affine::default();
112 unsafe {
113 let result = blst::blst_p1_uncompress(&mut point, bytes.as_ptr());
114 if result != blst::BLST_ERROR::BLST_SUCCESS {
115 return Err(PrecompileHalt::KzgInvalidG1Point);
116 }
117
118 if !blst_p1_affine_on_curve(&point) {
120 return Err(PrecompileHalt::KzgG1PointNotOnCurve);
121 }
122
123 if !blst_p1_affine_in_g1(&point) {
125 return Err(PrecompileHalt::KzgG1PointNotInSubgroup);
126 }
127 }
128 Ok(point)
129}
130
131fn read_scalar_canonical(bytes: &[u8; 32]) -> Result<blst_scalar, PrecompileHalt> {
133 let mut scalar = blst_scalar::default();
134
135 unsafe {
137 blst_scalar_from_bendian(&mut scalar, bytes.as_ptr());
138 }
139
140 if unsafe { !blst_scalar_fr_check(&scalar) } {
141 return Err(PrecompileHalt::NonCanonicalFp);
142 }
143
144 Ok(scalar)
145}
146
147fn p1_sub_affine(a: &blst_p1_affine, b: &blst_p1_affine) -> blst_p1_affine {
149 let a_jacobian = p1_from_affine(a);
151
152 let neg_b = p1_neg(b);
154
155 let result = p1_add_or_double(&a_jacobian, &neg_b);
157
158 p1_to_affine(&result)
159}
160
161fn p2_sub_affine(a: &blst_p2_affine, b: &blst_p2_affine) -> blst_p2_affine {
163 let a_jacobian = p2_from_affine(a);
165
166 let neg_b = p2_neg(b);
168
169 let result = p2_add_or_double(&a_jacobian, &neg_b);
171
172 p2_to_affine(&result)
173}
174
175fn p1_neg(p: &blst_p1_affine) -> blst_p1_affine {
177 let mut p_jacobian = p1_from_affine(p);
179 unsafe {
180 ::blst::blst_p1_cneg(&mut p_jacobian, true);
181 }
182 p1_to_affine(&p_jacobian)
183}
184
185fn p2_neg(p: &blst_p2_affine) -> blst_p2_affine {
187 let mut p_jacobian = p2_from_affine(p);
189 unsafe {
190 ::blst::blst_p2_cneg(&mut p_jacobian, true);
191 }
192 p2_to_affine(&p_jacobian)
193}