Skip to content
This repository was archived by the owner on Dec 15, 2025. It is now read-only.

Commit 7748a7a

Browse files
authored
Sigma protocol for inhomogeneous tuple morphism (aptos-labs#18302)
* Made sigma protocol perform batch normalisations (from projective to affine), should be faster * Also refactored it a bit, now incorporating what was formerly combine_msm_terms() * Main change: Wrote hacky sigma protocol for homomorphisms which output both in E::G1 and E::G2
1 parent cf7b2e2 commit 7748a7a

File tree

5 files changed

+276
-103
lines changed

5 files changed

+276
-103
lines changed

crates/aptos-dkg/src/pvss/chunky/hkzg_chunked_elgamal.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -138,10 +138,9 @@ type LiftedWeightedChunkedElgamal<'a, E> = LiftHomomorphism<
138138
// TODO: note here that we had to put a zero before z_{i,j}, because that's what DeKARTv2 is doing. So maybe
139139
// it would make more sense to say this is a tuple homomorphism consisting of (lifts of) the
140140
// DeKARTv2::commitment_homomorphism together with the chunked_elgamal::homomorphism.
141-
pub type Homomorphism<'a, E> =
142-
TupleHomomorphism<LiftedHkzg<'a, E>, LiftedChunkedElgamal<'a, E>, true>;
141+
pub type Homomorphism<'a, E> = TupleHomomorphism<LiftedHkzg<'a, E>, LiftedChunkedElgamal<'a, E>>;
143142
pub type WeightedHomomorphism<'a, E> =
144-
TupleHomomorphism<LiftedHkzgWeighted<'a, E>, LiftedWeightedChunkedElgamal<'a, E>, true>;
143+
TupleHomomorphism<LiftedHkzgWeighted<'a, E>, LiftedWeightedChunkedElgamal<'a, E>>;
145144

146145
pub type Proof<'a, E> = sigma_protocol::Proof<<E as Pairing>::ScalarField, Homomorphism<'a, E>>;
147146
pub type WeightedProof<'a, E> =

crates/aptos-dkg/src/pvss/chunky/weighted_transcript.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -530,8 +530,8 @@ impl<const N: usize, P: FpConfig<N>, E: Pairing<ScalarField = Fp<P, N>>> NonAggr
530530
}
531531
if self.subtrs.Vs.len() != sc.get_total_num_players() {
532532
bail!(
533-
"Expected {} commitment elements, but got {}",
534-
sc.get_total_num_players() + 1,
533+
"Expected {} arrays of commitment elements, but got {}",
534+
sc.get_total_num_players(),
535535
self.subtrs.Vs.len()
536536
);
537537
}

crates/aptos-dkg/src/sigma_protocol/homomorphism/tuple.rs

Lines changed: 172 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@ use crate::{
88
homomorphism::{fixed_base_msms, EntrywiseMap},
99
},
1010
};
11-
use ark_ec::CurveGroup;
11+
use ark_ec::{pairing::Pairing, CurveGroup};
1212
use ark_serialize::{
1313
CanonicalDeserialize, CanonicalSerialize, Compress, Read, SerializationError, Valid,
1414
};
15-
pub use ark_std::io::Write;
15+
use ark_std::io::Write;
1616
use std::fmt::Debug;
1717

1818
/// `TupleHomomorphism` combines two homomorphisms with the same domain
@@ -28,7 +28,7 @@ use std::fmt::Debug;
2828
/// In category-theoretic terms, this is the composition of the diagonal map
2929
/// `Δ: Domain -> Domain × Domain` with the product map `h1 × h2`.
3030
#[derive(CanonicalSerialize, Debug, Clone, PartialEq, Eq)]
31-
pub struct TupleHomomorphism<H1, H2, const HOMOG: bool>
31+
pub struct TupleHomomorphism<H1, H2>
3232
where
3333
H1: homomorphism::Trait,
3434
H2: homomorphism::Trait<Domain = H1::Domain>,
@@ -37,14 +37,42 @@ where
3737
pub hom2: H2,
3838
}
3939

