Skip to main content

revm_precompile/kzg_point_evaluation/
blst.rs

1//! KZG point evaluation precompile using BLST BLS12-381 implementation.
2use 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    PrecompileError,
9};
10use ::blst::{
11    blst_p1_affine, blst_p1_affine_in_g1, blst_p1_affine_on_curve, blst_p2_affine, blst_scalar,
12    blst_scalar_fr_check, blst_scalar_from_bendian,
13};
14use primitives::OnceLock;
15
16/// Verify KZG proof using BLST BLS12-381 implementation.
17///
18/// <https://github.com/ethereum/EIPs/blob/4d2a00692bb131366ede1a16eced2b0e25b1bf99/EIPS/eip-4844.md?plain=1#L203>
19/// <https://github.com/ethereum/consensus-specs/blob/master/specs/deneb/polynomial-commitments.md#verify_kzg_proof_impl>
20#[inline]
21pub fn verify_kzg_proof(
22    commitment: &[u8; 48],
23    z: &[u8; 32],
24    y: &[u8; 32],
25    proof: &[u8; 48],
26) -> bool {
27    // Parse the commitment (G1 point)
28    let Ok(commitment_point) = parse_g1_compressed(commitment) else {
29        return false;
30    };
31
32    // Parse the proof (G1 point)
33    let Ok(proof_point) = parse_g1_compressed(proof) else {
34        return false;
35    };
36
37    // Parse z and y as field elements (Fr, scalar field)
38    let Ok(z_scalar) = read_scalar_canonical(z) else {
39        return false;
40    };
41    let Ok(y_scalar) = read_scalar_canonical(y) else {
42        return false;
43    };
44
45    // Get the trusted setup G2 point [τ]₂
46    let tau_g2 = get_trusted_setup_g2();
47
48    // Get generators
49    let g1 = get_g1_generator();
50    let g2 = get_g2_generator();
51
52    // Compute P_minus_y = commitment - [y]G₁
53    let y_g1 = p1_scalar_mul(&g1, &y_scalar);
54    let p_minus_y = p1_sub_affine(&commitment_point, &y_g1);
55
56    // Compute X_minus_z = [τ]G₂ - [z]G₂
57    let z_g2 = p2_scalar_mul(&g2, &z_scalar);
58    let x_minus_z = p2_sub_affine(tau_g2, &z_g2);
59
60    // Verify: P - y = Q * (X - z)
61    // Using pairing check: e(P - y, -G₂) * e(proof, X - z) == 1
62    let neg_g2 = p2_neg(&g2);
63
64    pairing_check(&[(p_minus_y, neg_g2), (proof_point, x_minus_z)])
65}
66
67/// Get the trusted setup G2 point `[τ]₂` from the Ethereum KZG ceremony.
68/// This is g2_monomial_1 from trusted_setup_4096.json
69fn get_trusted_setup_g2() -> &'static blst_p2_affine {
70    static TAU_G2: OnceLock<blst_p2_affine> = OnceLock::new();
71    TAU_G2.get_or_init(|| {
72        // For compressed G2, we need to decompress
73        let mut g2_affine = blst_p2_affine::default();
74        unsafe {
75            // The compressed format has x coordinate and a flag bit for y
76            // We use uncompress which handles this automatically
77            let result =
78                blst::blst_p2_uncompress(&mut g2_affine, TRUSTED_SETUP_TAU_G2_BYTES.as_ptr());
79            if result != blst::BLST_ERROR::BLST_SUCCESS {
80                panic!("Failed to deserialize trusted setup G2 point");
81            }
82        }
83        g2_affine
84    })
85}
86
87/// Get G1 generator point
88fn get_g1_generator() -> blst_p1_affine {
89    unsafe { ::blst::BLS12_381_G1 }
90}
91
92/// Get G2 generator point
93fn get_g2_generator() -> blst_p2_affine {
94    unsafe { ::blst::BLS12_381_G2 }
95}
96
97/// Parse a G1 point from compressed format (48 bytes)
98fn parse_g1_compressed(bytes: &[u8; 48]) -> Result<blst_p1_affine, PrecompileError> {
99    let mut point = blst_p1_affine::default();
100    unsafe {
101        let result = blst::blst_p1_uncompress(&mut point, bytes.as_ptr());
102        if result != blst::BLST_ERROR::BLST_SUCCESS {
103            return Err(PrecompileError::KzgInvalidG1Point);
104        }
105
106        // Verify the point is on curve
107        if !blst_p1_affine_on_curve(&point) {
108            return Err(PrecompileError::KzgG1PointNotOnCurve);
109        }
110
111        // Verify the point is in the correct subgroup
112        if !blst_p1_affine_in_g1(&point) {
113            return Err(PrecompileError::KzgG1PointNotInSubgroup);
114        }
115    }
116    Ok(point)
117}
118
119/// Read a scalar field element from bytes and verify it's canonical
120fn read_scalar_canonical(bytes: &[u8; 32]) -> Result<blst_scalar, PrecompileError> {
121    let mut scalar = blst_scalar::default();
122
123    // Read scalar from big endian bytes
124    unsafe {
125        blst_scalar_from_bendian(&mut scalar, bytes.as_ptr());
126    }
127
128    if unsafe { !blst_scalar_fr_check(&scalar) } {
129        return Err(PrecompileError::NonCanonicalFp);
130    }
131
132    Ok(scalar)
133}
134
135/// Subtract two G1 points in affine form
136fn p1_sub_affine(a: &blst_p1_affine, b: &blst_p1_affine) -> blst_p1_affine {
137    // Convert first point to Jacobian
138    let a_jacobian = p1_from_affine(a);
139
140    // Negate second point
141    let neg_b = p1_neg(b);
142
143    // Add a + (-b)
144    let result = p1_add_or_double(&a_jacobian, &neg_b);
145
146    p1_to_affine(&result)
147}
148
149/// Subtract two G2 points in affine form
150fn p2_sub_affine(a: &blst_p2_affine, b: &blst_p2_affine) -> blst_p2_affine {
151    // Convert first point to Jacobian
152    let a_jacobian = p2_from_affine(a);
153
154    // Negate second point
155    let neg_b = p2_neg(b);
156
157    // Add a + (-b)
158    let result = p2_add_or_double(&a_jacobian, &neg_b);
159
160    p2_to_affine(&result)
161}
162
163/// Negate a G1 point
164fn p1_neg(p: &blst_p1_affine) -> blst_p1_affine {
165    // Convert to Jacobian, negate, convert back
166    let mut p_jacobian = p1_from_affine(p);
167    unsafe {
168        ::blst::blst_p1_cneg(&mut p_jacobian, true);
169    }
170    p1_to_affine(&p_jacobian)
171}
172
173/// Negate a G2 point
174fn p2_neg(p: &blst_p2_affine) -> blst_p2_affine {
175    // Convert to Jacobian, negate, convert back
176    let mut p_jacobian = p2_from_affine(p);
177    unsafe {
178        ::blst::blst_p2_cneg(&mut p_jacobian, true);
179    }
180    p2_to_affine(&p_jacobian)
181}