use alloc::string::String; use alloc::vec; use alloc::vec::Vec; use anyhow::Result; use core::marker::PhantomData; use num::integer::div_ceil; use plonky2::plonk::circuit_data::CommonCircuitData; use plonky2::util::serialization::{Buffer, IoError, IoResult, Read, Write}; use num::{BigUint, Integer, One, Zero}; use plonky2::field::extension::Extendable; use plonky2::field::types::{Field, PrimeField}; use plonky2::hash::hash_types::RichField; use plonky2::iop::generator::{GeneratedValues, SimpleGenerator}; use plonky2::iop::target::{BoolTarget, Target}; use plonky2::iop::witness::{PartitionWitness, WitnessWrite}; use plonky2::plonk::circuit_builder::CircuitBuilder; use plonky2_u32::gadgets::arithmetic_u32::{CircuitBuilderU32, U32Target}; use plonky2_u32::gadgets::range_check::range_check_u32_circuit; use plonky2_u32::witness::GeneratedValuesU32; use crate::gadgets::biguint::{ BigUintTarget, CircuitBuilderBiguint, GeneratedValuesBigUint, WitnessBigUint, }; #[derive(Clone, Debug)] pub struct NonNativeTarget { pub(crate) value: BigUintTarget, pub(crate) _phantom: PhantomData, } impl NonNativeTarget { pub fn to_target_vec(&self) -> Vec { self.value.to_target_vec() } pub fn from_target_vec(ts: &[Target]) -> Self { Self { value: BigUintTarget::from_target_vec(ts), _phantom: PhantomData, } } } pub trait CircuitBuilderNonNative, const D: usize> { fn num_nonnative_limbs() -> usize { div_ceil(FF::BITS, 32) } fn biguint_to_nonnative(&mut self, x: &BigUintTarget) -> NonNativeTarget; fn nonnative_to_canonical_biguint( &mut self, x: &NonNativeTarget, ) -> BigUintTarget; fn constant_nonnative(&mut self, x: FF) -> NonNativeTarget; fn zero_nonnative(&mut self) -> NonNativeTarget; // Assert that two NonNativeTarget's, both assumed to be in reduced form, are equal. fn connect_nonnative( &mut self, lhs: &NonNativeTarget, rhs: &NonNativeTarget, ); fn add_virtual_nonnative_target(&mut self) -> NonNativeTarget; fn add_virtual_nonnative_target_sized( &mut self, num_limbs: usize, ) -> NonNativeTarget; fn add_nonnative( &mut self, a: &NonNativeTarget, b: &NonNativeTarget, ) -> NonNativeTarget; fn mul_nonnative_by_bool( &mut self, a: &NonNativeTarget, b: BoolTarget, ) -> NonNativeTarget; fn if_nonnative( &mut self, b: BoolTarget, x: &NonNativeTarget, y: &NonNativeTarget, ) -> NonNativeTarget; fn add_many_nonnative( &mut self, to_add: &[NonNativeTarget], ) -> NonNativeTarget; // Subtract two `NonNativeTarget`s. fn sub_nonnative( &mut self, a: &NonNativeTarget, b: &NonNativeTarget, ) -> NonNativeTarget; fn mul_nonnative( &mut self, a: &NonNativeTarget, b: &NonNativeTarget, ) -> NonNativeTarget; fn mul_many_nonnative( &mut self, to_mul: &[NonNativeTarget], ) -> NonNativeTarget; fn neg_nonnative(&mut self, x: &NonNativeTarget) -> NonNativeTarget; fn inv_nonnative(&mut self, x: &NonNativeTarget) -> NonNativeTarget; /// Returns `x % |FF|` as a `NonNativeTarget`. fn reduce(&mut self, x: &BigUintTarget) -> NonNativeTarget; fn reduce_nonnative(&mut self, x: &NonNativeTarget) -> NonNativeTarget; fn bool_to_nonnative(&mut self, b: &BoolTarget) -> NonNativeTarget; // Split a nonnative field element to bits. fn split_nonnative_to_bits(&mut self, x: &NonNativeTarget) -> Vec; fn nonnative_conditional_neg( &mut self, x: &NonNativeTarget, b: BoolTarget, ) -> NonNativeTarget; } impl, const D: usize> CircuitBuilderNonNative for CircuitBuilder { fn num_nonnative_limbs() -> usize { div_ceil(FF::BITS, 32) } fn biguint_to_nonnative(&mut self, x: &BigUintTarget) -> NonNativeTarget { NonNativeTarget { value: x.clone(), _phantom: PhantomData, } } fn nonnative_to_canonical_biguint( &mut self, x: &NonNativeTarget, ) -> BigUintTarget { x.value.clone() } fn constant_nonnative(&mut self, x: FF) -> NonNativeTarget { let x_biguint = self.constant_biguint(&x.to_canonical_biguint()); self.biguint_to_nonnative(&x_biguint) } fn zero_nonnative(&mut self) -> NonNativeTarget { self.constant_nonnative(FF::ZERO) } // Assert that two NonNativeTarget's, both assumed to be in reduced form, are equal. fn connect_nonnative( &mut self, lhs: &NonNativeTarget, rhs: &NonNativeTarget, ) { self.connect_biguint(&lhs.value, &rhs.value); } fn add_virtual_nonnative_target(&mut self) -> NonNativeTarget { let num_limbs = Self::num_nonnative_limbs::(); let value = self.add_virtual_biguint_target(num_limbs); NonNativeTarget { value, _phantom: PhantomData, } } fn add_virtual_nonnative_target_sized( &mut self, num_limbs: usize, ) -> NonNativeTarget { let value = self.add_virtual_biguint_target(num_limbs); NonNativeTarget { value, _phantom: PhantomData, } } fn add_nonnative( &mut self, a: &NonNativeTarget, b: &NonNativeTarget, ) -> NonNativeTarget { let sum = self.add_virtual_nonnative_target::(); let overflow = self.add_virtual_bool_target_unsafe(); self.add_simple_generator(NonNativeAdditionGenerator:: { a: a.clone(), b: b.clone(), sum: sum.clone(), overflow, _phantom: PhantomData, }); let sum_expected = self.add_biguint(&a.value, &b.value); let modulus = self.constant_biguint(&FF::order()); let mod_times_overflow = self.mul_biguint_by_bool(&modulus, overflow); let sum_actual = self.add_biguint(&sum.value, &mod_times_overflow); self.connect_biguint(&sum_expected, &sum_actual); // Range-check result. // TODO: can potentially leave unreduced until necessary (e.g. when connecting values). let cmp = self.cmp_biguint(&sum.value, &modulus); let one = self.one(); self.connect(cmp.target, one); sum } fn mul_nonnative_by_bool( &mut self, a: &NonNativeTarget, b: BoolTarget, ) -> NonNativeTarget { NonNativeTarget { value: self.mul_biguint_by_bool(&a.value, b), _phantom: PhantomData, } } fn if_nonnative( &mut self, b: BoolTarget, x: &NonNativeTarget, y: &NonNativeTarget, ) -> NonNativeTarget { let not_b = self.not(b); let maybe_x = self.mul_nonnative_by_bool(x, b); let maybe_y = self.mul_nonnative_by_bool(y, not_b); self.add_nonnative(&maybe_x, &maybe_y) } fn add_many_nonnative( &mut self, to_add: &[NonNativeTarget], ) -> NonNativeTarget { if to_add.len() == 1 { return to_add[0].clone(); } let sum = self.add_virtual_nonnative_target::(); let overflow = self.add_virtual_u32_target(); let summands = to_add.to_vec(); self.add_simple_generator(NonNativeMultipleAddsGenerator:: { summands: summands.clone(), sum: sum.clone(), overflow, _phantom: PhantomData, }); range_check_u32_circuit(self, sum.value.limbs.clone()); range_check_u32_circuit(self, vec![overflow]); let sum_expected = summands .iter() .fold(self.zero_biguint(), |a, b| self.add_biguint(&a, &b.value)); let modulus = self.constant_biguint(&FF::order()); let overflow_biguint = BigUintTarget { limbs: vec![overflow], }; let mod_times_overflow = self.mul_biguint(&modulus, &overflow_biguint); let sum_actual = self.add_biguint(&sum.value, &mod_times_overflow); self.connect_biguint(&sum_expected, &sum_actual); // Range-check result. // TODO: can potentially leave unreduced until necessary (e.g. when connecting values). let cmp = self.cmp_biguint(&sum.value, &modulus); let one = self.one(); self.connect(cmp.target, one); sum } // Subtract two `NonNativeTarget`s. fn sub_nonnative( &mut self, a: &NonNativeTarget, b: &NonNativeTarget, ) -> NonNativeTarget { let diff = self.add_virtual_nonnative_target::(); let overflow = self.add_virtual_bool_target_unsafe(); self.add_simple_generator(NonNativeSubtractionGenerator:: { a: a.clone(), b: b.clone(), diff: diff.clone(), overflow, _phantom: PhantomData, }); range_check_u32_circuit(self, diff.value.limbs.clone()); self.assert_bool(overflow); let diff_plus_b = self.add_biguint(&diff.value, &b.value); let modulus = self.constant_biguint(&FF::order()); let mod_times_overflow = self.mul_biguint_by_bool(&modulus, overflow); let diff_plus_b_reduced = self.sub_biguint(&diff_plus_b, &mod_times_overflow); self.connect_biguint(&a.value, &diff_plus_b_reduced); diff } fn mul_nonnative( &mut self, a: &NonNativeTarget, b: &NonNativeTarget, ) -> NonNativeTarget { let prod = self.add_virtual_nonnative_target::(); let modulus = self.constant_biguint(&FF::order()); let overflow = self.add_virtual_biguint_target( a.value.num_limbs() + b.value.num_limbs() - modulus.num_limbs(), ); self.add_simple_generator(NonNativeMultiplicationGenerator:: { a: a.clone(), b: b.clone(), prod: prod.clone(), overflow: overflow.clone(), _phantom: PhantomData, }); range_check_u32_circuit(self, prod.value.limbs.clone()); range_check_u32_circuit(self, overflow.limbs.clone()); let prod_expected = self.mul_biguint(&a.value, &b.value); let mod_times_overflow = self.mul_biguint(&modulus, &overflow); let prod_actual = self.add_biguint(&prod.value, &mod_times_overflow); self.connect_biguint(&prod_expected, &prod_actual); prod } fn mul_many_nonnative( &mut self, to_mul: &[NonNativeTarget], ) -> NonNativeTarget { if to_mul.len() == 1 { return to_mul[0].clone(); } let mut accumulator = self.mul_nonnative(&to_mul[0], &to_mul[1]); for t in to_mul.iter().skip(2) { accumulator = self.mul_nonnative(&accumulator, t); } accumulator } fn neg_nonnative(&mut self, x: &NonNativeTarget) -> NonNativeTarget { let zero_target = self.constant_biguint(&BigUint::zero()); let zero_ff = self.biguint_to_nonnative(&zero_target); self.sub_nonnative(&zero_ff, x) } fn inv_nonnative(&mut self, x: &NonNativeTarget) -> NonNativeTarget { let num_limbs = x.value.num_limbs(); let inv_biguint = self.add_virtual_biguint_target(num_limbs); let div = self.add_virtual_biguint_target(num_limbs); self.add_simple_generator(NonNativeInverseGenerator:: { x: x.clone(), inv: inv_biguint.clone(), div: div.clone(), _phantom: PhantomData, }); let product = self.mul_biguint(&x.value, &inv_biguint); let modulus = self.constant_biguint(&FF::order()); let mod_times_div = self.mul_biguint(&modulus, &div); let one = self.constant_biguint(&BigUint::one()); let expected_product = self.add_biguint(&mod_times_div, &one); self.connect_biguint(&product, &expected_product); NonNativeTarget:: { value: inv_biguint, _phantom: PhantomData, } } /// Returns `x % |FF|` as a `NonNativeTarget`. fn reduce(&mut self, x: &BigUintTarget) -> NonNativeTarget { let modulus = FF::order(); let order_target = self.constant_biguint(&modulus); let value = self.rem_biguint(x, &order_target); NonNativeTarget { value, _phantom: PhantomData, } } fn reduce_nonnative(&mut self, x: &NonNativeTarget) -> NonNativeTarget { let x_biguint = self.nonnative_to_canonical_biguint(x); self.reduce(&x_biguint) } fn bool_to_nonnative(&mut self, b: &BoolTarget) -> NonNativeTarget { let limbs = vec![U32Target(b.target)]; let value = BigUintTarget { limbs }; NonNativeTarget { value, _phantom: PhantomData, } } // Split a nonnative field element to bits. fn split_nonnative_to_bits(&mut self, x: &NonNativeTarget) -> Vec { let num_limbs = x.value.num_limbs(); let mut result = Vec::with_capacity(num_limbs * 32); for i in 0..num_limbs { let limb = x.value.get_limb(i); let bit_targets = self.split_le_base::<2>(limb.0, 32); let mut bits: Vec<_> = bit_targets .iter() .map(|&t| BoolTarget::new_unsafe(t)) .collect(); result.append(&mut bits); } result } fn nonnative_conditional_neg( &mut self, x: &NonNativeTarget, b: BoolTarget, ) -> NonNativeTarget { let not_b = self.not(b); let neg = self.neg_nonnative(x); let x_if_true = self.mul_nonnative_by_bool(&neg, b); let x_if_false = self.mul_nonnative_by_bool(x, not_b); self.add_nonnative(&x_if_true, &x_if_false) } } #[derive(Debug)] struct NonNativeAdditionGenerator, const D: usize, FF: PrimeField> { a: NonNativeTarget, b: NonNativeTarget, sum: NonNativeTarget, overflow: BoolTarget, _phantom: PhantomData, } impl, const D: usize, FF: PrimeField> SimpleGenerator for NonNativeAdditionGenerator { fn id(&self) -> String { "NonNativeAdditionGenerator".into() } fn dependencies(&self) -> Vec { self.a .value .limbs .iter() .cloned() .chain(self.b.value.limbs.clone()) .map(|l| l.0) .collect() } fn run_once( &self, witness: &PartitionWitness, out_buffer: &mut GeneratedValues, ) -> Result<()> { let a = FF::from_noncanonical_biguint(witness.get_biguint_target(self.a.value.clone())); let b = FF::from_noncanonical_biguint(witness.get_biguint_target(self.b.value.clone())); let a_biguint = a.to_canonical_biguint(); let b_biguint = b.to_canonical_biguint(); let sum_biguint = a_biguint + b_biguint; let modulus = FF::order(); let (overflow, sum_reduced) = if sum_biguint > modulus { (true, sum_biguint - modulus) } else { (false, sum_biguint) }; out_buffer.set_biguint_target(&self.sum.value, &sum_reduced)?; out_buffer.set_bool_target(self.overflow, overflow) } fn serialize(&self, dst: &mut Vec, _common_data: &CommonCircuitData) -> IoResult<()> { dst.write_target_vec(&self.a.to_target_vec())?; dst.write_target_vec(&self.b.to_target_vec())?; dst.write_target_vec(&self.sum.to_target_vec())?; dst.write_target_bool(self.overflow) } fn deserialize(src: &mut Buffer, _common_data: &CommonCircuitData) -> IoResult where Self: Sized, { let a = NonNativeTarget::from_target_vec(&src.read_target_vec()?); let b = NonNativeTarget::from_target_vec(&src.read_target_vec()?); let sum = NonNativeTarget::from_target_vec(&src.read_target_vec()?); let overflow = src.read_target_bool()?; Ok(Self { a, b, sum, overflow, _phantom: PhantomData, }) } } #[derive(Debug)] struct NonNativeMultipleAddsGenerator, const D: usize, FF: PrimeField> { summands: Vec>, sum: NonNativeTarget, overflow: U32Target, _phantom: PhantomData, } impl, const D: usize, FF: PrimeField> SimpleGenerator for NonNativeMultipleAddsGenerator { fn id(&self) -> String { "NonNativeMultipleAddsGenerator".into() } fn dependencies(&self) -> Vec { self.summands .iter() .flat_map(|summand| summand.value.limbs.iter().map(|limb| limb.0)) .collect() } fn run_once( &self, witness: &PartitionWitness, out_buffer: &mut GeneratedValues, ) -> Result<()> { let summands: Vec<_> = self .summands .iter() .map(|summand| { FF::from_noncanonical_biguint(witness.get_biguint_target(summand.value.clone())) }) .collect(); let summand_biguints: Vec<_> = summands .iter() .map(|summand| summand.to_canonical_biguint()) .collect(); let sum_biguint = summand_biguints .iter() .fold(BigUint::zero(), |a, b| a + b.clone()); let modulus = FF::order(); let (overflow_biguint, sum_reduced) = sum_biguint.div_rem(&modulus); let overflow = overflow_biguint.to_u64_digits()[0] as u32; out_buffer.set_biguint_target(&self.sum.value, &sum_reduced)?; out_buffer.set_u32_target(self.overflow, overflow) } fn serialize(&self, dst: &mut Vec, _common_data: &CommonCircuitData) -> IoResult<()> { let summands_len = self.summands.len(); dst.write_usize(summands_len)?; self.summands .iter() .try_for_each(|summand| dst.write_target_vec(&summand.to_target_vec()))?; dst.write_target_vec(&self.sum.to_target_vec())?; dst.write_target(self.overflow.0) } fn deserialize(src: &mut Buffer, _common_data: &CommonCircuitData) -> IoResult where Self: Sized, { let summands_len = src.read_usize()?; let summands: Vec<_> = (0..summands_len) .map(|_| { Ok::<_, IoError>(NonNativeTarget::::from_target_vec( &src.read_target_vec()?, )) }) .collect::>()?; let sum = NonNativeTarget::from_target_vec(&src.read_target_vec()?); let overflow = U32Target(src.read_target()?); Ok(Self { summands, sum, overflow, _phantom: PhantomData, }) } } #[derive(Debug)] struct NonNativeSubtractionGenerator, const D: usize, FF: Field> { a: NonNativeTarget, b: NonNativeTarget, diff: NonNativeTarget, overflow: BoolTarget, _phantom: PhantomData, } impl, const D: usize, FF: PrimeField> SimpleGenerator for NonNativeSubtractionGenerator { fn id(&self) -> String { "NonNativeSubtractionGenerator".into() } fn dependencies(&self) -> Vec { self.a .value .limbs .iter() .cloned() .chain(self.b.value.limbs.clone()) .map(|l| l.0) .collect() } fn run_once( &self, witness: &PartitionWitness, out_buffer: &mut GeneratedValues, ) -> Result<()> { let a = FF::from_noncanonical_biguint(witness.get_biguint_target(self.a.value.clone())); let b = FF::from_noncanonical_biguint(witness.get_biguint_target(self.b.value.clone())); let a_biguint = a.to_canonical_biguint(); let b_biguint = b.to_canonical_biguint(); let modulus = FF::order(); let (diff_biguint, overflow) = if a_biguint >= b_biguint { (a_biguint - b_biguint, false) } else { (modulus + a_biguint - b_biguint, true) }; out_buffer.set_biguint_target(&self.diff.value, &diff_biguint)?; out_buffer.set_bool_target(self.overflow, overflow) } fn serialize(&self, dst: &mut Vec, _common_data: &CommonCircuitData) -> IoResult<()> { dst.write_target_vec(&self.a.to_target_vec())?; dst.write_target_vec(&self.b.to_target_vec())?; dst.write_target_vec(&self.diff.to_target_vec())?; dst.write_target_bool(self.overflow) } fn deserialize(src: &mut Buffer, _common_data: &CommonCircuitData) -> IoResult where Self: Sized, { let a = NonNativeTarget::from_target_vec(&src.read_target_vec()?); let b = NonNativeTarget::from_target_vec(&src.read_target_vec()?); let diff = NonNativeTarget::from_target_vec(&src.read_target_vec()?); let overflow = src.read_target_bool()?; Ok(Self { a, b, diff, overflow, _phantom: PhantomData, }) } } #[derive(Debug)] struct NonNativeMultiplicationGenerator, const D: usize, FF: Field> { a: NonNativeTarget, b: NonNativeTarget, prod: NonNativeTarget, overflow: BigUintTarget, _phantom: PhantomData, } impl, const D: usize, FF: PrimeField> SimpleGenerator for NonNativeMultiplicationGenerator { fn id(&self) -> String { "NonNativeMultiplicationGenerator".into() } fn dependencies(&self) -> Vec { self.a .value .limbs .iter() .cloned() .chain(self.b.value.limbs.clone()) .map(|l| l.0) .collect() } fn run_once( &self, witness: &PartitionWitness, out_buffer: &mut GeneratedValues, ) -> Result<()> { let a = FF::from_noncanonical_biguint(witness.get_biguint_target(self.a.value.clone())); let b = FF::from_noncanonical_biguint(witness.get_biguint_target(self.b.value.clone())); let a_biguint = a.to_canonical_biguint(); let b_biguint = b.to_canonical_biguint(); let prod_biguint = a_biguint * b_biguint; let modulus = FF::order(); let (overflow_biguint, prod_reduced) = prod_biguint.div_rem(&modulus); out_buffer.set_biguint_target(&self.prod.value, &prod_reduced)?; out_buffer.set_biguint_target(&self.overflow, &overflow_biguint) } fn serialize(&self, dst: &mut Vec, _common_data: &CommonCircuitData) -> IoResult<()> { dst.write_target_vec(&self.a.to_target_vec())?; dst.write_target_vec(&self.b.to_target_vec())?; dst.write_target_vec(&self.prod.to_target_vec())?; dst.write_target_vec(&self.overflow.to_target_vec()) } fn deserialize(src: &mut Buffer, _common_data: &CommonCircuitData) -> IoResult where Self: Sized, { let a = NonNativeTarget::from_target_vec(&src.read_target_vec()?); let b = NonNativeTarget::from_target_vec(&src.read_target_vec()?); let prod = NonNativeTarget::from_target_vec(&src.read_target_vec()?); let overflow = BigUintTarget::from_target_vec(&src.read_target_vec()?); Ok(Self { a, b, prod, overflow, _phantom: PhantomData, }) } } #[derive(Debug)] struct NonNativeInverseGenerator, const D: usize, FF: PrimeField> { x: NonNativeTarget, inv: BigUintTarget, div: BigUintTarget, _phantom: PhantomData, } impl, const D: usize, FF: PrimeField> SimpleGenerator for NonNativeInverseGenerator { fn id(&self) -> String { "NonNativeInverseGenerator".into() } fn dependencies(&self) -> Vec { self.x.value.limbs.iter().map(|&l| l.0).collect() } fn run_once( &self, witness: &PartitionWitness, out_buffer: &mut GeneratedValues, ) -> Result<()> { let x = FF::from_noncanonical_biguint(witness.get_biguint_target(self.x.value.clone())); let inv = x.inverse(); let x_biguint = x.to_canonical_biguint(); let inv_biguint = inv.to_canonical_biguint(); let prod = x_biguint * &inv_biguint; let modulus = FF::order(); let (div, _rem) = prod.div_rem(&modulus); out_buffer.set_biguint_target(&self.div, &div)?; out_buffer.set_biguint_target(&self.inv, &inv_biguint) } fn serialize(&self, dst: &mut Vec, _common_data: &CommonCircuitData) -> IoResult<()> { dst.write_target_vec(&self.x.to_target_vec())?; dst.write_target_vec(&self.inv.to_target_vec())?; dst.write_target_vec(&self.div.to_target_vec()) } fn deserialize(src: &mut Buffer, _common_data: &CommonCircuitData) -> IoResult where Self: Sized, { let x = NonNativeTarget::from_target_vec(&src.read_target_vec()?); let inv = BigUintTarget::from_target_vec(&src.read_target_vec()?); let div = BigUintTarget::from_target_vec(&src.read_target_vec()?); Ok(Self { x, inv, div, _phantom: PhantomData, }) } } #[cfg(test)] mod tests { use anyhow::Result; use plonky2::field::secp256k1_base::Secp256K1Base; use plonky2::field::types::{Field, PrimeField, Sample}; use plonky2::iop::witness::PartialWitness; use plonky2::plonk::circuit_builder::CircuitBuilder; use plonky2::plonk::circuit_data::CircuitConfig; use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; use crate::gadgets::nonnative::CircuitBuilderNonNative; #[test] fn test_nonnative_add() -> Result<()> { type FF = Secp256K1Base; const D: usize = 2; type C = PoseidonGoldilocksConfig; type F = >::F; let x_ff = FF::rand(); let y_ff = FF::rand(); let sum_ff = x_ff + y_ff; let config = CircuitConfig::standard_ecc_config(); let pw = PartialWitness::new(); let mut builder = CircuitBuilder::::new(config); let x = builder.constant_nonnative(x_ff); let y = builder.constant_nonnative(y_ff); let sum = builder.add_nonnative(&x, &y); let sum_expected = builder.constant_nonnative(sum_ff); builder.connect_nonnative(&sum, &sum_expected); let data = builder.build::(); let proof = data.prove(pw).unwrap(); data.verify(proof) } #[test] fn test_nonnative_many_adds() -> Result<()> { type FF = Secp256K1Base; const D: usize = 2; type C = PoseidonGoldilocksConfig; type F = >::F; let a_ff = FF::rand(); let b_ff = FF::rand(); let c_ff = FF::rand(); let d_ff = FF::rand(); let e_ff = FF::rand(); let f_ff = FF::rand(); let g_ff = FF::rand(); let h_ff = FF::rand(); let sum_ff = a_ff + b_ff + c_ff + d_ff + e_ff + f_ff + g_ff + h_ff; let config = CircuitConfig::standard_ecc_config(); let pw = PartialWitness::new(); let mut builder = CircuitBuilder::::new(config); let a = builder.constant_nonnative(a_ff); let b = builder.constant_nonnative(b_ff); let c = builder.constant_nonnative(c_ff); let d = builder.constant_nonnative(d_ff); let e = builder.constant_nonnative(e_ff); let f = builder.constant_nonnative(f_ff); let g = builder.constant_nonnative(g_ff); let h = builder.constant_nonnative(h_ff); let all = [a, b, c, d, e, f, g, h]; let sum = builder.add_many_nonnative(&all); let sum_expected = builder.constant_nonnative(sum_ff); builder.connect_nonnative(&sum, &sum_expected); let data = builder.build::(); let proof = data.prove(pw).unwrap(); data.verify(proof) } #[test] fn test_nonnative_sub() -> Result<()> { type FF = Secp256K1Base; const D: usize = 2; type C = PoseidonGoldilocksConfig; type F = >::F; let x_ff = FF::rand(); let mut y_ff = FF::rand(); while y_ff.to_canonical_biguint() > x_ff.to_canonical_biguint() { y_ff = FF::rand(); } let diff_ff = x_ff - y_ff; let config = CircuitConfig::standard_ecc_config(); let pw = PartialWitness::new(); let mut builder = CircuitBuilder::::new(config); let x = builder.constant_nonnative(x_ff); let y = builder.constant_nonnative(y_ff); let diff = builder.sub_nonnative(&x, &y); let diff_expected = builder.constant_nonnative(diff_ff); builder.connect_nonnative(&diff, &diff_expected); let data = builder.build::(); let proof = data.prove(pw).unwrap(); data.verify(proof) } #[test] fn test_nonnative_mul() -> Result<()> { type FF = Secp256K1Base; const D: usize = 2; type C = PoseidonGoldilocksConfig; type F = >::F; let x_ff = FF::rand(); let y_ff = FF::rand(); let product_ff = x_ff * y_ff; let config = CircuitConfig::standard_ecc_config(); let pw = PartialWitness::new(); let mut builder = CircuitBuilder::::new(config); let x = builder.constant_nonnative(x_ff); let y = builder.constant_nonnative(y_ff); let product = builder.mul_nonnative(&x, &y); let product_expected = builder.constant_nonnative(product_ff); builder.connect_nonnative(&product, &product_expected); let data = builder.build::(); let proof = data.prove(pw).unwrap(); data.verify(proof) } #[test] fn test_nonnative_neg() -> Result<()> { type FF = Secp256K1Base; const D: usize = 2; type C = PoseidonGoldilocksConfig; type F = >::F; let x_ff = FF::rand(); let neg_x_ff = -x_ff; let config = CircuitConfig::standard_ecc_config(); let pw = PartialWitness::new(); let mut builder = CircuitBuilder::::new(config); let x = builder.constant_nonnative(x_ff); let neg_x = builder.neg_nonnative(&x); let neg_x_expected = builder.constant_nonnative(neg_x_ff); builder.connect_nonnative(&neg_x, &neg_x_expected); let data = builder.build::(); let proof = data.prove(pw).unwrap(); data.verify(proof) } #[test] fn test_nonnative_inv() -> Result<()> { type FF = Secp256K1Base; const D: usize = 2; type C = PoseidonGoldilocksConfig; type F = >::F; let x_ff = FF::rand(); let inv_x_ff = x_ff.inverse(); let config = CircuitConfig::standard_ecc_config(); let pw = PartialWitness::new(); let mut builder = CircuitBuilder::::new(config); let x = builder.constant_nonnative(x_ff); let inv_x = builder.inv_nonnative(&x); let inv_x_expected = builder.constant_nonnative(inv_x_ff); builder.connect_nonnative(&inv_x, &inv_x_expected); let data = builder.build::(); let proof = data.prove(pw).unwrap(); data.verify(proof) } }