40+
#[derive(CanonicalSerialize, Debug, Clone, PartialEq, Eq)]
41+
pub struct PairingTupleHomomorphism<E, H1, H2>
42+
where
43+
E: Pairing,
44+
H1: homomorphism::Trait,
45+
H2: homomorphism::Trait<Domain = H1::Domain>,
46+
{
47+
pub hom1: H1,
48+
pub hom2: H2,
49+
pub _pairing: std::marker::PhantomData<E>,
50+
}
51+
4052
/// Implements `Homomorphism` for `TupleHomomorphism` by applying both
4153
/// component homomorphisms to the same input and returning their results
4254
/// as a tuple.
4355
///
4456
/// In other words, for input `x: Domain`, this produces
4557
/// `(hom1(x), hom2(x))`. For technical reasons, we then put the output inside a wrapper.
46-
impl<H1, H2, const HOMOG: bool> homomorphism::Trait for TupleHomomorphism<H1, H2, HOMOG>
58+
impl<H1, H2> homomorphism::Trait for TupleHomomorphism<H1, H2>
59+
where
60+
H1: homomorphism::Trait,
61+
H2: homomorphism::Trait<Domain = H1::Domain>,
62+
H1::Codomain: CanonicalSerialize + CanonicalDeserialize,
63+
H2::Codomain: CanonicalSerialize + CanonicalDeserialize,
64+
{
65+
type Codomain = TupleCodomainShape<H1::Codomain, H2::Codomain>;
66+
type Domain = H1::Domain;
67+
68+
fn apply(&self, x: &Self::Domain) -> Self::Codomain {
69+
TupleCodomainShape(self.hom1.apply(x), self.hom2.apply(x))
70+
}
71+
}
72+
73+
impl<E, H1, H2> homomorphism::Trait for PairingTupleHomomorphism<E, H1, H2>
4774
where
75+
E: Pairing,
4876
H1: homomorphism::Trait,
4977
H2: homomorphism::Trait<Domain = H1::Domain>,
5078
H1::Codomain: CanonicalSerialize + CanonicalDeserialize,
@@ -152,7 +180,7 @@ where
152180
/// not necessary through enums.
153181
///
154182
/// The codomain shapes of the two homomorphisms are combined using `TupleCodomainShape`.
155-
impl<H1, H2> fixed_base_msms::Trait for TupleHomomorphism<H1, H2, true>
183+
impl<H1, H2> fixed_base_msms::Trait for TupleHomomorphism<H1, H2>
156184
where
157185
H1: fixed_base_msms::Trait,
158186
H2: fixed_base_msms::Trait<
@@ -181,7 +209,7 @@ where
181209
}
182210
}
183211

