Nova/src/gadgets/r1cs.rs
src/gadgets/r1cs.rsRaw
1//! This module implements various gadgets necessary for folding R1CS types.
2use super::nonnative::{
3 bignat::BigNat,
4 util::{f_to_nat, Num},
5};
6use crate::{
7 constants::{NUM_CHALLENGE_BITS, NUM_FE_FOR_RO},
8 gadgets::{
9 ecc::AllocatedPoint,
10 utils::{
11 alloc_bignat_constant, alloc_one, alloc_scalar_as_base, conditionally_select,
12 conditionally_select_bignat, le_bits_to_num,
13 },
14 },
15 r1cs::{R1CSInstance, RelaxedR1CSInstance},
16 traits::{commitment::CommitmentTrait, Group, ROCircuitTrait, ROConstantsCircuit},
17};
18use bellperson::{
19 gadgets::{boolean::Boolean, num::AllocatedNum, Assignment},
20 ConstraintSystem, SynthesisError,
21};
22use ff::Field;
23
24/// An Allocated R1CS Instance
25#[derive(Clone)]
26pub struct AllocatedR1CSInstance<G: Group> {
27 pub(crate) W: AllocatedPoint<G>,
28 pub(crate) X0: AllocatedNum<G::Base>,
29 pub(crate) X1: AllocatedNum<G::Base>,
30}
31
32impl<G: Group> AllocatedR1CSInstance<G> {
33 /// Takes the r1cs instance and creates a new allocated r1cs instance
34 pub fn alloc<CS: ConstraintSystem<<G as Group>::Base>>(
35 mut cs: CS,
36 u: Option<R1CSInstance<G>>,
37 ) -> Result<Self, SynthesisError> {
38 // Check that the incoming instance has exactly 2 io
39 let W = AllocatedPoint::alloc(
40 cs.namespace(|| "allocate W"),
41 u.get().map_or(None, |u| Some(u.comm_W.to_coordinates())),
42 )?;
43
44 let X0 = alloc_scalar_as_base::<G, _>(
45 cs.namespace(|| "allocate X[0]"),
46 u.get().map_or(None, |u| Some(u.X[0])),
47 )?;
48 let X1 = alloc_scalar_as_base::<G, _>(
49 cs.namespace(|| "allocate X[1]"),
50 u.get().map_or(None, |u| Some(u.X[1])),
51 )?;
52
53 Ok(AllocatedR1CSInstance { W, X0, X1 })
54 }
55
56 /// Absorb the provided instance in the RO
57 pub fn absorb_in_ro(&self, ro: &mut G::ROCircuit) {
58 ro.absorb(self.W.x.clone());
59 ro.absorb(self.W.y.clone());
60 ro.absorb(self.W.is_infinity.clone());
61 ro.absorb(self.X0.clone());
62 ro.absorb(self.X1.clone());
63 }
64}
65
66/// An Allocated Relaxed R1CS Instance
67pub struct AllocatedRelaxedR1CSInstance<G: Group> {
68 pub(crate) W: AllocatedPoint<G>,
69 pub(crate) E: AllocatedPoint<G>,
70 pub(crate) u: AllocatedNum<G::Base>,
71 pub(crate) X0: BigNat<G::Base>,
72 pub(crate) X1: BigNat<G::Base>,
73}
74
75impl<G: Group> AllocatedRelaxedR1CSInstance<G> {
76 /// Allocates the given RelaxedR1CSInstance as a witness of the circuit
77 pub fn alloc<CS: ConstraintSystem<<G as Group>::Base>>(
78 mut cs: CS,
79 inst: Option<RelaxedR1CSInstance<G>>,
80 limb_width: usize,
81 n_limbs: usize,
82 ) -> Result<Self, SynthesisError> {
83 let W = AllocatedPoint::alloc(
84 cs.namespace(|| "allocate W"),
85 inst
86 .get()
87 .map_or(None, |inst| Some(inst.comm_W.to_coordinates())),
88 )?;
89
90 let E = AllocatedPoint::alloc(
91 cs.namespace(|| "allocate E"),
92 inst
93 .get()
94 .map_or(None, |inst| Some(inst.comm_E.to_coordinates())),
95 )?;
96
97 // u << |G::Base| despite the fact that u is a scalar.
98 // So we parse all of its bytes as a G::Base element
99 let u = alloc_scalar_as_base::<G, _>(
100 cs.namespace(|| "allocate u"),
101 inst.get().map_or(None, |inst| Some(inst.u)),
102 )?;
103
104 // Allocate X0 and X1. If the input instance is None, then allocate default values 0.
105 let X0 = BigNat::alloc_from_nat(
106 cs.namespace(|| "allocate X[0]"),
107 || {
108 Ok(f_to_nat(
109 &inst.clone().map_or(G::Scalar::ZERO, |inst| inst.X[0]),
110 ))
111 },
112 limb_width,
113 n_limbs,
114 )?;
115
116 let X1 = BigNat::alloc_from_nat(
117 cs.namespace(|| "allocate X[1]"),
118 || {
119 Ok(f_to_nat(
120 &inst.clone().map_or(G::Scalar::ZERO, |inst| inst.X[1]),
121 ))
122 },
123 limb_width,
124 n_limbs,
125 )?;
126
127 Ok(AllocatedRelaxedR1CSInstance { W, E, u, X0, X1 })
128 }
129
130 /// Allocates the hardcoded default RelaxedR1CSInstance in the circuit.
131 /// W = E = 0, u = 1, X0 = X1 = 0
132 pub fn default<CS: ConstraintSystem<<G as Group>::Base>>(
133 mut cs: CS,
134 limb_width: usize,
135 n_limbs: usize,
136 ) -> Result<Self, SynthesisError> {
137 let W = AllocatedPoint::default(cs.namespace(|| "allocate W"))?;
138 let E = W.clone();
139
140 let u = W.x.clone(); // In the default case, W.x = u = 0
141
142 let X0 = BigNat::alloc_from_nat(
143 cs.namespace(|| "allocate x_default[0]"),
144 || Ok(f_to_nat(&G::Scalar::ZERO)),
145 limb_width,
146 n_limbs,
147 )?;
148
149 let X1 = BigNat::alloc_from_nat(
150 cs.namespace(|| "allocate x_default[1]"),
151 || Ok(f_to_nat(&G::Scalar::ZERO)),
152 limb_width,
153 n_limbs,
154 )?;
155
156 Ok(AllocatedRelaxedR1CSInstance { W, E, u, X0, X1 })
157 }
158
159 /// Allocates the R1CS Instance as a RelaxedR1CSInstance in the circuit.
160 /// E = 0, u = 1
161 pub fn from_r1cs_instance<CS: ConstraintSystem<<G as Group>::Base>>(
162 mut cs: CS,
163 inst: AllocatedR1CSInstance<G>,
164 limb_width: usize,
165 n_limbs: usize,
166 ) -> Result<Self, SynthesisError> {
167 let E = AllocatedPoint::default(cs.namespace(|| "allocate W"))?;
168
169 let u = alloc_one(cs.namespace(|| "one"))?;
170
171 let X0 = BigNat::from_num(
172 cs.namespace(|| "allocate X0 from relaxed r1cs"),
173 Num::from(inst.X0.clone()),
174 limb_width,
175 n_limbs,
176 )?;
177
178 let X1 = BigNat::from_num(
179 cs.namespace(|| "allocate X1 from relaxed r1cs"),
180 Num::from(inst.X1.clone()),
181 limb_width,
182 n_limbs,
183 )?;
184
185 Ok(AllocatedRelaxedR1CSInstance {
186 W: inst.W,
187 E,
188 u,
189 X0,
190 X1,
191 })
192 }
193
194 /// Absorb the provided instance in the RO
195 pub fn absorb_in_ro<CS: ConstraintSystem<<G as Group>::Base>>(
196 &self,
197 mut cs: CS,
198 ro: &mut G::ROCircuit,
199 ) -> Result<(), SynthesisError> {
200 ro.absorb(self.W.x.clone());
201 ro.absorb(self.W.y.clone());
202 ro.absorb(self.W.is_infinity.clone());
203 ro.absorb(self.E.x.clone());
204 ro.absorb(self.E.y.clone());
205 ro.absorb(self.E.is_infinity.clone());
206 ro.absorb(self.u.clone());
207
208 // Analyze X0 as limbs
209 let X0_bn = self
210 .X0
211 .as_limbs()
212 .iter()
213 .enumerate()
214 .map(|(i, limb)| {
215 limb.as_allocated_num(cs.namespace(|| format!("convert limb {i} of X_r[0] to num")))
216 })
217 .collect::<Result<Vec<AllocatedNum<G::Base>>, _>>()?;
218
219 // absorb each of the limbs of X[0]
220 for limb in X0_bn.into_iter() {
221 ro.absorb(limb);
222 }
223
224 // Analyze X1 as limbs
225 let X1_bn = self
226 .X1
227 .as_limbs()
228 .iter()
229 .enumerate()
230 .map(|(i, limb)| {
231 limb.as_allocated_num(cs.namespace(|| format!("convert limb {i} of X_r[1] to num")))
232 })
233 .collect::<Result<Vec<AllocatedNum<G::Base>>, _>>()?;
234
235 // absorb each of the limbs of X[1]
236 for limb in X1_bn.into_iter() {
237 ro.absorb(limb);
238 }
239
240 Ok(())
241 }
242
243 /// Folds self with a relaxed r1cs instance and returns the result
244 #[allow(clippy::too_many_arguments)]
245 pub fn fold_with_r1cs<CS: ConstraintSystem<<G as Group>::Base>>(
246 &self,
247 mut cs: CS,
248 params: AllocatedNum<G::Base>, // hash of R1CSShape of F'
249 u: AllocatedR1CSInstance<G>,
250 T: AllocatedPoint<G>,
251 ro_consts: ROConstantsCircuit<G>,
252 limb_width: usize,
253 n_limbs: usize,
254 ) -> Result<AllocatedRelaxedR1CSInstance<G>, SynthesisError> {
255 // Compute r:
256 let mut ro = G::ROCircuit::new(ro_consts, NUM_FE_FOR_RO);
257 ro.absorb(params);
258 self.absorb_in_ro(cs.namespace(|| "absorb running instance"), &mut ro)?;
259 u.absorb_in_ro(&mut ro);
260 ro.absorb(T.x.clone());
261 ro.absorb(T.y.clone());
262 ro.absorb(T.is_infinity.clone());
263 let r_bits = ro.squeeze(cs.namespace(|| "r bits"), NUM_CHALLENGE_BITS)?;
264 let r = le_bits_to_num(cs.namespace(|| "r"), r_bits.clone())?;
265
266 // W_fold = self.W + r * u.W
267 let rW = u.W.scalar_mul(cs.namespace(|| "r * u.W"), r_bits.clone())?;
268 let W_fold = self.W.add(cs.namespace(|| "self.W + r * u.W"), &rW)?;
269
270 // E_fold = self.E + r * T
271 let rT = T.scalar_mul(cs.namespace(|| "r * T"), r_bits)?;
272 let E_fold = self.E.add(cs.namespace(|| "self.E + r * T"), &rT)?;
273
274 // u_fold = u_r + r
275 let u_fold = AllocatedNum::alloc(cs.namespace(|| "u_fold"), || {
276 Ok(*self.u.get_value().get()? + r.get_value().get()?)
277 })?;
278 cs.enforce(
279 || "Check u_fold",
280 |lc| lc,
281 |lc| lc,
282 |lc| lc + u_fold.get_variable() - self.u.get_variable() - r.get_variable(),
283 );
284
285 // Fold the IO:
286 // Analyze r into limbs
287 let r_bn = BigNat::from_num(
288 cs.namespace(|| "allocate r_bn"),
289 Num::from(r.clone()),
290 limb_width,
291 n_limbs,
292 )?;
293
294 // Allocate the order of the non-native field as a constant
295 let m_bn = alloc_bignat_constant(
296 cs.namespace(|| "alloc m"),
297 &G::get_curve_params().2,
298 limb_width,
299 n_limbs,
300 )?;
301
302 // Analyze X0 to bignat
303 let X0_bn = BigNat::from_num(
304 cs.namespace(|| "allocate X0_bn"),
305 Num::from(u.X0.clone()),
306 limb_width,
307 n_limbs,
308 )?;
309
310 // Fold self.X[0] + r * X[0]
311 let (_, r_0) = X0_bn.mult_mod(cs.namespace(|| "r*X[0]"), &r_bn, &m_bn)?;
312 // add X_r[0]
313 let r_new_0 = self.X0.add(&r_0)?;
314 // Now reduce
315 let X0_fold = r_new_0.red_mod(cs.namespace(|| "reduce folded X[0]"), &m_bn)?;
316
317 // Analyze X1 to bignat
318 let X1_bn = BigNat::from_num(
319 cs.namespace(|| "allocate X1_bn"),
320 Num::from(u.X1.clone()),
321 limb_width,
322 n_limbs,
323 )?;
324
325 // Fold self.X[1] + r * X[1]
326 let (_, r_1) = X1_bn.mult_mod(cs.namespace(|| "r*X[1]"), &r_bn, &m_bn)?;
327 // add X_r[1]
328 let r_new_1 = self.X1.add(&r_1)?;
329 // Now reduce
330 let X1_fold = r_new_1.red_mod(cs.namespace(|| "reduce folded X[1]"), &m_bn)?;
331
332 Ok(Self {
333 W: W_fold,
334 E: E_fold,
335 u: u_fold,
336 X0: X0_fold,
337 X1: X1_fold,
338 })
339 }
340
341 /// If the condition is true then returns this otherwise it returns the other
342 pub fn conditionally_select<CS: ConstraintSystem<<G as Group>::Base>>(
343 &self,
344 mut cs: CS,
345 other: AllocatedRelaxedR1CSInstance<G>,
346 condition: &Boolean,
347 ) -> Result<AllocatedRelaxedR1CSInstance<G>, SynthesisError> {
348 let W = AllocatedPoint::conditionally_select(
349 cs.namespace(|| "W = cond ? self.W : other.W"),
350 &self.W,
351 &other.W,
352 condition,
353 )?;
354
355 let E = AllocatedPoint::conditionally_select(
356 cs.namespace(|| "E = cond ? self.E : other.E"),
357 &self.E,
358 &other.E,
359 condition,
360 )?;
361
362 let u = conditionally_select(
363 cs.namespace(|| "u = cond ? self.u : other.u"),
364 &self.u,
365 &other.u,
366 condition,
367 )?;
368
369 let X0 = conditionally_select_bignat(
370 cs.namespace(|| "X[0] = cond ? self.X[0] : other.X[0]"),
371 &self.X0,
372 &other.X0,
373 condition,
374 )?;
375
376 let X1 = conditionally_select_bignat(
377 cs.namespace(|| "X[1] = cond ? self.X[1] : other.X[1]"),
378 &self.X1,
379 &other.X1,
380 condition,
381 )?;
382
383 Ok(AllocatedRelaxedR1CSInstance { W, E, u, X0, X1 })
384 }
385}