Skip to content

Commit 512decf

Browse files
committed
Add FiatShamirRng
1 parent 2825b97 commit 512decf

File tree

6 files changed

+584
-8
lines changed

6 files changed

+584
-8
lines changed

src/constraints/ahp.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use crate::{
88
sponge::CryptographicSpongeVarNonNative,
99
CryptographicSpongeParameters, PhantomData, PrimeField, String, ToString, Vec,
1010
};
11-
use ark_nonnative_field::NonNativeFieldVar;
11+
use ark_nonnative_field::{params::OptimizationType, NonNativeFieldVar};
1212
use ark_poly::univariate::DensePolynomial;
1313
use ark_poly_commit::{
1414
EvaluationsVar, LCTerm, LabeledPointVar, LinearCombinationCoeffVar, LinearCombinationVar,
@@ -103,7 +103,7 @@ where
103103
elems.append(&mut comm.to_constraint_field().unwrap());
104104
});
105105
sponge_var.absorb(&elems)?;
106-
sponge_var.absorb_nonnative(&message)?;
106+
sponge_var.absorb_nonnative(&message, OptimizationType::Weight)?;
107107
}
108108

109109
// obtain four elements from the sponge_var
@@ -153,7 +153,7 @@ where
153153
elems.append(&mut comm.to_constraint_field().unwrap());
154154
});
155155
sponge_var.absorb(&elems)?;
156-
sponge_var.absorb_nonnative(&message)?;
156+
sponge_var.absorb_nonnative(&message, OptimizationType::Weight)?;
157157
}
158158

159159
// obtain one element from the sponge_var
@@ -195,7 +195,7 @@ where
195195
elems.append(&mut comm.to_constraint_field().unwrap());
196196
});
197197
sponge_var.absorb(&elems)?;
198-
sponge_var.absorb_nonnative(&message)?;
198+
sponge_var.absorb_nonnative(&message, OptimizationType::Weight)?;
199199
}
200200

201201
// obtain one element from the sponge_var

src/constraints/verifier.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use crate::{
55
CryptographicSpongeParameters, CryptographicSpongeWithRate, Error, PhantomData, PrimeField,
66
String, Vec,
77
};
8-
use ark_nonnative_field::NonNativeFieldVar;
8+
use ark_nonnative_field::{params::OptimizationType, NonNativeFieldVar};
99
use ark_poly::univariate::DensePolynomial;
1010
use ark_poly_commit::{PCCheckRandomDataVar, PCCheckVar, PolynomialCommitment};
1111
use ark_r1cs_std::{bits::boolean::Boolean, fields::FieldVar, R1CSVar, ToConstraintFieldGadget};
@@ -59,7 +59,7 @@ where
5959

6060
eprintln!("before AHP: constraints: {}", cs.num_constraints());
6161

62-
sponge_var.absorb_nonnative(&public_input)?;
62+
sponge_var.absorb_nonnative(&public_input, OptimizationType::Weight)?;
6363

6464
let (_, verifier_state) = AHPForR1CS::<F, CF, S, SVN, PC, PCG>::verifier_first_round(
6565
index_pvk.domain_h_size,
@@ -116,7 +116,7 @@ where
116116
}
117117
}
118118

119-
sponge_var.absorb_nonnative(&evals_vec)?;
119+
sponge_var.absorb_nonnative(&evals_vec, OptimizationType::Weight)?;
120120

121121
let (opening_challenges, opening_challenges_bits) =
122122
sponge_var.squeeze_nonnative_field_elements(num_opening_challenges)?;

src/fiat_shamir/constraints.rs