184-
impl<C: CurveGroup, H1, H2> sigma_protocol::Trait<C> for TupleHomomorphism<H1, H2, true>
212+
impl<C: CurveGroup, H1, H2> sigma_protocol::Trait<C> for TupleHomomorphism<H1, H2>
185213
where
186214
H1: sigma_protocol::Trait<C>,
187215
H2: sigma_protocol::Trait<C>,
@@ -207,3 +235,141 @@ where
207235
dst
208236
}
209237
}
238+
239+
use crate::sigma_protocol::{
240+
traits::{fiat_shamir_challenge_for_sigma_protocol, prove_homomorphism, FirstProofItem},
241+
Proof, Witness,
242+
};
243+
use anyhow::ensure;
244+
use aptos_crypto::utils;
245+
use ark_ff::{UniformRand, Zero};
246+
use serde::Serialize;
247+
248+
// Slightly hacky implementation of a sigma protocol
249+
impl<E: Pairing, H1, H2> PairingTupleHomomorphism<E, H1, H2>
250+
where
251+
H1: sigma_protocol::Trait<E::G1>,
252+
H2: sigma_protocol::Trait<E::G2>,
253+
H1: fixed_base_msms::Trait<Domain: Witness<H1::Scalar>>,
254+
H2: fixed_base_msms::Trait<Domain = H1::Domain, Scalar = H1::Scalar>,
255+
{
256+
fn dst(&self) -> Vec<u8> {
257+
let mut dst = Vec::new();
258+
259+
let dst1 = self.hom1.dst();
260+
let dst2 = self.hom2.dst();
261+
262+
// Domain-separate them properly so concatenation is unambiguous.
263+
// Prefix with their lengths so [a|b] and [ab|] don't collide.
264+
dst.extend_from_slice(b"PairingTupleHomomorphism(");
265+
dst.extend_from_slice(&(dst1.len() as u32).to_be_bytes());
266+
dst.extend_from_slice(&dst1);
267+
dst.extend_from_slice(&(dst2.len() as u32).to_be_bytes());
268+
dst.extend_from_slice(&dst2);
269+
dst.extend_from_slice(b")");
270+
271+
dst
272+
}
273+
274+
/// Returns the MSM terms for each homomorphism, combined into a tuple.
275+
fn msm_terms(
276+
&self,
277+
input: &H1::Domain,
278+
) -> (
279+
H1::CodomainShape<H1::MsmInput>,
280+
H2::CodomainShape<H2::MsmInput>,
281+
) {
282+
let terms1 = self.hom1.msm_terms(input);
283+
let terms2 = self.hom2.msm_terms(input);
284+
(terms1, terms2)
285+
}
286+
287+
pub fn prove<Ct: Serialize, R: rand_core::RngCore + rand_core::CryptoRng>(
288+
&self,
289+
witness: &<Self as homomorphism::Trait>::Domain,
290+
statement: &<Self as homomorphism::Trait>::Codomain,
291+
cntxt: &Ct, // for SoK purposes
292+
rng: &mut R,
293+
) -> Proof<H1::Scalar, Self> {
294+
prove_homomorphism(self, witness, statement, cntxt, true, rng, &self.dst())
295+
}
296+
297+
#[allow(non_snake_case)]
298+
pub fn verify<C: Serialize, H>(
299+
&self,
300+
public_statement: &<Self as homomorphism::Trait>::Codomain,
301+
proof: &Proof<H1::Scalar, H>, // Would like to set &Proof<E, Self>, but that ties the lifetime of H to that of Self, but we'd like it to be eg static
302+
cntxt: &C,
303+
) -> anyhow::Result<()>
304+
where
305+
H: homomorphism::Trait<
306+
Domain = <Self as homomorphism::Trait>::Domain,
307+
Codomain = <Self as homomorphism::Trait>::Codomain,
308+
>,
309+
{
310+
let (first_msm_terms, second_msm_terms) =
311+
self.msm_terms_for_verify::<_, H>(public_statement, proof, cntxt);
312+
313+
let first_msm_result = H1::msm_eval(first_msm_terms);
314+
ensure!(first_msm_result == H1::MsmOutput::zero());
315+
316+
let second_msm_result = H2::msm_eval(second_msm_terms);
317+
ensure!(second_msm_result == H2::MsmOutput::zero());
318+
319+
Ok(())
320+
}
321+
322+
#[allow(non_snake_case)]
323+
fn msm_terms_for_verify<Ct: Serialize, H>(
324+
&self,
325+
public_statement: &<Self as homomorphism::Trait>::Codomain,
326+
proof: &Proof<H1::Scalar, H>,
327+
cntxt: &Ct,
328+
) -> (H1::MsmInput, H2::MsmInput)
329+
where
330+
H: homomorphism::Trait<
331+
Domain = <Self as homomorphism::Trait>::Domain,
332+
Codomain = <Self as homomorphism::Trait>::Codomain,
333+
>, // need this?
334+
{
335+
let prover_first_message = match &proof.first_proof_item {
336+
FirstProofItem::Commitment(A) => A,
337+
FirstProofItem::Challenge(_) => {
338+
panic!("Missing implementation - expected commitment, not challenge")
339+
},
340+
};
341+
let c = fiat_shamir_challenge_for_sigma_protocol::<_, H1::Scalar, _>(
342+
cntxt,
343+
self,
344+
public_statement,
345+
&prover_first_message,
346+
&self.dst(),
347+
);
348+
349+
let mut rng = ark_std::rand::thread_rng(); // TODO: make this part of the function input?
350+
let beta = H1::Scalar::rand(&mut rng);
351+
let len1 = public_statement.0.clone().into_iter().count(); // hmm maybe pass the into_iter version in merge_msm_terms?
352+
let len2 = public_statement.1.clone().into_iter().count();
353+
let powers_of_beta = utils::powers(beta, len1 + len2);
354+
let (first_powers_of_beta, second_powers_of_beta) = powers_of_beta.split_at(len1);
355+
356+
let (first_msm_terms_of_response, second_msm_terms_of_response) = self.msm_terms(&proof.z);
357+
358+
let first_input = H1::merge_msm_terms(
359+
first_msm_terms_of_response.into_iter().collect(),
360+
&prover_first_message.0,
361+
&public_statement.0,
362+
first_powers_of_beta,
363+
c,
364+
);
365+
let second_input = H2::merge_msm_terms(
366+
second_msm_terms_of_response.into_iter().collect(),
367+
&prover_first_message.1,
368+
&public_statement.1,
369+
second_powers_of_beta,
370+
c,
371+
);
372+
373+
(first_input, second_input)
374+
}
375+
}

0 commit comments

Comments
 (0)