nova_snark/gadgets/nonnative/
util.rs1use super::{BitAccess, OptionExt};
2use crate::frontend::{
3 num::AllocatedNum, ConstraintSystem, LinearCombination, SynthesisError, Variable,
4};
5use ff::PrimeField;
6use num_bigint::{BigInt, Sign};
7use std::convert::From;
8
9#[derive(Clone)]
10pub struct Bit<Scalar: PrimeField> {
12 pub bit: LinearCombination<Scalar>,
14}
15
16#[derive(Clone)]
17pub struct Bitvector<Scalar: PrimeField> {
19 pub bits: Vec<LinearCombination<Scalar>>,
21 pub values: Option<Vec<bool>>,
23 pub allocations: Vec<Bit<Scalar>>,
25}
26
27impl<Scalar: PrimeField> Bit<Scalar> {
28 pub fn alloc<CS: ConstraintSystem<Scalar>>(
31 mut cs: CS,
32 value: Option<bool>,
33 ) -> Result<Self, SynthesisError> {
34 let var = cs.alloc(
35 || "boolean",
36 || {
37 if *value.grab()? {
38 Ok(Scalar::ONE)
39 } else {
40 Ok(Scalar::ZERO)
41 }
42 },
43 )?;
44
45 cs.enforce(
48 || "boolean constraint",
49 |lc| lc + CS::one() - var,
50 |lc| lc + var,
51 |lc| lc,
52 );
53
54 Ok(Self {
55 bit: LinearCombination::zero() + var,
56 })
57 }
58}
59
60pub struct Num<Scalar: PrimeField> {
62 pub num: LinearCombination<Scalar>,
64 pub value: Option<Scalar>,
66}
67
68impl<Scalar: PrimeField> Num<Scalar> {
69 pub const fn new(value: Option<Scalar>, num: LinearCombination<Scalar>) -> Self {
71 Self { value, num }
72 }
73 pub fn alloc<CS, F>(mut cs: CS, value: F) -> Result<Self, SynthesisError>
75 where
76 CS: ConstraintSystem<Scalar>,
77 F: FnOnce() -> Result<Scalar, SynthesisError>,
78 {
79 let mut new_value = None;
80 let var = cs.alloc(
81 || "num",
82 || {
83 let tmp = value()?;
84
85 new_value = Some(tmp);
86
87 Ok(tmp)
88 },
89 )?;
90
91 Ok(Num {
92 value: new_value,
93 num: LinearCombination::zero() + var,
94 })
95 }
96
97 pub fn fits_in_bits<CS: ConstraintSystem<Scalar>>(
99 &self,
100 mut cs: CS,
101 n_bits: usize,
102 ) -> Result<(), SynthesisError> {
103 let v = self.value;
104
105 let bits: Vec<Variable> = (1..n_bits)
107 .map(|i| {
108 cs.alloc(
109 || format!("bit {i}"),
110 || {
111 let r = if *v.grab()?.get_bit(i).grab()? {
112 Scalar::ONE
113 } else {
114 Scalar::ZERO
115 };
116 Ok(r)
117 },
118 )
119 })
120 .collect::<Result<_, _>>()?;
121
122 for (i, v) in bits.iter().enumerate() {
123 cs.enforce(
124 || format!("{i} is bit"),
125 |lc| lc + *v,
126 |lc| lc + CS::one() - *v,
127 |lc| lc,
128 )
129 }
130
131 cs.enforce(
133 || "last bit",
134 |mut lc| {
135 let mut f = Scalar::ONE;
136 lc = lc + &self.num;
137 for v in bits.iter() {
138 f = f.double();
139 lc = lc - (f, *v);
140 }
141 lc
142 },
143 |mut lc| {
144 lc = lc + CS::one();
145 let mut f = Scalar::ONE;
146 lc = lc - &self.num;
147 for v in bits.iter() {
148 f = f.double();
149 lc = lc + (f, *v);
150 }
151 lc
152 },
153 |lc| lc,
154 );
155 Ok(())
156 }
157
158 pub fn is_equal<CS: ConstraintSystem<Scalar>>(&self, mut cs: CS, other: &Bitvector<Scalar>) {
161 let allocations = other.allocations.clone();
162 let mut f = Scalar::ONE;
163 let sum = allocations
164 .iter()
165 .fold(LinearCombination::zero(), |lc, bit| {
166 let l = lc + (f, &bit.bit);
167 f = f.double();
168 l
169 });
170 let sum_lc = LinearCombination::zero() + &self.num - ∑
171 cs.enforce(|| "sum", |lc| lc + &sum_lc, |lc| lc + CS::one(), |lc| lc);
172 }
173
174 pub fn decompose<CS: ConstraintSystem<Scalar>>(
178 &self,
179 mut cs: CS,
180 n_bits: usize,
181 ) -> Result<Bitvector<Scalar>, SynthesisError> {
182 let values: Option<Vec<bool>> = self.value.as_ref().map(|v| {
183 let num = *v;
184 (0..n_bits).map(|i| num.get_bit(i).unwrap()).collect()
185 });
186 let allocations: Vec<Bit<Scalar>> = (0..n_bits)
187 .map(|bit_i| {
188 Bit::alloc(
189 cs.namespace(|| format!("bit{bit_i}")),
190 values.as_ref().map(|vs| vs[bit_i]),
191 )
192 })
193 .collect::<Result<Vec<_>, _>>()?;
194 let mut f = Scalar::ONE;
195 let sum = allocations
196 .iter()
197 .fold(LinearCombination::zero(), |lc, bit| {
198 let l = lc + (f, &bit.bit);
199 f = f.double();
200 l
201 });
202 let sum_lc = LinearCombination::zero() + &self.num - ∑
203 cs.enforce(|| "sum", |lc| lc + &sum_lc, |lc| lc + CS::one(), |lc| lc);
204 let bits: Vec<LinearCombination<Scalar>> = allocations
205 .clone()
206 .into_iter()
207 .map(|a| LinearCombination::zero() + &a.bit)
208 .collect();
209 Ok(Bitvector {
210 allocations,
211 values,
212 bits,
213 })
214 }
215
216 pub fn as_allocated_num<CS: ConstraintSystem<Scalar>>(
218 &self,
219 mut cs: CS,
220 ) -> Result<AllocatedNum<Scalar>, SynthesisError> {
221 let new = AllocatedNum::alloc(cs.namespace(|| "alloc"), || Ok(*self.value.grab()?))?;
222 cs.enforce(
223 || "eq",
224 |lc| lc,
225 |lc| lc,
226 |lc| lc + new.get_variable() - &self.num,
227 );
228 Ok(new)
229 }
230}
231
232impl<Scalar: PrimeField> From<AllocatedNum<Scalar>> for Num<Scalar> {
233 fn from(a: AllocatedNum<Scalar>) -> Self {
234 Self::new(a.get_value(), LinearCombination::zero() + a.get_variable())
235 }
236}
237
238pub fn f_to_nat<Scalar: PrimeField>(f: &Scalar) -> BigInt {
240 BigInt::from_bytes_le(Sign::Plus, f.to_repr().as_ref())
241}
242
243pub fn nat_to_f<Scalar: PrimeField>(n: &BigInt) -> Option<Scalar> {
246 Scalar::from_str_vartime(&format!("{n}"))
247}
248
249use super::bignat::BigNat;
250use crate::{
251 constants::{BN_LIMB_WIDTH, BN_N_LIMBS},
252 gadgets::utils::fingerprint,
253 traits::{Engine, Group, ROCircuitTrait, ROTrait},
254};
255
256pub fn get_base_modulus<E: Engine>() -> BigInt {
258 E::GE::group_params().3
259}
260
261pub fn absorb_bignat_in_ro<E: Engine, CS: ConstraintSystem<E::Base>>(
263 n: &BigNat<E::Base>,
264 mut cs: CS,
265 ro: &mut E::ROCircuit,
266) -> Result<(), SynthesisError> {
267 let limbs = n
268 .as_limbs()
269 .iter()
270 .enumerate()
271 .map(|(i, limb)| limb.as_allocated_num(cs.namespace(|| format!("convert limb {i} of num"))))
272 .collect::<Result<Vec<AllocatedNum<E::Base>>, _>>()?;
273
274 for limb in limbs {
276 ro.absorb(&limb);
277 }
278
279 Ok(())
280}
281
282pub fn absorb_bignat_in_ro_scalar<E: Engine, CS: ConstraintSystem<E::Scalar>>(
284 n: &BigNat<E::Scalar>,
285 mut cs: CS,
286 ro: &mut E::RO2Circuit,
287) -> Result<(), SynthesisError> {
288 let limbs = n
289 .as_limbs()
290 .iter()
291 .enumerate()
292 .map(|(i, limb)| limb.as_allocated_num(cs.namespace(|| format!("convert limb {i} of num"))))
293 .collect::<Result<Vec<AllocatedNum<E::Scalar>>, _>>()?;
294
295 for limb in limbs {
297 ro.absorb(&limb);
298 }
299 Ok(())
300}
301
302pub fn absorb_bignat_in_ro_native<E: Engine>(
304 e: &E::Scalar,
305 ro: &mut E::RO,
306) -> Result<(), SynthesisError> {
307 use super::bignat::nat_to_limbs;
308 let limbs: Vec<E::Base> = nat_to_limbs(&f_to_nat(e), BN_LIMB_WIDTH, BN_N_LIMBS)?;
310 for limb in limbs {
312 ro.absorb(limb);
313 }
314 Ok(())
315}
316
317pub fn fingerprint_bignat<E: Engine, CS: ConstraintSystem<E::Base>>(
319 mut cs: CS,
320 acc: &AllocatedNum<E::Base>,
321 c: &AllocatedNum<E::Base>,
322 c_i: &AllocatedNum<E::Base>,
323 bn: &BigNat<E::Base>,
324) -> Result<(AllocatedNum<E::Base>, AllocatedNum<E::Base>), SynthesisError> {
325 let limbs = bn
327 .as_limbs()
328 .iter()
329 .enumerate()
330 .map(|(i, limb)| {
331 limb.as_allocated_num(cs.namespace(|| format!("convert limb {i} of x to num")))
332 })
333 .collect::<Result<Vec<AllocatedNum<E::Base>>, _>>()?;
334
335 let mut acc_out = acc.clone();
337 let mut c_i_out = c_i.clone();
338 for (i, limb) in limbs.iter().enumerate() {
339 (acc_out, c_i_out) = fingerprint::<E::Base, _>(
340 cs.namespace(|| format!("output limb_{i}")),
341 &acc_out,
342 c,
343 &c_i_out,
344 limb,
345 )?;
346 }
347
348 Ok((acc_out, c_i_out))
349}