Lines changed: 301 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,301 @@
1+
use crate::{overhead, Vec};
2+
use ark_ff::PrimeField;
3+
use ark_nonnative_field::params::{get_params, OptimizationType};
4+
use ark_nonnative_field::{AllocatedNonNativeFieldVar, NonNativeFieldVar};
5+
use ark_r1cs_std::{
6+
alloc::AllocVar,
7+
bits::{uint8::UInt8, ToBitsGadget},
8+
boolean::Boolean,
9+
fields::fp::AllocatedFp,
10+
fields::fp::FpVar,
11+
R1CSVar,
12+
};
13+
use ark_relations::lc;
14+
use ark_relations::r1cs::{
15+
ConstraintSystemRef, LinearCombination, OptimizationGoal, SynthesisError,
16+
};
17+
use ark_sponge::constraints::{AbsorbGadget, CryptographicSpongeVar};
18+
use ark_sponge::CryptographicSponge;
19+
use core::marker::PhantomData;
20+
21+
/// Building the Fiat-Shamir sponge's gadget from any algebraic sponge's gadget.
22+
#[derive(Clone)]
23+
pub struct FiatShamirAlgebraicSpongeRngVar<
24+
F: PrimeField,
25+
CF: PrimeField,
26+
PS: CryptographicSponge,
27+
S: CryptographicSpongeVar<CF, PS>,
28+
> {
29+
pub cs: ConstraintSystemRef<CF>,
30+
pub s: S,
31+
#[doc(hidden)]
32+
f_phantom: PhantomData<F>,
33+
cf_phantom: PhantomData<CF>,
34+
ps_phantom: PhantomData<PS>,
35+
}
36+
37+
impl<F: PrimeField, CF: PrimeField, PS: CryptographicSponge, S: CryptographicSpongeVar<CF, PS>>
38+
FiatShamirAlgebraicSpongeRngVar<F, CF, PS, S>
39+
{
40+
/// Compress every two elements if possible. Provides a vector of (limb, num_of_additions),
41+
/// both of which are CF.
42+
#[tracing::instrument(target = "r1cs")]
43+
pub fn compress_gadgets(
44+
src_limbs: &[(FpVar<CF>, CF)],
45+
ty: OptimizationType,
46+
) -> Result<Vec<FpVar<CF>>, SynthesisError> {
47+
let capacity = CF::size_in_bits() - 1;
48+
let mut dest_limbs = Vec::<FpVar<CF>>::new();
49+
50+
if src_limbs.is_empty() {
51+
return Ok(vec![]);
52+
}
53+
54+
let params = get_params(F::size_in_bits(), CF::size_in_bits(), ty);
55+
56+
let adjustment_factor_lookup_table = {
57+
let mut table = Vec::<CF>::new();
58+
59+
let mut cur = CF::one();
60+
for _ in 1..=capacity {
61+
table.push(cur);
62+
cur.double_in_place();
63+
}
64+
65+
table
66+
};
67+
68+
let mut i: usize = 0;
69+
let src_len = src_limbs.len();
70+
while i < src_len {
71+
let first = &src_limbs[i];
72+
let second = if i + 1 < src_len {
73+
Some(&src_limbs[i + 1])
74+
} else {
75+
None
76+
};
77+
78+
let first_max_bits_per_limb = params.bits_per_limb + overhead!(first.1 + &CF::one());
79+
let second_max_bits_per_limb = if second.is_some() {
80+
params.bits_per_limb + overhead!(second.unwrap().1 + &CF::one())
81+
} else {
82+
0
83+
};
84+
85+
if second.is_some() && first_max_bits_per_limb + second_max_bits_per_limb <= capacity {
86+
let adjustment_factor = &adjustment_factor_lookup_table[second_max_bits_per_limb];
87+
88+
dest_limbs.push(&first.0 * *adjustment_factor + &second.unwrap().0);
89+
i += 2;
90+
} else {
91+
dest_limbs.push(first.0.clone());
92+
i += 1;
93+
}
94+
}
95+
96+
Ok(dest_limbs)
97+
}
98+
99+
/// Push gadgets to sponge.
100+
#[tracing::instrument(target = "r1cs", skip(sponge))]
101+
pub fn push_gadgets_to_sponge(
102+
sponge: &mut S,
103+
src: &[NonNativeFieldVar<F, CF>],
104+
ty: OptimizationType,
105+
) -> Result<(), SynthesisError> {
106+
let mut src_limbs: Vec<(FpVar<CF>, CF)> = Vec::new();
107+
108+
for elem in src.iter() {
109+
match elem {
110+
NonNativeFieldVar::Constant(c) => {
111+
let v = AllocatedNonNativeFieldVar::<F, CF>::new_constant(sponge.cs(), c)?;
112+
113+
for limb in v.limbs.iter() {
114+
let num_of_additions_over_normal_form =
115+
if v.num_of_additions_over_normal_form == CF::zero() {
116+
CF::one()
117+
} else {
118+
v.num_of_additions_over_normal_form
119+
};
120+
src_limbs.push((limb.clone(), num_of_additions_over_normal_form));
121+
}
122+
}
123+
NonNativeFieldVar::Var(v) => {
124+
for limb in v.limbs.iter() {
125+
let num_of_additions_over_normal_form =
126+
if v.num_of_additions_over_normal_form == CF::zero() {
127+
CF::one()
128+
} else {
129+
v.num_of_additions_over_normal_form
130+
};
131+
src_limbs.push((limb.clone(), num_of_additions_over_normal_form));
132+
}
133+
}
134+
}
135+
}
136+
137+
let dest_limbs = Self::compress_gadgets(&src_limbs, ty)?;
138+
sponge.absorb(&dest_limbs)?;
139+
Ok(())
140+
}
141+
142+
/// Obtain random bits from hashchain gadget. (Not guaranteed to be uniformly distributed,
143+
/// should only be used in certain situations.)
144+
#[tracing::instrument(target = "r1cs", skip(sponge))]
145+
pub fn get_booleans_from_sponge(
146+
sponge: &mut S,
147+
num_bits: usize,
148+
) -> Result<Vec<Boolean<CF>>, SynthesisError> {
149+
let bits_per_element = CF::size_in_bits() - 1;
150+
let num_elements = (num_bits + bits_per_element - 1) / bits_per_element;
151+
152+
let src_elements = sponge.squeeze_field_elements(num_elements)?;
153+
let mut dest_bits = Vec::<Boolean<CF>>::new();
154+
155+
for elem in src_elements.iter() {
156+
let elem_bits = elem.to_bits_be()?;
157+
dest_bits.extend_from_slice(&elem_bits[1..]); // discard the highest bit
158+
}
159+
160+
Ok(dest_bits)
161+
}
162+
163+
/// Obtain random elements from hashchain gadget. (Not guaranteed to be uniformly distributed,
164+
/// should only be used in certain situations.)
165+
#[tracing::instrument(target = "r1cs", skip(sponge))]
166+
pub fn get_gadgets_from_sponge(
167+
sponge: &mut S,
168+
num_elements: usize,
169+
outputs_short_elements: bool,
170+
) -> Result<Vec<NonNativeFieldVar<F, CF>>, SynthesisError> {
171+
let (dest_gadgets, _) =
172+
Self::get_gadgets_and_bits_from_sponge(sponge, num_elements, outputs_short_elements)?;
173+
174+
Ok(dest_gadgets)
175+
}
176+
177+
/// Obtain random elements, and the corresponding bits, from hashchain gadget. (Not guaranteed
178+
/// to be uniformly distributed, should only be used in certain situations.)
179+
#[tracing::instrument(target = "r1cs", skip(sponge))]
180+
#[allow(clippy::type_complexity)]
181+
pub fn get_gadgets_and_bits_from_sponge(
182+
sponge: &mut S,
183+
num_elements: usize,
184+
outputs_short_elements: bool,
185+
) -> Result<(Vec<NonNativeFieldVar<F, CF>>, Vec<Vec<Boolean<CF>>>), SynthesisError> {
186+
let cs = sponge.cs();
187+
188+
let optimization_type = match cs.optimization_goal() {
189+
OptimizationGoal::None => OptimizationType::Constraints,
190+
OptimizationGoal::Constraints => OptimizationType::Constraints,
191+
OptimizationGoal::Weight => OptimizationType::Weight,
192+
};
193+
194+
let params = get_params(F::size_in_bits(), CF::size_in_bits(), optimization_type);
195+
196+
let num_bits_per_nonnative = if outputs_short_elements {
197+
128
198+
} else {
199+
F::size_in_bits() - 1 // also omit the highest bit
200+
};
201+
let bits = Self::get_booleans_from_sponge(sponge, num_bits_per_nonnative * num_elements)?;
202+
203+
let mut lookup_table = Vec::<Vec<CF>>::new();
204+
let mut cur = F::one();
205+
for _ in 0..num_bits_per_nonnative {
206+
let repr = AllocatedNonNativeFieldVar::<F, CF>::get_limbs_representations(
207+
&cur,
208+
optimization_type,
209+
)?;
210+
lookup_table.push(repr);
211+
cur.double_in_place();
212+
}
213+
214+
let mut dest_gadgets = Vec::<NonNativeFieldVar<F, CF>>::new();
215+
let mut dest_bits = Vec::<Vec<Boolean<CF>>>::new();
216+
bits.chunks_exact(num_bits_per_nonnative)
217+
.for_each(|per_nonnative_bits| {
218+
let mut val = vec![CF::zero(); params.num_limbs];
219+
let mut lc = vec![LinearCombination::<CF>::zero(); params.num_limbs];
220+
221+
let mut per_nonnative_bits_le = per_nonnative_bits.to_vec();
222+
per_nonnative_bits_le.reverse();
223+
224+
dest_bits.push(per_nonnative_bits_le.clone());
225+
226+
for (j, bit) in per_nonnative_bits_le.iter().enumerate() {
227+
if bit.value().unwrap_or_default() {
228+
for (k, val) in val.iter_mut().enumerate().take(params.num_limbs) {
229+
*val += &lookup_table[j][k];
230+
}
231+
}
232+
233+
#[allow(clippy::needless_range_loop)]
234+
for k in 0..params.num_limbs {
235+
lc[k] = &lc[k] + bit.lc() * lookup_table[j][k];
236+
}
237+
}
238+
239+
let mut limbs = Vec::new();
240+
for k in 0..params.num_limbs {
241+
let gadget =
242+
AllocatedFp::new_witness(ark_relations::ns!(cs, "alloc"), || Ok(val[k]))
243+
.unwrap();
244+
lc[k] = lc[k].clone() - (CF::one(), gadget.variable);
245+
cs.enforce_constraint(lc!(), lc!(), lc[k].clone()).unwrap();
246+
limbs.push(FpVar::<CF>::from(gadget));
247+
}
248+
249+
dest_gadgets.push(NonNativeFieldVar::<F, CF>::Var(
250+
AllocatedNonNativeFieldVar::<F, CF> {
251+
cs: cs.clone(),
252+
limbs,
253+
num_of_additions_over_normal_form: CF::zero(),
254+
is_in_the_normal_form: true,
255+
target_phantom: Default::default(),
256+
},
257+
));
258+
});
259+
260+
Ok((dest_gadgets, dest_bits))
261+
}
262+
}
263+
264+
impl<F: PrimeField, CF: PrimeField, PS: CryptographicSponge, S: CryptographicSpongeVar<CF, PS>>
265+
CryptographicSpongeVar<CF, PS> for FiatShamirAlgebraicSpongeRngVar<F, CF, PS, S>
266+
{
267+
type Parameters = S::Parameters;
268+
269+
fn new(cs: ConstraintSystemRef<CF>, params: &Self::Parameters) -> Self {
270+
Self {
271+
cs: cs.clone(),
272+
s: S::new(cs, params),
273+
f_phantom: PhantomData,
274+
cf_phantom: PhantomData,
275+
ps_phantom: PhantomData,
276+
}
277+
}
278+
279+
fn cs(&self) -> ConstraintSystemRef<CF> {
280+
self.cs.clone()
281+
}
282+
283+
fn absorb(&mut self, input: &impl AbsorbGadget<CF>) -> Result<(), SynthesisError> {
284+
self.s.absorb(input)
285+
}
286+
287+
fn squeeze_bytes(&mut self, num_bytes: usize) -> Result<Vec<UInt8<CF>>, SynthesisError> {
288+
self.s.squeeze_bytes(num_bytes)
289+
}
290+
291+
fn squeeze_bits(&mut self, num_bits: usize) -> Result<Vec<Boolean<CF>>, SynthesisError> {
292+
self.s.squeeze_bits(num_bits)
293+
}
294+
295+
fn squeeze_field_elements(
296+
&mut self,
297+
num_elements: usize,
298+
) -> Result<Vec<FpVar<CF>>, SynthesisError> {
299+
self.s.squeeze_field_elements(num_elements)
300+
}
301+
}

0 commit comments

Comments
 (0)