1use std::{
2 fmt::{Debug, Display},
3 iter::zip,
4};
5
6mod div;
7mod from;
8mod ops;
9
10#[cfg(test)]
11mod tests;
12
13use ark_ff::{BigInteger, PrimeField, ToConstraintField, Zero};
14use ark_r1cs_std::bits::uint64::UInt64;
15use ark_r1cs_std::fields::fp::FpVar;
16use ark_r1cs_std::prelude::*;
17use ark_relations::r1cs::{ConstraintSystemRef, SynthesisError};
18
19use decaf377::{r1cs::FqVar, Fq};
20use ethnum::U256;
21
22use crate::{Amount, AmountVar};
23
24use self::div::stub_div_rem_u384_by_u256;
25
26#[derive(thiserror::Error, Debug)]
27pub enum Error {
28 #[error("overflow")]
29 Overflow,
30 #[error("underflow")]
31 Underflow,
32 #[error("division by zero")]
33 DivisionByZero,
34 #[error("attempted to convert invalid f64: {value:?} to a U128x128")]
35 InvalidFloat64 { value: f64 },
36 #[error("attempted to convert non-integral value {value:?} to an integer")]
37 NonIntegral { value: U128x128 },
38 #[error("attempted to decode a slice of the wrong length {0}, expected 32")]
39 SliceLength(usize),
40}
41
42#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
43pub struct U128x128(U256);
44
45impl Default for U128x128 {
46 fn default() -> Self {
47 Self::from(0u64)
48 }
49}
50
51impl Debug for U128x128 {
52 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
53 let (integral, fractional) = self.0.into_words();
54 f.debug_struct("U128x128")
55 .field("integral", &integral)
56 .field("fractional", &fractional)
57 .finish()
58 }
59}
60
61impl Display for U128x128 {
62 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
63 write!(f, "{}", f64::from(*self))
64 }
65}
66
67impl U128x128 {
68 pub fn to_bytes(self) -> [u8; 32] {
74 let mut bytes = [0u8; 32];
77 let (hi, lo) = self.0.into_words();
78 bytes[0..16].copy_from_slice(&hi.to_be_bytes());
79 bytes[16..32].copy_from_slice(&lo.to_be_bytes());
80 bytes
81 }
82
83 pub fn from_bytes(bytes: [u8; 32]) -> Self {
85 let hi = u128::from_be_bytes(bytes[0..16].try_into().expect("slice is 16 bytes"));
87 let lo = u128::from_be_bytes(bytes[16..32].try_into().expect("slice is 16 bytes"));
88 Self(U256::from_words(hi, lo))
89 }
90
91 pub fn ratio<T: Into<Self>>(numerator: T, denominator: T) -> Result<Self, Error> {
92 numerator.into() / denominator.into()
93 }
94
95 pub fn is_integral(&self) -> bool {
97 let fractional_word = self.0.into_words().1;
98 fractional_word == 0
99 }
100
101 pub fn round_down(self) -> Self {
103 let integral_word = self.0.into_words().0;
104 Self(U256::from_words(integral_word, 0u128))
105 }
106
107 pub fn round_up(&self) -> Result<Self, Error> {
109 let (integral, fractional) = self.0.into_words();
110 if fractional == 0 {
111 Ok(*self)
112 } else {
113 let integral = integral.checked_add(1).ok_or(Error::Overflow)?;
114 Ok(Self(U256::from_words(integral, 0u128)))
115 }
116 }
117
118 pub fn checked_mul(self, rhs: &Self) -> Result<Self, Error> {
120 let (x1, x0) = self.0.into_words();
124 let (y1, y0) = rhs.0.into_words();
125 let x0 = U256::from(x0);
126 let x1 = U256::from(x1);
127 let y0 = U256::from(y0);
128 let y1 = U256::from(y1);
129
130 let x0y0 = x0 * y0; let x0y1 = x0 * y1; let x1y0 = x1 * y0; let x1y1 = x1 * y1; let (x1y1_hi, _x1y1_lo) = x1y1.into_words();
143 if x1y1_hi != 0 {
144 return Err(Error::Overflow);
145 }
146
147 x1y1.checked_shl(128)
148 .and_then(|acc| acc.checked_add(x0y1))
149 .and_then(|acc| acc.checked_add(x1y0))
150 .and_then(|acc| acc.checked_add(x0y0 >> 128))
151 .map(U128x128)
152 .ok_or(Error::Overflow)
153 }
154
155 pub fn checked_div(self, rhs: &Self) -> Result<Self, Error> {
157 stub_div_rem_u384_by_u256(self.0, rhs.0).map(|(quo, _rem)| U128x128(quo))
158 }
159
160 pub fn checked_add(self, rhs: &Self) -> Result<Self, Error> {
162 self.0
163 .checked_add(rhs.0)
164 .map(U128x128)
165 .ok_or(Error::Overflow)
166 }
167
168 pub fn checked_sub(self, rhs: &Self) -> Result<Self, Error> {
170 self.0
171 .checked_sub(rhs.0)
172 .map(U128x128)
173 .ok_or(Error::Underflow)
174 }
175
176 pub fn saturating_sub(self, rhs: &Self) -> Self {
178 U128x128(self.0.saturating_sub(rhs.0))
179 }
180
181 pub fn apply_to_amount(self, rhs: &Amount) -> Result<Amount, Error> {
183 let mul = (Self::from(rhs) * self)?;
184 let out = mul
185 .round_down()
186 .try_into()
187 .expect("converting integral U128xU128 into Amount will succeed");
188 Ok(out)
189 }
190}
191
192#[derive(Clone)]
193pub struct U128x128Var {
194 pub limbs: [UInt64<Fq>; 4],
195}
196
197impl AllocVar<U128x128, Fq> for U128x128Var {
198 fn new_variable<T: std::borrow::Borrow<U128x128>>(
199 cs: impl Into<ark_relations::r1cs::Namespace<Fq>>,
200 f: impl FnOnce() -> Result<T, SynthesisError>,
201 mode: ark_r1cs_std::prelude::AllocationMode,
202 ) -> Result<Self, SynthesisError> {
203 let ns = cs.into();
204 let cs = ns.cs();
205 let inner: U128x128 = *f()?.borrow();
206
207 let (hi_128, lo_128) = inner.0.into_words();
213 let hi_128_var = FqVar::new_variable(cs.clone(), || Ok(Fq::from(hi_128)), mode)?;
214 let lo_128_var = FqVar::new_variable(cs.clone(), || Ok(Fq::from(lo_128)), mode)?;
215
216 let bytes = inner.to_bytes();
218 let limb_3 = u64::from_be_bytes(bytes[0..8].try_into().expect("slice is 8 bytes"));
220 let limb_2 = u64::from_be_bytes(bytes[8..16].try_into().expect("slice is 8 bytes"));
221 let limb_1 = u64::from_be_bytes(bytes[16..24].try_into().expect("slice is 8 bytes"));
222 let limb_0 = u64::from_be_bytes(bytes[24..32].try_into().expect("slice is 8 bytes"));
223
224 let limb_0_var = UInt64::new_variable(cs.clone(), || Ok(limb_0), AllocationMode::Witness)?;
225 let limb_1_var = UInt64::new_variable(cs.clone(), || Ok(limb_1), AllocationMode::Witness)?;
226 let limb_2_var = UInt64::new_variable(cs.clone(), || Ok(limb_2), AllocationMode::Witness)?;
227 let limb_3_var = UInt64::new_variable(cs, || Ok(limb_3), AllocationMode::Witness)?;
228
229 let lo_128_bits = limb_0_var
231 .to_bits_le()
232 .into_iter()
233 .chain(limb_1_var.to_bits_le())
234 .collect::<Vec<_>>();
235 let hi_128_bits = limb_2_var
236 .to_bits_le()
237 .into_iter()
238 .chain(limb_3_var.to_bits_le())
239 .collect::<Vec<_>>();
240
241 hi_128_var.enforce_equal(&Boolean::<Fq>::le_bits_to_fp_var(
242 &(hi_128_bits[..]).to_bits_le()?,
243 )?)?;
244 lo_128_var.enforce_equal(&Boolean::<Fq>::le_bits_to_fp_var(
245 &(lo_128_bits[..]).to_bits_le()?,
246 )?)?;
247
248 Ok(Self {
249 limbs: [limb_0_var, limb_1_var, limb_2_var, limb_3_var],
250 })
251 }
252}
253
254impl R1CSVar<Fq> for U128x128Var {
255 type Value = U128x128;
256
257 fn cs(&self) -> ark_relations::r1cs::ConstraintSystemRef<Fq> {
258 self.limbs[0].cs()
259 }
260
261 fn value(&self) -> Result<Self::Value, ark_relations::r1cs::SynthesisError> {
262 let x0 = self.limbs[0].value()?;
263 let x1 = self.limbs[1].value()?;
264 let x2 = self.limbs[2].value()?;
265 let x3 = self.limbs[3].value()?;
266
267 let mut bytes = [0u8; 32];
268 bytes[0..8].copy_from_slice(x3.to_be_bytes().as_ref());
269 bytes[8..16].copy_from_slice(x2.to_be_bytes().as_ref());
270 bytes[16..24].copy_from_slice(x1.to_be_bytes().as_ref());
271 bytes[24..32].copy_from_slice(x0.to_be_bytes().as_ref());
272
273 Ok(Self::Value::from_bytes(bytes))
274 }
275}
276
277impl U128x128Var {
278 pub fn checked_add(self, rhs: &Self) -> Result<U128x128Var, SynthesisError> {
279 let x0 = Boolean::<Fq>::le_bits_to_fp_var(&self.limbs[0].to_bits_le())?;
284 let x1 = Boolean::<Fq>::le_bits_to_fp_var(&self.limbs[1].to_bits_le())?;
285 let x2 = Boolean::<Fq>::le_bits_to_fp_var(&self.limbs[2].to_bits_le())?;
286 let x3 = Boolean::<Fq>::le_bits_to_fp_var(&self.limbs[3].to_bits_le())?;
287
288 let y0 = Boolean::<Fq>::le_bits_to_fp_var(&rhs.limbs[0].to_bits_le())?;
289 let y1 = Boolean::<Fq>::le_bits_to_fp_var(&rhs.limbs[1].to_bits_le())?;
290 let y2 = Boolean::<Fq>::le_bits_to_fp_var(&rhs.limbs[2].to_bits_le())?;
291 let y3 = Boolean::<Fq>::le_bits_to_fp_var(&rhs.limbs[3].to_bits_le())?;
292
293 let z0_raw = &x0 + &y0;
296 let z1_raw = &x1 + &y1;
297 let z2_raw = &x2 + &y2;
298 let z3_raw = &x3 + &y3;
299
300 let z0_bits = bit_constrain(z0_raw, 65)?; let z0 = UInt64::from_bits_le(&z0_bits[0..64]);
303 let c1 = Boolean::<Fq>::le_bits_to_fp_var(&z0_bits[64..].to_bits_le()?)?;
304
305 let z1_bits = bit_constrain(z1_raw + c1, 65)?; let z1 = UInt64::from_bits_le(&z1_bits[0..64]);
308 let c2 = Boolean::<Fq>::le_bits_to_fp_var(&z1_bits[64..].to_bits_le()?)?;
309
310 let z2_bits = bit_constrain(z2_raw + c2, 65)?; let z2 = UInt64::from_bits_le(&z2_bits[0..64]);
313 let c3 = Boolean::<Fq>::le_bits_to_fp_var(&z2_bits[64..].to_bits_le()?)?;
314
315 let z3_bits = bit_constrain(z3_raw + c3, 64)?; let z3 = UInt64::from_bits_le(&z3_bits[0..64]);
320
321 Ok(Self {
322 limbs: [z0, z1, z2, z3],
323 })
324 }
325
326 pub fn checked_sub(
327 self,
328 _rhs: &Self,
329 _cs: ConstraintSystemRef<Fq>,
330 ) -> Result<U128x128Var, SynthesisError> {
331 todo!();
332 }
333
334 pub fn checked_mul(self, rhs: &Self) -> Result<U128x128Var, SynthesisError> {
335 let x0 = Boolean::<Fq>::le_bits_to_fp_var(&self.limbs[0].to_bits_le())?;
340 let x1 = Boolean::<Fq>::le_bits_to_fp_var(&self.limbs[1].to_bits_le())?;
341 let x2 = Boolean::<Fq>::le_bits_to_fp_var(&self.limbs[2].to_bits_le())?;
342 let x3 = Boolean::<Fq>::le_bits_to_fp_var(&self.limbs[3].to_bits_le())?;
343
344 let y0 = Boolean::<Fq>::le_bits_to_fp_var(&rhs.limbs[0].to_bits_le())?;
345 let y1 = Boolean::<Fq>::le_bits_to_fp_var(&rhs.limbs[1].to_bits_le())?;
346 let y2 = Boolean::<Fq>::le_bits_to_fp_var(&rhs.limbs[2].to_bits_le())?;
347 let y3 = Boolean::<Fq>::le_bits_to_fp_var(&rhs.limbs[3].to_bits_le())?;
348
349 let z0 = &x0 * &y0;
354 let z1 = &x0 * &y1 + &x1 * &y0;
355 let z2 = &x0 * &y2 + &x1 * &y1 + &x2 * &y0;
356 let z3 = &x0 * &y3 + &x1 * &y2 + &x2 * &y1 + &x3 * &y0;
357 let z4 = &x1 * &y3 + &x2 * &y2 + &x3 * &y1;
358 let z5 = &x2 * &y3 + &x3 * &y2;
359 let z6 = &x3 * &y3;
360 let t0 = z0 + z1 * Fq::from(1u128 << 64);
371 let t0_bits = bit_constrain(t0, 193)?;
372 let t1 = z2 + Boolean::<Fq>::le_bits_to_fp_var(&t0_bits[128..193].to_bits_le()?)?;
376 let t1_bits = bit_constrain(t1, 130)?;
378
379 let w0 = UInt64::from_bits_le(&t1_bits[0..64]);
381
382 let t2 = z3 + Boolean::<Fq>::le_bits_to_fp_var(&t1_bits[64..129].to_bits_le()?)?;
384 let t2_bits = bit_constrain(t2, 129)?;
386
387 let w1 = UInt64::from_bits_le(&t2_bits[0..64]);
389
390 let t3 = z4 + Boolean::<Fq>::le_bits_to_fp_var(&t2_bits[64..129].to_bits_le()?)?;
392 let t3_bits = bit_constrain(t3, 128)?;
394
395 let w2 = UInt64::from_bits_le(&t3_bits[0..64]);
397
398 let t4 = z5 + Boolean::<Fq>::le_bits_to_fp_var(&t3_bits[64..128].to_bits_le()?)?;
400 let t4_bits = bit_constrain(t4, 64)?;
402 let w3 = UInt64::from_bits_le(&t4_bits[0..64]);
406
407 z6.enforce_equal(&FqVar::zero())?;
409
410 Ok(U128x128Var {
411 limbs: [w0, w1, w2, w3],
412 })
413 }
414
415 pub fn to_bits_le(&self) -> Vec<Boolean<Fq>> {
416 let lo_128_bits = self.limbs[0]
417 .to_bits_le()
418 .into_iter()
419 .chain(self.limbs[1].to_bits_le())
420 .collect::<Vec<_>>();
421 let hi_128_bits = self.limbs[2]
422 .to_bits_le()
423 .into_iter()
424 .chain(self.limbs[3].to_bits_le())
425 .collect::<Vec<_>>();
426 lo_128_bits.into_iter().chain(hi_128_bits).collect()
427 }
428
429 pub fn enforce_cmp(
431 &self,
432 other: &U128x128Var,
433 ordering: std::cmp::Ordering,
434 ) -> Result<(), SynthesisError> {
435 let self_bits: Vec<Boolean<Fq>> = self.to_bits_le().into_iter().rev().collect();
437 let other_bits: Vec<Boolean<Fq>> = other.to_bits_le().into_iter().rev().collect();
438
439 let mut gt: Boolean<Fq> = Boolean::constant(false);
443 let mut lt: Boolean<Fq> = Boolean::constant(false);
444 for (p, q) in zip(self_bits, other_bits) {
445 gt = gt.or(<.not().and(&p)?.and(&q.not())?)?;
451 lt = lt.or(>.not().and(&q)?.and(&p.not())?)?;
453 }
454
455 match ordering {
456 std::cmp::Ordering::Greater => {
457 gt.enforce_equal(&Boolean::constant(true))?;
458 lt.enforce_equal(&Boolean::constant(false))?;
459 }
460 std::cmp::Ordering::Less => {
461 gt.enforce_equal(&Boolean::constant(false))?;
462 lt.enforce_equal(&Boolean::constant(true))?;
463 }
464 std::cmp::Ordering::Equal => {
465 unimplemented!("use EqGadget for efficiency");
466 }
467 }
468
469 Ok(())
470 }
471
472 pub fn checked_div(
473 self,
474 rhs: &Self,
475 cs: ConstraintSystemRef<Fq>,
476 ) -> Result<U128x128Var, SynthesisError> {
477 rhs.enforce_not_equal(&U128x128Var::zero())?;
499
500 let xbar_ooc = self.value().unwrap_or_default();
502 let ybar_ooc = rhs.value().unwrap_or(U128x128::from(1u64));
503 let Ok((quo_ooc, rem_ooc)) = stub_div_rem_u384_by_u256(xbar_ooc.0, ybar_ooc.0) else {
504 return Err(SynthesisError::Unsatisfiable);
505 };
506 let x = self;
511 let y = rhs;
512 let q = U128x128Var::new_witness(cs.clone(), || Ok(U128x128(quo_ooc)))?;
513 let r_var = U128x128Var::new_witness(cs, || Ok(U128x128(rem_ooc)))?;
515 r_var.enforce_cmp(rhs, core::cmp::Ordering::Less)?;
518
519 let r = r_var.limbs;
520 let qbar = &q.limbs;
521 let ybar = &y.limbs;
522 let xbar = &x.limbs;
523
524 let xbar0 = Boolean::<Fq>::le_bits_to_fp_var(&xbar[0].to_bits_le())?;
529 let xbar1 = Boolean::<Fq>::le_bits_to_fp_var(&xbar[1].to_bits_le())?;
530 let xbar2 = Boolean::<Fq>::le_bits_to_fp_var(&xbar[2].to_bits_le())?;
531 let xbar3 = Boolean::<Fq>::le_bits_to_fp_var(&xbar[3].to_bits_le())?;
532
533 let ybar0 = Boolean::<Fq>::le_bits_to_fp_var(&ybar[0].to_bits_le())?;
534 let ybar1 = Boolean::<Fq>::le_bits_to_fp_var(&ybar[1].to_bits_le())?;
535 let ybar2 = Boolean::<Fq>::le_bits_to_fp_var(&ybar[2].to_bits_le())?;
536 let ybar3 = Boolean::<Fq>::le_bits_to_fp_var(&ybar[3].to_bits_le())?;
537
538 let qbar0 = Boolean::<Fq>::le_bits_to_fp_var(&qbar[0].to_bits_le())?;
539 let qbar1 = Boolean::<Fq>::le_bits_to_fp_var(&qbar[1].to_bits_le())?;
540 let qbar2 = Boolean::<Fq>::le_bits_to_fp_var(&qbar[2].to_bits_le())?;
541 let qbar3 = Boolean::<Fq>::le_bits_to_fp_var(&qbar[3].to_bits_le())?;
542
543 let r0 = Boolean::<Fq>::le_bits_to_fp_var(&r[0].to_bits_le())?;
544 let r1 = Boolean::<Fq>::le_bits_to_fp_var(&r[1].to_bits_le())?;
545 let r2 = Boolean::<Fq>::le_bits_to_fp_var(&r[2].to_bits_le())?;
546 let r3 = Boolean::<Fq>::le_bits_to_fp_var(&r[3].to_bits_le())?;
547
548 let z0_raw = r0 + &qbar0 * &ybar0;
566 let z1_raw = r1 + &qbar1 * &ybar0 + &qbar0 * &ybar1;
567 let z2_raw = r2 + &qbar2 * &ybar0 + &qbar1 * &ybar1 + &qbar0 * &ybar2;
568 let z3_raw = r3 + &qbar3 * &ybar0 + &qbar2 * &ybar1 + &qbar1 * &ybar2 + &qbar0 * &ybar3;
569 let z4_raw = &qbar3 * &ybar1 + &qbar2 * &ybar2 + &qbar1 * &ybar3;
570 let z5_raw = &qbar3 * &ybar2 + &qbar2 * &ybar3;
571 let z6_raw = &qbar3 * &ybar3;
572 let z0_bits = bit_constrain(z0_raw, 128)?; let z0 = Boolean::<Fq>::le_bits_to_fp_var(&z0_bits[0..64].to_bits_le()?)?;
588 let c1 = Boolean::<Fq>::le_bits_to_fp_var(&z0_bits[64..].to_bits_le()?)?; let z1_bits = bit_constrain(z1_raw + c1, 129)?; let z1 = Boolean::<Fq>::le_bits_to_fp_var(&z1_bits[0..64].to_bits_le()?)?;
593 let c2 = Boolean::<Fq>::le_bits_to_fp_var(&z1_bits[64..].to_bits_le()?)?; let z2_bits = bit_constrain(z2_raw + c2, 130)?; let z2 = Boolean::<Fq>::le_bits_to_fp_var(&z2_bits[0..64].to_bits_le()?)?;
598 let c3 = Boolean::<Fq>::le_bits_to_fp_var(&z2_bits[64..].to_bits_le()?)?; let z3_bits = bit_constrain(z3_raw + c3, 130)?; let z3 = Boolean::<Fq>::le_bits_to_fp_var(&z3_bits[0..64].to_bits_le()?)?;
603 let c4 = Boolean::<Fq>::le_bits_to_fp_var(&z3_bits[64..].to_bits_le()?)?; let z4_bits = bit_constrain(z4_raw + c4, 128)?; let z4 = Boolean::<Fq>::le_bits_to_fp_var(&z4_bits[0..64].to_bits_le()?)?;
609 let c5 = Boolean::<Fq>::le_bits_to_fp_var(&z4_bits[64..].to_bits_le()?)?; let z5_bits = bit_constrain(z5_raw + c5, 64)?; let z5 = Boolean::<Fq>::le_bits_to_fp_var(&z5_bits[0..64].to_bits_le()?)?;
617
618 z0.enforce_equal(&FqVar::zero())?;
623 z1.enforce_equal(&FqVar::zero())?;
624 z2.enforce_equal(&xbar0)?;
625 z3.enforce_equal(&xbar1)?;
626 z4.enforce_equal(&xbar2)?;
627 z5.enforce_equal(&xbar3)?;
628 z6_raw.enforce_equal(&FqVar::zero())?;
630
631 Ok(q)
632 }
633
634 pub fn round_down(self) -> U128x128Var {
635 Self {
636 limbs: [
637 UInt64::constant(0u64),
638 UInt64::constant(0u64),
639 self.limbs[2].clone(),
640 self.limbs[3].clone(),
641 ],
642 }
643 }
644 pub fn apply_to_amount(self, rhs: AmountVar) -> Result<AmountVar, SynthesisError> {
646 U128x128Var::from_amount_var(rhs)?
647 .checked_mul(&self)?
648 .round_down_to_amount()
649 }
650
651 pub fn round_down_to_amount(self) -> Result<AmountVar, SynthesisError> {
652 let bits = self.limbs[2]
653 .to_bits_le()
654 .into_iter()
655 .chain(self.limbs[3].to_bits_le().into_iter())
656 .collect::<Vec<Boolean<Fq>>>();
657 Ok(AmountVar {
658 amount: Boolean::<Fq>::le_bits_to_fp_var(&bits)?,
659 })
660 }
661
662 pub fn zero() -> U128x128Var {
663 Self {
664 limbs: [
665 UInt64::constant(0u64),
666 UInt64::constant(0u64),
667 UInt64::constant(0u64),
668 UInt64::constant(0u64),
669 ],
670 }
671 }
672}
673
674impl EqGadget<Fq> for U128x128Var {
675 fn is_eq(&self, other: &Self) -> Result<Boolean<Fq>, SynthesisError> {
676 let limb_1_eq = self.limbs[0].is_eq(&other.limbs[0])?;
677 let limb_2_eq = self.limbs[1].is_eq(&other.limbs[1])?;
678 let limb_3_eq = self.limbs[2].is_eq(&other.limbs[2])?;
679 let limb_4_eq = self.limbs[3].is_eq(&other.limbs[3])?;
680
681 let limb_12_eq = limb_1_eq.and(&limb_2_eq)?;
682 let limb_34_eq = limb_3_eq.and(&limb_4_eq)?;
683
684 limb_12_eq.and(&limb_34_eq)
685 }
686}
687
688impl ToConstraintField<Fq> for U128x128 {
689 fn to_field_elements(&self) -> Option<Vec<Fq>> {
690 let (hi_128, lo_128) = self.0.into_words();
691 Some(vec![Fq::from(hi_128), Fq::from(lo_128)])
692 }
693}
694
695impl CondSelectGadget<Fq> for U128x128Var {
696 fn conditionally_select(
697 cond: &Boolean<Fq>,
698 true_value: &Self,
699 false_value: &Self,
700 ) -> Result<Self, SynthesisError> {
701 let limb0 = cond.select(&true_value.limbs[0], &false_value.limbs[0])?;
702 let limb1 = cond.select(&true_value.limbs[1], &false_value.limbs[1])?;
703 let limb2 = cond.select(&true_value.limbs[2], &false_value.limbs[2])?;
704 let limb3 = cond.select(&true_value.limbs[3], &false_value.limbs[3])?;
705 Ok(Self {
706 limbs: [limb0, limb1, limb2, limb3],
707 })
708 }
709}
710
711pub fn convert_uint64_to_fqvar<F: PrimeField>(value: &UInt64<F>) -> FpVar<F> {
713 Boolean::<F>::le_bits_to_fp_var(&value.to_bits_le()).expect("can convert to bits")
714}
715
716pub fn bit_constrain(value: FqVar, n: usize) -> Result<Vec<Boolean<Fq>>, SynthesisError> {
718 let inner = value.value().unwrap_or(Fq::zero());
719
720 let inner_bigint = inner.into_bigint();
722 let bits = &inner_bigint.to_bits_le()[0..n];
723
724 let mut boolean_constraints = Vec::new();
726 for bit in bits {
727 let boolean = Boolean::new_witness(value.cs().clone(), || Ok(bit))?;
728 boolean_constraints.push(boolean);
729 }
730
731 let constructed_fqvar = Boolean::<Fq>::le_bits_to_fp_var(&boolean_constraints.to_bits_le()?)
733 .expect("can convert to bits");
734 constructed_fqvar.enforce_equal(&value)?;
735
736 Ok(boolean_constraints)
737}
738
739#[cfg(test)]
740mod test {
741 use ark_groth16::{r1cs_to_qap::LibsnarkReduction, Groth16, ProvingKey, VerifyingKey};
742 use ark_relations::r1cs::ConstraintSynthesizer;
743 use ark_snark::SNARK;
744 use decaf377::Bls12_377;
745 use proptest::prelude::*;
746 use rand_core::OsRng;
747
748 use crate::Amount;
749
750 use super::*;
751
752 proptest! {
753 #![proptest_config(ProptestConfig::with_cases(1))]
754 #[test]
755 fn multiply_and_round(
756 a_int in any::<u64>(),
757 a_frac in any::<u64>(),
758 b_int in any::<u64>(),
759 b_frac in any::<u64>(),
760 ) {
761 let a = U128x128(
762 U256([a_frac.into(), a_int.into()])
763 );
764 let b = U128x128(
765 U256([b_frac.into(), b_int.into()])
766 );
767
768 let result = a.checked_mul(&b);
769
770 let expected_c = result.expect("result should not overflow");
771 let rounded_down_c = expected_c.round_down();
772
773 let circuit = TestMultiplicationCircuit {
774 a,
775 b,
776 c: expected_c,
777 rounded_down_c,
778 };
779
780 let (pk, vk) = TestMultiplicationCircuit::generate_test_parameters();
781 let mut rng = OsRng;
782
783 let proof = Groth16::<Bls12_377, LibsnarkReduction>::prove(&pk, circuit, &mut rng)
784 .expect("should be able to form proof");
785
786 let mut pi = Vec::new();
787 pi.extend_from_slice(&expected_c.to_field_elements().unwrap());
788 pi.extend_from_slice(&rounded_down_c.to_field_elements().unwrap());
789 let proof_result = Groth16::<Bls12_377, LibsnarkReduction>::verify(
790 &vk,
791 &pi,
792 &proof,
793 );
794 assert!(proof_result.is_ok());
795 }
796 }
797
798 struct TestMultiplicationCircuit {
799 a: U128x128,
800 b: U128x128,
801
802 pub c: U128x128,
804 pub rounded_down_c: U128x128,
805 }
806
807 impl ConstraintSynthesizer<Fq> for TestMultiplicationCircuit {
808 fn generate_constraints(
809 self,
810 cs: ConstraintSystemRef<Fq>,
811 ) -> ark_relations::r1cs::Result<()> {
812 let a_var = U128x128Var::new_witness(cs.clone(), || Ok(self.a))?;
813 let b_var = U128x128Var::new_witness(cs.clone(), || Ok(self.b))?;
814 let c_public_var = U128x128Var::new_input(cs.clone(), || Ok(self.c))?;
815 let c_public_rounded_down_var = U128x128Var::new_input(cs, || Ok(self.rounded_down_c))?;
816 let c_var = a_var.clone().checked_mul(&b_var)?;
817 c_var.enforce_equal(&c_public_var)?;
818 let c_rounded_down = c_var.clone().round_down();
819 c_rounded_down.enforce_equal(&c_public_rounded_down_var)?;
820
821 a_var.enforce_cmp(&c_var, std::cmp::Ordering::Less)?;
823
824 c_var.enforce_cmp(&a_var, std::cmp::Ordering::Greater)?;
826 Ok(())
827 }
828 }
829
830 impl TestMultiplicationCircuit {
831 fn generate_test_parameters() -> (ProvingKey<Bls12_377>, VerifyingKey<Bls12_377>) {
832 let num: [u8; 32] = [0u8; 32];
833 let a = U128x128::from_bytes(num);
834 let b = U128x128::from_bytes(num);
835 let c = a.checked_mul(&b).unwrap();
836 let rounded_down_c = c.round_down();
837 let circuit = TestMultiplicationCircuit {
838 a,
839 b,
840 c,
841 rounded_down_c,
842 };
843 let (pk, vk) = Groth16::<Bls12_377, LibsnarkReduction>::circuit_specific_setup(
844 circuit, &mut OsRng,
845 )
846 .expect("can perform circuit specific setup");
847 (pk, vk)
848 }
849 }
850
851 proptest! {
852 #![proptest_config(ProptestConfig::with_cases(5))]
853 #[test]
854 fn add(
855 a_int in any::<u64>(),
856 a_frac in any::<u128>(),
857 b_int in any::<u64>(),
858 b_frac in any::<u128>(),
859 ) {
860 let a = U128x128(
861 U256([a_frac, a_int.into()])
862 );
863 let b = U128x128(
864 U256([b_frac, b_int.into()])
865 );
866 let result = a.checked_add(&b);
867
868 if result.is_err() {
869 return Ok(())
871 }
872 let expected_c = result.expect("result should not overflow");
873
874 let circuit = TestAdditionCircuit {
875 a,
876 b,
877 c: expected_c,
878 };
879
880 let (pk, vk) = TestAdditionCircuit::generate_test_parameters();
881 let mut rng = OsRng;
882
883 let proof = Groth16::<Bls12_377, LibsnarkReduction>::prove(&pk, circuit, &mut rng)
884 .expect("should be able to form proof");
885
886 let proof_result = Groth16::<Bls12_377, LibsnarkReduction>::verify(
887 &vk,
888 &expected_c.to_field_elements().unwrap(),
889 &proof,
890 );
891 assert!(proof_result.is_ok());
892 }
893 }
894
895 #[test]
896 fn max_u64_addition() {
897 let a = U128x128(U256([u64::MAX as u128, 0]));
898 let b = U128x128(U256([u64::MAX as u128, 0]));
899
900 let result = a.checked_add(&b);
901
902 let expected_c = result.expect("result should not overflow");
903
904 let circuit = TestAdditionCircuit {
905 a,
906 b,
907 c: expected_c,
908 };
909
910 let (pk, vk) = TestAdditionCircuit::generate_test_parameters();
911 let mut rng = OsRng;
912
913 let proof = Groth16::<Bls12_377, LibsnarkReduction>::prove(&pk, circuit, &mut rng)
914 .expect("should be able to form proof");
915
916 let proof_result = Groth16::<Bls12_377, LibsnarkReduction>::verify(
917 &vk,
918 &expected_c.to_field_elements().unwrap(),
919 &proof,
920 );
921 assert!(proof_result.is_ok());
922 }
923
924 struct TestAdditionCircuit {
925 a: U128x128,
926 b: U128x128,
927
928 pub c: U128x128,
930 }
931
932 impl ConstraintSynthesizer<Fq> for TestAdditionCircuit {
933 fn generate_constraints(
934 self,
935 cs: ConstraintSystemRef<Fq>,
936 ) -> ark_relations::r1cs::Result<()> {
937 let a_var = U128x128Var::new_witness(cs.clone(), || Ok(self.a))?;
938 let b_var = U128x128Var::new_witness(cs.clone(), || Ok(self.b))?;
939 let c_public_var = U128x128Var::new_input(cs, || Ok(self.c))?;
940 let c_var = a_var.checked_add(&b_var)?;
941 c_var.enforce_equal(&c_public_var)?;
942 Ok(())
943 }
944 }
945
946 impl TestAdditionCircuit {
947 fn generate_test_parameters() -> (ProvingKey<Bls12_377>, VerifyingKey<Bls12_377>) {
948 let num: [u8; 32] = [0u8; 32];
949 let a = U128x128::from_bytes(num);
950 let b = U128x128::from_bytes(num);
951 let circuit = TestAdditionCircuit {
952 a,
953 b,
954 c: a.checked_add(&b).unwrap(),
955 };
956 let (pk, vk) = Groth16::<Bls12_377, LibsnarkReduction>::circuit_specific_setup(
957 circuit, &mut OsRng,
958 )
959 .expect("can perform circuit specific setup");
960 (pk, vk)
961 }
962 }
963
964 #[test]
965 fn max_division() {
966 let b = U128x128(U256([0, 1]));
967 let a = U128x128(U256([u128::MAX, u128::MAX]));
968
969 let result = a.checked_div(&b);
970
971 let expected_c = result.expect("result should not overflow");
972 dbg!(expected_c);
973
974 let circuit = TestDivisionCircuit {
975 a,
976 b,
977 c: expected_c,
978 };
979
980 let (pk, vk) = TestDivisionCircuit::generate_test_parameters();
981 let mut rng = OsRng;
982
983 let proof = Groth16::<Bls12_377, LibsnarkReduction>::prove(&pk, circuit, &mut rng)
984 .expect("should be able to form proof");
985
986 let proof_result = Groth16::<Bls12_377, LibsnarkReduction>::verify(
987 &vk,
988 &expected_c.to_field_elements().unwrap(),
989 &proof,
990 );
991 assert!(proof_result.is_ok());
992 }
993
994 proptest! {
995 #![proptest_config(ProptestConfig::with_cases(10))]
996 #[test]
997 fn division(
998 a_int in any::<u64>(),
999 a_frac in any::<u64>(),
1000 b_int in any::<u128>(),
1001 b_frac in any::<u128>(),
1002 ) {
1003 let a = U128x128(
1004 U256([a_frac.into(), a_int.into()])
1005 );
1006 let b = U128x128(
1007 U256([b_frac, b_int])
1008 );
1009
1010 if b_int == 0 {
1012 return Ok(())
1013 }
1014
1015 let result = a.checked_div(&b);
1016
1017 let expected_c = result.expect("result should not overflow");
1018
1019 let circuit = TestDivisionCircuit {
1020 a,
1021 b,
1022 c: expected_c,
1023 };
1024
1025 let (pk, vk) = TestDivisionCircuit::generate_test_parameters();
1026 let mut rng = OsRng;
1027
1028 let proof = Groth16::<Bls12_377, LibsnarkReduction>::prove(&pk, circuit, &mut rng)
1029 .expect("should be able to form proof");
1030
1031 let proof_result = Groth16::<Bls12_377, LibsnarkReduction>::verify(
1032 &vk,
1033 &expected_c.to_field_elements().unwrap(),
1034 &proof,
1035 );
1036 assert!(proof_result.is_ok());
1037 }
1038 }
1039
1040 struct TestDivisionCircuit {
1041 a: U128x128,
1042 b: U128x128,
1043
1044 pub c: U128x128,
1046 }
1047
1048 impl ConstraintSynthesizer<Fq> for TestDivisionCircuit {
1049 fn generate_constraints(
1050 self,
1051 cs: ConstraintSystemRef<Fq>,
1052 ) -> ark_relations::r1cs::Result<()> {
1053 let a_var = U128x128Var::new_witness(cs.clone(), || Ok(self.a))?;
1054 let b_var = U128x128Var::new_witness(cs.clone(), || Ok(self.b))?;
1055 let c_public_var = U128x128Var::new_input(cs.clone(), || Ok(self.c))?;
1056 let c_var = a_var.checked_div(&b_var, cs)?;
1057 c_var.enforce_equal(&c_public_var)?;
1058 Ok(())
1059 }
1060 }
1061
1062 impl TestDivisionCircuit {
1063 fn generate_test_parameters() -> (ProvingKey<Bls12_377>, VerifyingKey<Bls12_377>) {
1064 let num: [u8; 32] = [1u8; 32];
1065 let a = U128x128::from_bytes(num);
1066 let b = U128x128::from_bytes(num);
1067 let circuit = TestDivisionCircuit {
1068 a,
1069 b,
1070 c: a.checked_div(&b).unwrap(),
1071 };
1072 let (pk, vk) = Groth16::<Bls12_377, LibsnarkReduction>::circuit_specific_setup(
1073 circuit, &mut OsRng,
1074 )
1075 .expect("can perform circuit specific setup");
1076 (pk, vk)
1077 }
1078 }
1079
1080 proptest! {
1081 #![proptest_config(ProptestConfig::with_cases(5))]
1082 #[test]
1083 fn compare(
1084 a_int in any::<u64>(),
1085 c_int in any::<u64>(),
1086 ) {
1087 let a =
1089 if a_int == u64::MAX {
1090 U128x128::from(a_int - 1)
1091 } else {
1092 U128x128::from(a_int)
1093 };
1094 let b = (a + U128x128::from(1u64)).expect("should not overflow");
1095 let c =
1097 if c_int == 0 {
1098 U128x128::from(c_int + 1)
1099 } else {
1100 U128x128::from(c_int)
1101 };
1102 let d = (c - U128x128::from(1u64)).expect("should not underflow");
1103
1104 let circuit = TestComparisonCircuit {
1105 a,
1106 b,
1107 c,
1108 d,
1109 };
1110
1111 let (pk, vk) = TestComparisonCircuit::generate_test_parameters();
1112 let mut rng = OsRng;
1113
1114 let proof = Groth16::<Bls12_377, LibsnarkReduction>::prove(&pk, circuit, &mut rng)
1115 .expect("should be able to form proof");
1116
1117 let proof_result = Groth16::<Bls12_377, LibsnarkReduction>::verify(
1118 &vk,
1119 &[],
1120 &proof,
1121 );
1122 assert!(proof_result.is_ok());
1123 }
1124 }
1125
1126 struct TestComparisonCircuit {
1127 a: U128x128,
1128 b: U128x128,
1129 c: U128x128,
1130 d: U128x128,
1131 }
1132
1133 impl ConstraintSynthesizer<Fq> for TestComparisonCircuit {
1134 fn generate_constraints(
1135 self,
1136 cs: ConstraintSystemRef<Fq>,
1137 ) -> ark_relations::r1cs::Result<()> {
1138 let a_var = U128x128Var::new_witness(cs.clone(), || Ok(self.a))?;
1140 let b_var = U128x128Var::new_witness(cs.clone(), || Ok(self.b))?;
1141 a_var.enforce_cmp(&b_var, std::cmp::Ordering::Less)?;
1142 let c_var = U128x128Var::new_witness(cs.clone(), || Ok(self.c))?;
1144 let d_var = U128x128Var::new_witness(cs, || Ok(self.d))?;
1145 c_var.enforce_cmp(&d_var, std::cmp::Ordering::Greater)?;
1146
1147 Ok(())
1148 }
1149 }
1150
1151 impl TestComparisonCircuit {
1152 fn generate_test_parameters() -> (ProvingKey<Bls12_377>, VerifyingKey<Bls12_377>) {
1153 let num: [u8; 32] = [0u8; 32];
1154 let a = U128x128::from_bytes(num);
1155 let b = U128x128::from_bytes(num);
1156 let c = U128x128::from_bytes(num);
1157 let d = U128x128::from_bytes(num);
1158 let circuit = TestComparisonCircuit { a, b, c, d };
1159 let (pk, vk) = Groth16::<Bls12_377, LibsnarkReduction>::circuit_specific_setup(
1160 circuit, &mut OsRng,
1161 )
1162 .expect("can perform circuit specific setup");
1163 (pk, vk)
1164 }
1165 }
1166
1167 struct TestGreaterInvalidComparisonCircuit {
1168 a: U128x128,
1169 b: U128x128,
1170 }
1171
1172 impl ConstraintSynthesizer<Fq> for TestGreaterInvalidComparisonCircuit {
1173 fn generate_constraints(
1174 self,
1175 cs: ConstraintSystemRef<Fq>,
1176 ) -> ark_relations::r1cs::Result<()> {
1177 let a_var = U128x128Var::new_witness(cs.clone(), || Ok(self.a))?;
1179 let b_var = U128x128Var::new_witness(cs, || Ok(self.b))?;
1180 a_var.enforce_cmp(&b_var, std::cmp::Ordering::Greater)?;
1181
1182 Ok(())
1183 }
1184 }
1185
1186 impl TestGreaterInvalidComparisonCircuit {
1187 fn generate_test_parameters(
1188 ) -> Result<(ProvingKey<Bls12_377>, VerifyingKey<Bls12_377>), SynthesisError> {
1189 let num: [u8; 32] = [0u8; 32];
1190 let a = U128x128::from_bytes(num);
1191 let b = U128x128::from_bytes(num);
1192 let circuit = TestGreaterInvalidComparisonCircuit { a, b };
1193 Groth16::<Bls12_377, LibsnarkReduction>::circuit_specific_setup(circuit, &mut OsRng)
1194 }
1195 }
1196
1197 proptest! {
1198 #![proptest_config(ProptestConfig::with_cases(5))]
1199 #[should_panic]
1200 #[test]
1201 fn invalid_greater_compare(
1202 a_int in any::<u128>(),
1203 ) {
1204 let a =
1206 if a_int == u128::MAX {
1207 U128x128::from(a_int - 1)
1208 } else {
1209 U128x128::from(a_int)
1210 };
1211 let b = (a + U128x128::from(1u64)).expect("should not overflow");
1212
1213 let circuit = TestGreaterInvalidComparisonCircuit {
1214 a,
1215 b,
1216 };
1217
1218 let (pk, vk) = TestGreaterInvalidComparisonCircuit::generate_test_parameters().expect("can perform setup");
1219 let mut rng = OsRng;
1220
1221 let proof = Groth16::<Bls12_377, LibsnarkReduction>::prove(&pk, circuit, &mut rng)
1222 .expect("in debug mode only, we assert that the circuit is satisfied, so we will panic here");
1223
1224 let proof_result = Groth16::<Bls12_377, LibsnarkReduction>::verify(
1225 &vk,
1226 &[],
1227 &proof,
1228 ).expect("in release mode, we will be able to construct the proof, so we can unwrap the result");
1229
1230 if !proof_result {
1232 panic!("should not be able to verify proof");
1233 }
1234 }
1235 }
1236
1237 struct TestLessInvalidComparisonCircuit {
1238 c: U128x128,
1239 d: U128x128,
1240 }
1241
1242 impl ConstraintSynthesizer<Fq> for TestLessInvalidComparisonCircuit {
1243 fn generate_constraints(
1244 self,
1245 cs: ConstraintSystemRef<Fq>,
1246 ) -> ark_relations::r1cs::Result<()> {
1247 let c_var = U128x128Var::new_witness(cs.clone(), || Ok(self.c))?;
1249 let d_var = U128x128Var::new_witness(cs, || Ok(self.d))?;
1250 c_var.enforce_cmp(&d_var, std::cmp::Ordering::Less)?;
1251
1252 Ok(())
1253 }
1254 }
1255
1256 impl TestLessInvalidComparisonCircuit {
1257 fn generate_test_parameters(
1258 ) -> Result<(ProvingKey<Bls12_377>, VerifyingKey<Bls12_377>), SynthesisError> {
1259 let num: [u8; 32] = [0u8; 32];
1260 let c = U128x128::from_bytes(num);
1261 let d = U128x128::from_bytes(num);
1262 let circuit = TestLessInvalidComparisonCircuit { c, d };
1263 Groth16::<Bls12_377, LibsnarkReduction>::circuit_specific_setup(circuit, &mut OsRng)
1264 }
1265 }
1266
1267 proptest! {
1268 #![proptest_config(ProptestConfig::with_cases(5))]
1269 #[should_panic]
1270 #[test]
1271 fn invalid_less_compare(
1272 c_int in any::<u128>(),
1273 ) {
1274 let c =
1276 if c_int == 0 {
1277 U128x128::from(c_int + 1)
1278 } else {
1279 U128x128::from(c_int)
1280 };
1281 let d = (c - U128x128::from(1u64)).expect("should not underflow");
1282
1283 let circuit = TestLessInvalidComparisonCircuit {
1284 c,
1285 d,
1286 };
1287
1288 let (pk, vk) = TestLessInvalidComparisonCircuit::generate_test_parameters().expect("can perform setup");
1289 let mut rng = OsRng;
1290
1291 let proof = Groth16::<Bls12_377, LibsnarkReduction>::prove(&pk, circuit, &mut rng)
1292 .expect("in debug mode only, we assert that the circuit is satisfied, so we will panic here");
1293
1294 let proof_result = Groth16::<Bls12_377, LibsnarkReduction>::verify(
1295 &vk,
1296 &[],
1297 &proof,
1298 ).expect("in release mode, we will be able to construct the proof, so we can unwrap the result");
1299
1300 if !proof_result {
1302 panic!("should not be able to verify proof");
1303 }
1304 }
1305 }
1306
1307 #[should_panic]
1308 #[test]
1309 fn regression_invalid_less_compare() {
1310 let c = U128x128::from(354389783742u64);
1312 let d = U128x128::from(17u64);
1313
1314 let circuit = TestLessInvalidComparisonCircuit { c, d };
1315
1316 let (pk, vk) = TestLessInvalidComparisonCircuit::generate_test_parameters()
1317 .expect("can perform setup");
1318 let mut rng = OsRng;
1319
1320 let proof = Groth16::<Bls12_377, LibsnarkReduction>::prove(&pk, circuit, &mut rng).expect(
1321 "in debug mode only, we assert that the circuit is satisfied, so we will panic here",
1322 );
1323
1324 let proof_result = Groth16::<Bls12_377, LibsnarkReduction>::verify(&vk, &[], &proof)
1325 .expect(
1326 "in release mode, we will be able to construct the proof, so we can unwrap the result",
1327 );
1328
1329 if !proof_result {
1331 panic!("should not be able to verify proof");
1332 }
1333 }
1334
1335 proptest! {
1336 #![proptest_config(ProptestConfig::with_cases(5))]
1337 #[test]
1338 fn round_down_to_amount(
1339 a_int in any::<u128>(),
1340 a_frac in any::<u128>(),
1341 ) {
1342 let a = U128x128(
1343 U256([a_frac, a_int])
1344 );
1345
1346 let expected_c = a.round_down().try_into().expect("should be able to round down OOC");
1347
1348 let circuit = TestRoundDownCircuit {
1349 a,
1350 c: expected_c,
1351 };
1352
1353 let (pk, vk) = TestRoundDownCircuit::generate_test_parameters();
1354 let mut rng = OsRng;
1355
1356 let proof = Groth16::<Bls12_377, LibsnarkReduction>::prove(&pk, circuit, &mut rng)
1357 .expect("should be able to form proof");
1358
1359 let proof_result = Groth16::<Bls12_377, LibsnarkReduction>::verify(
1360 &vk,
1361 &expected_c.to_field_elements().unwrap(),
1362 &proof,
1363 );
1364 assert!(proof_result.is_ok());
1365 }
1366 }
1367
1368 struct TestRoundDownCircuit {
1369 a: U128x128,
1370
1371 pub c: Amount,
1373 }
1374
1375 impl ConstraintSynthesizer<Fq> for TestRoundDownCircuit {
1376 fn generate_constraints(
1377 self,
1378 cs: ConstraintSystemRef<Fq>,
1379 ) -> ark_relations::r1cs::Result<()> {
1380 let a_var = U128x128Var::new_witness(cs.clone(), || Ok(self.a))?;
1381 let c_public_var = AmountVar::new_input(cs, || Ok(self.c))?;
1382 let c_var = a_var.round_down_to_amount()?;
1383 c_var.enforce_equal(&c_public_var)?;
1384 Ok(())
1385 }
1386 }
1387
1388 impl TestRoundDownCircuit {
1389 fn generate_test_parameters() -> (ProvingKey<Bls12_377>, VerifyingKey<Bls12_377>) {
1390 let num: [u8; 32] = [0u8; 32];
1391 let a = U128x128::from_bytes(num);
1392 let c: Amount = a
1393 .round_down()
1394 .try_into()
1395 .expect("should be able to round down OOC");
1396 let circuit = TestRoundDownCircuit { a, c };
1397 let (pk, vk) = Groth16::<Bls12_377, LibsnarkReduction>::circuit_specific_setup(
1398 circuit, &mut OsRng,
1399 )
1400 .expect("can perform circuit specific setup");
1401 (pk, vk)
1402 }
1403 }
1404}