proof_of_sql/proof_primitive/hyperkzg/
commitment.rs

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