proof_of_sql/proof_primitive/hyperkzg/
commitment.rs

1use super::{BNScalar, HyperKZGPublicSetup};
2#[cfg(any(not(feature = "blitzar"), test))]
3use crate::base::if_rayon;
4use crate::base::{
5    commitment::{Commitment, CommittableColumn},
6    scalar::Scalar,
7    slice_ops,
8};
9use alloc::vec::Vec;
10use ark_bn254::{G1Affine, G1Projective};
11use ark_ec::AffineRepr;
12use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
13use core::ops::{AddAssign, Mul, Neg, Sub, SubAssign};
14#[cfg(all(feature = "rayon", any(not(feature = "blitzar"), test)))]
15use rayon::prelude::*;
16use serde::{Deserialize, Deserializer, Serialize, Serializer};
17
18/// This is the commitment type used in the hyperkzg proof system.
19#[derive(Clone, Copy, Debug, PartialEq, Eq, CanonicalSerialize, CanonicalDeserialize, Default)]
20pub struct HyperKZGCommitment {
21    /// The underlying commitment.
22    pub commitment: G1Projective,
23}
24impl Serialize for HyperKZGCommitment {
25    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
26        let affine: G1Affine = self.commitment.into();
27        match affine.xy() {
28            None => ([0u8; 32], [0u8; 32]).serialize(serializer),
29            Some((x, y)) => {
30                let mut x_bytes = [0u8; 32];
31                CanonicalSerialize::serialize_uncompressed(&x, &mut x_bytes[..])
32                    .map_err(serde::ser::Error::custom)?;
33                x_bytes.reverse();
34                let mut y_bytes = [0u8; 32];
35                CanonicalSerialize::serialize_uncompressed(&y, &mut y_bytes[..])
36                    .map_err(serde::ser::Error::custom)?;
37                y_bytes.reverse();
38                (x_bytes, y_bytes).serialize(serializer)
39            }
40        }
41    }
42}
43impl<'de> Deserialize<'de> for HyperKZGCommitment {
44    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
45        let (mut x_bytes, mut y_bytes) = <([u8; 32], [u8; 32])>::deserialize(deserializer)?;
46        let affine: G1Affine = if (x_bytes, y_bytes) == ([0u8; 32], [0u8; 32]) {
47            G1Affine::identity()
48        } else {
49            x_bytes.reverse();
50            y_bytes.reverse();
51            let x = CanonicalDeserialize::deserialize_uncompressed(&x_bytes[..])
52                .map_err(serde::de::Error::custom)?;
53            let y = CanonicalDeserialize::deserialize_uncompressed(&y_bytes[..])
54                .map_err(serde::de::Error::custom)?;
55            G1Affine::new_unchecked(x, y)
56        };
57        Ok(Self {
58            commitment: affine.into(),
59        })
60    }
61}
62
63impl AddAssign for HyperKZGCommitment {
64    fn add_assign(&mut self, rhs: Self) {
65        self.commitment = self.commitment + rhs.commitment;
66    }
67}
68impl From<&G1Affine> for HyperKZGCommitment {
69    fn from(value: &G1Affine) -> Self {
70        Self {
71            commitment: (*value).into(),
72        }
73    }
74}
75
76impl Mul<&HyperKZGCommitment> for BNScalar {
77    type Output = HyperKZGCommitment;
78    fn mul(self, rhs: &HyperKZGCommitment) -> Self::Output {
79        Self::Output {
80            commitment: rhs.commitment * self.0,
81        }
82    }
83}
84
85impl Mul<HyperKZGCommitment> for BNScalar {
86    type Output = HyperKZGCommitment;
87    #[expect(clippy::op_ref)]
88    fn mul(self, rhs: HyperKZGCommitment) -> Self::Output {
89        self * &rhs
90    }
91}
92impl Neg for HyperKZGCommitment {
93    type Output = Self;
94    fn neg(self) -> Self::Output {
95        (-BNScalar::ONE) * self
96    }
97}
98impl SubAssign for HyperKZGCommitment {
99    fn sub_assign(&mut self, rhs: Self) {
100        *self += -rhs;
101    }
102}
103impl Sub for HyperKZGCommitment {
104    type Output = Self;
105    fn sub(mut self, rhs: Self) -> Self::Output {
106        self -= rhs;
107        self
108    }
109}
110
111#[cfg(any(not(feature = "blitzar"), test))]
112#[tracing::instrument(
113    name = "compute_commitment_generic_impl (cpu)",
114    level = "debug",
115    skip_all
116)]
117fn compute_commitment_generic_impl<T: Into<BNScalar> + Clone + Sync>(
118    setup: HyperKZGPublicSetup<'_>,
119    offset: usize,
120    scalars: &[T],
121) -> HyperKZGCommitment {
122    assert!(offset + scalars.len() <= setup.len());
123    let product: G1Projective = if_rayon!(scalars.par_iter(), scalars.iter())
124        .zip(&setup[offset..offset + scalars.len()])
125        .map(|(t, s)| *s * Into::<BNScalar>::into(t).0)
126        .sum();
127    HyperKZGCommitment {
128        commitment: G1Projective::from(product),
129    }
130}
131
132#[cfg(any(not(feature = "blitzar"), test))]
133#[tracing::instrument(name = "compute_commitments_impl (cpu)", level = "debug", skip_all)]
134fn compute_commitments_impl(
135    committable_columns: &[crate::base::commitment::CommittableColumn],
136    offset: usize,
137    setup: &<HyperKZGCommitment as Commitment>::PublicSetup<'_>,
138) -> Vec<HyperKZGCommitment> {
139    if_rayon!(committable_columns.par_iter(), committable_columns.iter())
140        .map(|column| match column {
141            CommittableColumn::Boolean(vals) => {
142                compute_commitment_generic_impl(setup, offset, vals)
143            }
144            CommittableColumn::Uint8(vals) => compute_commitment_generic_impl(setup, offset, vals),
145            CommittableColumn::TinyInt(vals) => {
146                compute_commitment_generic_impl(setup, offset, vals)
147            }
148            CommittableColumn::SmallInt(vals) => {
149                compute_commitment_generic_impl(setup, offset, vals)
150            }
151            CommittableColumn::Int(vals) => compute_commitment_generic_impl(setup, offset, vals),
152            CommittableColumn::BigInt(vals) | CommittableColumn::TimestampTZ(_, _, vals) => {
153                compute_commitment_generic_impl(setup, offset, vals)
154            }
155            CommittableColumn::Int128(vals) => compute_commitment_generic_impl(setup, offset, vals),
156            CommittableColumn::Decimal75(_, _, vals)
157            | CommittableColumn::Scalar(vals)
158            | CommittableColumn::VarChar(vals)
159            | CommittableColumn::VarBinary(vals) => {
160                compute_commitment_generic_impl(setup, offset, vals)
161            }
162        })
163        .collect()
164}
165
166impl Commitment for HyperKZGCommitment {
167    type Scalar = BNScalar;
168    type PublicSetup<'a> = HyperKZGPublicSetup<'a>;
169
170    #[cfg(not(feature = "blitzar"))]
171    #[tracing::instrument(name = "compute_commitments (cpu)", level = "debug", skip_all)]
172    fn compute_commitments(
173        committable_columns: &[crate::base::commitment::CommittableColumn],
174        offset: usize,
175        setup: &Self::PublicSetup<'_>,
176    ) -> Vec<Self> {
177        compute_commitments_impl(committable_columns, offset, setup)
178    }
179
180    #[cfg(feature = "blitzar")]
181    #[tracing::instrument(name = "compute_commitments (gpu)", level = "debug", skip_all)]
182    fn compute_commitments(
183        committable_columns: &[crate::base::commitment::CommittableColumn],
184        offset: usize,
185        setup: &Self::PublicSetup<'_>,
186    ) -> Vec<Self> {
187        if committable_columns.is_empty() {
188            return Vec::new();
189        }
190
191        // Find the maximum length of the columns to get number of generators to use
192        let max_column_len = committable_columns
193            .iter()
194            .map(CommittableColumn::len)
195            .max()
196            .expect("You must have at least one column");
197
198        let mut blitzar_commitments = vec![G1Affine::default(); committable_columns.len()];
199
200        blitzar::compute::compute_bn254_g1_uncompressed_commitments_with_generators(
201            &mut blitzar_commitments,
202            &slice_ops::slice_cast(committable_columns),
203            &setup[offset..offset + max_column_len],
204        );
205
206        slice_ops::slice_cast(&blitzar_commitments)
207    }
208
209    fn to_transcript_bytes(&self) -> Vec<u8> {
210        let mut writer = Vec::with_capacity(self.commitment.compressed_size());
211        self.commitment.serialize_compressed(&mut writer).unwrap();
212        writer
213    }
214}
215
216#[cfg(test)]
217mod tests {
218    use super::*;
219    #[cfg(feature = "hyperkzg_proof")]
220    use crate::base::database::OwnedColumn;
221    use crate::base::{try_standard_binary_deserialization, try_standard_binary_serialization};
222    #[cfg(feature = "hyperkzg_proof")]
223    use crate::proof_primitive::hyperkzg::nova_commitment_key_to_hyperkzg_public_setup;
224    #[cfg(feature = "hyperkzg_proof")]
225    use crate::proof_primitive::hyperkzg::HyperKZGEngine;
226    use ark_ec::AffineRepr;
227    #[cfg(feature = "hyperkzg_proof")]
228    use nova_snark::provider::hyperkzg::{CommitmentEngine, CommitmentKey};
229    #[cfg(feature = "hyperkzg_proof")]
230    use nova_snark::traits::commitment::CommitmentEngineTrait;
231    #[cfg(feature = "hyperkzg_proof")]
232    use proptest::prelude::*;
233
234    #[test]
235    fn we_can_convert_default_point_to_a_hyperkzg_commitment_from_ark_bn254_g1_affine() {
236        let commitment: HyperKZGCommitment = HyperKZGCommitment::from(&G1Affine::default());
237        assert_eq!(commitment.commitment, G1Affine::default());
238    }
239
240    #[test]
241    fn we_can_convert_generator_to_a_hyperkzg_commitment_from_ark_bn254_g1_affine() {
242        let commitment: HyperKZGCommitment = (&G1Affine::generator()).into();
243        let expected: HyperKZGCommitment = HyperKZGCommitment::from(&G1Affine::generator());
244        assert_eq!(commitment.commitment, expected.commitment);
245    }
246
247    #[cfg(feature = "hyperkzg_proof")]
248    proptest! {
249        #[test]
250        fn blitzar_and_non_blitzar_commitments_are_equal(owned_column: OwnedColumn<BNScalar>) {
251            let ck: CommitmentKey<HyperKZGEngine> = CommitmentEngine::setup(b"test", owned_column.len());
252
253            let public_setup = nova_commitment_key_to_hyperkzg_public_setup(&ck);
254
255            let committable_columns = [CommittableColumn::from(&owned_column)];
256
257            let non_blitzar_commitments = compute_commitments_impl(&committable_columns, 0, &&public_setup[..]);
258            let blitzar_commitments = HyperKZGCommitment::compute_commitments(&committable_columns, 0, &&public_setup[..]);
259
260            prop_assert_eq!(non_blitzar_commitments, blitzar_commitments);
261        }
262    }
263
264    #[test]
265    fn we_can_serialize_and_deserialize_hyperkzg_commitment_generator() {
266        let commitment: HyperKZGCommitment = (&G1Affine::generator()).into();
267        let bytes = try_standard_binary_serialization(commitment).unwrap();
268        assert_eq!(bytes, [&[0u8; 31][..], &[1], &[0; 31], &[2]].concat());
269
270        let (deserialized_commitment, _): (HyperKZGCommitment, _) =
271            try_standard_binary_deserialization(&bytes[..]).unwrap();
272        assert_eq!(deserialized_commitment.commitment, G1Affine::generator());
273    }
274    #[test]
275    fn we_can_serialize_and_deserialize_hyperkzg_commitment_identity() {
276        let commitment: HyperKZGCommitment = (&G1Affine::identity()).into();
277        let bytes = try_standard_binary_serialization(commitment).unwrap();
278        assert_eq!(bytes, [&[0u8; 31][..], &[0], &[0; 31], &[0]].concat());
279
280        let (deserialized_commitment, _): (HyperKZGCommitment, _) =
281            try_standard_binary_deserialization(&bytes[..]).unwrap();
282        assert_eq!(deserialized_commitment.commitment, G1Affine::identity());
283    }
284    #[test]
285    fn we_can_round_trip_serialize_and_deserialize_random_hyperkzg_commitments() {
286        use ark_std::UniformRand;
287
288        let mut rng = ark_std::test_rng();
289
290        for _ in 0..100 {
291            let commitment: HyperKZGCommitment = (&G1Affine::rand(&mut rng)).into();
292            let bytes = try_standard_binary_serialization(commitment).unwrap();
293            let (deserialized_commitment, _): (HyperKZGCommitment, _) =
294                try_standard_binary_deserialization(&bytes[..]).unwrap();
295            assert_eq!(deserialized_commitment.commitment, commitment.commitment);
296        }
297    }
298}