proof_of_sql/proof_primitive/hyperkzg/
commitment.rs1use 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#[derive(Clone, Copy, Debug, PartialEq, Eq, CanonicalSerialize, CanonicalDeserialize, Default)]
16pub struct HyperKZGCommitment {
17 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 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}