Skip to main content

nova_snark/gadgets/nonnative/
util.rs

1use 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)]
10/// A representation of a bit
11pub struct Bit<Scalar: PrimeField> {
12  /// The linear combination which constrains the value of the bit
13  pub bit: LinearCombination<Scalar>,
14}
15
16#[derive(Clone)]
17/// A representation of a bit-vector
18pub struct Bitvector<Scalar: PrimeField> {
19  /// The linear combination which constrains the values of the bits
20  pub bits: Vec<LinearCombination<Scalar>>,
21  /// The value of the bits (filled at witness-time)
22  pub values: Option<Vec<bool>>,
23  /// Allocated bit variables
24  pub allocations: Vec<Bit<Scalar>>,
25}
26
27impl<Scalar: PrimeField> Bit<Scalar> {
28  /// Allocate a variable in the constraint system which can only be a
29  /// boolean value.
30  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    // Constrain: (1 - a) * a = 0
46    // This constrains a to be either 0 or 1.
47    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
60/// A representation of a field element as a linear combination with an optional value.
61pub struct Num<Scalar: PrimeField> {
62  /// The linear combination representing the number.
63  pub num: LinearCombination<Scalar>,
64  /// The value of the number (filled at witness-time).
65  pub value: Option<Scalar>,
66}
67
68impl<Scalar: PrimeField> Num<Scalar> {
69  /// Creates a new `Num` with the given value and linear combination.
70  pub const fn new(value: Option<Scalar>, num: LinearCombination<Scalar>) -> Self {
71    Self { value, num }
72  }
73  /// Allocates a new `Num` in the constraint system with the given value.
74  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  /// Checks that the `Num` fits in the given number of bits.
98  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    // Allocate all but the first bit.
106    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    // Last bit
132    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  /// Computes the natural number represented by an array of bits.
159  /// Checks if the natural number equals `self`
160  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 - &sum;
171    cs.enforce(|| "sum", |lc| lc + &sum_lc, |lc| lc + CS::one(), |lc| lc);
172  }
173
174  /// Compute the natural number represented by an array of limbs.
175  /// The limbs are assumed to be based the `limb_width` power of 2.
176  /// Low-index bits are low-order
177  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 - &sum;
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  /// Converts the `Num` to an `AllocatedNum` in the constraint system.
217  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
238/// Convert a field element to a natural number
239pub fn f_to_nat<Scalar: PrimeField>(f: &Scalar) -> BigInt {
240  BigInt::from_bytes_le(Sign::Plus, f.to_repr().as_ref())
241}
242
243/// Convert a natural number to a field element.
244/// Returns `None` if the number is too big for the field.
245pub 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
256/// Get the base field modulus as a BigInt.
257pub fn get_base_modulus<E: Engine>() -> BigInt {
258  E::GE::group_params().3
259}
260
261/// Absorb a BigNat into a random oracle circuit (base field version).
262pub 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  // absorb each limb directly (no packing - limbs are not constrained to be small)
275  for limb in limbs {
276    ro.absorb(&limb);
277  }
278
279  Ok(())
280}
281
282/// Absorb a BigNat into a random oracle circuit (scalar field version).
283pub 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  // absorb each limb directly (no packing - limbs are not constrained to be small)
296  for limb in limbs {
297    ro.absorb(&limb);
298  }
299  Ok(())
300}
301
302/// Absorb a scalar field element into a random oracle (native version).
303pub 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  // absorb each element of x in bignum format
309  let limbs: Vec<E::Base> = nat_to_limbs(&f_to_nat(e), BN_LIMB_WIDTH, BN_N_LIMBS)?;
310  // absorb each limb directly (no packing - limbs are not constrained to be small)
311  for limb in limbs {
312    ro.absorb(limb);
313  }
314  Ok(())
315}
316
317/// Fingerprint a BigNat by fingerprinting each of its limbs.
318pub 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  // Analyze bignat as limbs
326  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  // fingerprint the limbs
336  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}