You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1014 lines
32 KiB

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<FF: Field> {
pub(crate) value: BigUintTarget,
pub(crate) _phantom: PhantomData<FF>,
}
impl<FF: Field> NonNativeTarget<FF> {
pub fn to_target_vec(&self) -> Vec<Target> {
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<F: RichField + Extendable<D>, const D: usize> {
fn num_nonnative_limbs<FF: Field>() -> usize {
div_ceil(FF::BITS, 32)
}
fn biguint_to_nonnative<FF: Field>(&mut self, x: &BigUintTarget) -> NonNativeTarget<FF>;
fn nonnative_to_canonical_biguint<FF: Field>(
&mut self,
x: &NonNativeTarget<FF>,
) -> BigUintTarget;
fn constant_nonnative<FF: PrimeField>(&mut self, x: FF) -> NonNativeTarget<FF>;
fn zero_nonnative<FF: PrimeField>(&mut self) -> NonNativeTarget<FF>;
// Assert that two NonNativeTarget's, both assumed to be in reduced form, are equal.
fn connect_nonnative<FF: Field>(
&mut self,
lhs: &NonNativeTarget<FF>,
rhs: &NonNativeTarget<FF>,
);
fn add_virtual_nonnative_target<FF: Field>(&mut self) -> NonNativeTarget<FF>;
fn add_virtual_nonnative_target_sized<FF: Field>(
&mut self,
num_limbs: usize,
) -> NonNativeTarget<FF>;
fn add_nonnative<FF: PrimeField>(
&mut self,
a: &NonNativeTarget<FF>,
b: &NonNativeTarget<FF>,
) -> NonNativeTarget<FF>;
fn mul_nonnative_by_bool<FF: Field>(
&mut self,
a: &NonNativeTarget<FF>,
b: BoolTarget,
) -> NonNativeTarget<FF>;
fn if_nonnative<FF: PrimeField>(
&mut self,
b: BoolTarget,
x: &NonNativeTarget<FF>,
y: &NonNativeTarget<FF>,
) -> NonNativeTarget<FF>;
fn add_many_nonnative<FF: PrimeField>(
&mut self,
to_add: &[NonNativeTarget<FF>],
) -> NonNativeTarget<FF>;
// Subtract two `NonNativeTarget`s.
fn sub_nonnative<FF: PrimeField>(
&mut self,
a: &NonNativeTarget<FF>,
b: &NonNativeTarget<FF>,
) -> NonNativeTarget<FF>;
fn mul_nonnative<FF: PrimeField>(
&mut self,
a: &NonNativeTarget<FF>,
b: &NonNativeTarget<FF>,
) -> NonNativeTarget<FF>;
fn mul_many_nonnative<FF: PrimeField>(
&mut self,
to_mul: &[NonNativeTarget<FF>],
) -> NonNativeTarget<FF>;
fn neg_nonnative<FF: PrimeField>(&mut self, x: &NonNativeTarget<FF>) -> NonNativeTarget<FF>;
fn inv_nonnative<FF: PrimeField>(&mut self, x: &NonNativeTarget<FF>) -> NonNativeTarget<FF>;
/// Returns `x % |FF|` as a `NonNativeTarget`.
fn reduce<FF: Field>(&mut self, x: &BigUintTarget) -> NonNativeTarget<FF>;
fn reduce_nonnative<FF: Field>(&mut self, x: &NonNativeTarget<FF>) -> NonNativeTarget<FF>;
fn bool_to_nonnative<FF: Field>(&mut self, b: &BoolTarget) -> NonNativeTarget<FF>;
// Split a nonnative field element to bits.
fn split_nonnative_to_bits<FF: Field>(&mut self, x: &NonNativeTarget<FF>) -> Vec<BoolTarget>;
fn nonnative_conditional_neg<FF: PrimeField>(
&mut self,
x: &NonNativeTarget<FF>,
b: BoolTarget,
) -> NonNativeTarget<FF>;
}
impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilderNonNative<F, D>
for CircuitBuilder<F, D>
{
fn num_nonnative_limbs<FF: Field>() -> usize {
div_ceil(FF::BITS, 32)
}
fn biguint_to_nonnative<FF: Field>(&mut self, x: &BigUintTarget) -> NonNativeTarget<FF> {
NonNativeTarget {
value: x.clone(),
_phantom: PhantomData,
}
}
fn nonnative_to_canonical_biguint<FF: Field>(
&mut self,
x: &NonNativeTarget<FF>,
) -> BigUintTarget {
x.value.clone()
}
fn constant_nonnative<FF: PrimeField>(&mut self, x: FF) -> NonNativeTarget<FF> {
let x_biguint = self.constant_biguint(&x.to_canonical_biguint());
self.biguint_to_nonnative(&x_biguint)
}
fn zero_nonnative<FF: PrimeField>(&mut self) -> NonNativeTarget<FF> {
self.constant_nonnative(FF::ZERO)
}
// Assert that two NonNativeTarget's, both assumed to be in reduced form, are equal.
fn connect_nonnative<FF: Field>(
&mut self,
lhs: &NonNativeTarget<FF>,
rhs: &NonNativeTarget<FF>,
) {
self.connect_biguint(&lhs.value, &rhs.value);
}
fn add_virtual_nonnative_target<FF: Field>(&mut self) -> NonNativeTarget<FF> {
let num_limbs = Self::num_nonnative_limbs::<FF>();
let value = self.add_virtual_biguint_target(num_limbs);
NonNativeTarget {
value,
_phantom: PhantomData,
}
}
fn add_virtual_nonnative_target_sized<FF: Field>(
&mut self,
num_limbs: usize,
) -> NonNativeTarget<FF> {
let value = self.add_virtual_biguint_target(num_limbs);
NonNativeTarget {
value,
_phantom: PhantomData,
}
}
fn add_nonnative<FF: PrimeField>(
&mut self,
a: &NonNativeTarget<FF>,
b: &NonNativeTarget<FF>,
) -> NonNativeTarget<FF> {
let sum = self.add_virtual_nonnative_target::<FF>();
let overflow = self.add_virtual_bool_target_unsafe();
self.add_simple_generator(NonNativeAdditionGenerator::<F, D, FF> {
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<FF: Field>(
&mut self,
a: &NonNativeTarget<FF>,
b: BoolTarget,
) -> NonNativeTarget<FF> {
NonNativeTarget {
value: self.mul_biguint_by_bool(&a.value, b),
_phantom: PhantomData,
}
}
fn if_nonnative<FF: PrimeField>(
&mut self,
b: BoolTarget,
x: &NonNativeTarget<FF>,
y: &NonNativeTarget<FF>,
) -> NonNativeTarget<FF> {
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<FF: PrimeField>(
&mut self,
to_add: &[NonNativeTarget<FF>],
) -> NonNativeTarget<FF> {
if to_add.len() == 1 {
return to_add[0].clone();
}
let sum = self.add_virtual_nonnative_target::<FF>();
let overflow = self.add_virtual_u32_target();
let summands = to_add.to_vec();
self.add_simple_generator(NonNativeMultipleAddsGenerator::<F, D, FF> {
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<FF: PrimeField>(
&mut self,
a: &NonNativeTarget<FF>,
b: &NonNativeTarget<FF>,
) -> NonNativeTarget<FF> {
let diff = self.add_virtual_nonnative_target::<FF>();
let overflow = self.add_virtual_bool_target_unsafe();
self.add_simple_generator(NonNativeSubtractionGenerator::<F, D, FF> {
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<FF: PrimeField>(
&mut self,
a: &NonNativeTarget<FF>,
b: &NonNativeTarget<FF>,
) -> NonNativeTarget<FF> {
let prod = self.add_virtual_nonnative_target::<FF>();
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::<F, D, FF> {
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<FF: PrimeField>(
&mut self,
to_mul: &[NonNativeTarget<FF>],
) -> NonNativeTarget<FF> {
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<FF: PrimeField>(&mut self, x: &NonNativeTarget<FF>) -> NonNativeTarget<FF> {
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<FF: PrimeField>(&mut self, x: &NonNativeTarget<FF>) -> NonNativeTarget<FF> {
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::<F, D, FF> {
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::<FF> {
value: inv_biguint,
_phantom: PhantomData,
}
}
/// Returns `x % |FF|` as a `NonNativeTarget`.
fn reduce<FF: Field>(&mut self, x: &BigUintTarget) -> NonNativeTarget<FF> {
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<FF: Field>(&mut self, x: &NonNativeTarget<FF>) -> NonNativeTarget<FF> {
let x_biguint = self.nonnative_to_canonical_biguint(x);
self.reduce(&x_biguint)
}
fn bool_to_nonnative<FF: Field>(&mut self, b: &BoolTarget) -> NonNativeTarget<FF> {
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<FF: Field>(&mut self, x: &NonNativeTarget<FF>) -> Vec<BoolTarget> {
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<FF: PrimeField>(
&mut self,
x: &NonNativeTarget<FF>,
b: BoolTarget,
) -> NonNativeTarget<FF> {
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<F: RichField + Extendable<D>, const D: usize, FF: PrimeField> {
a: NonNativeTarget<FF>,
b: NonNativeTarget<FF>,
sum: NonNativeTarget<FF>,
overflow: BoolTarget,
_phantom: PhantomData<F>,
}
impl<F: RichField + Extendable<D>, const D: usize, FF: PrimeField> SimpleGenerator<F, D>
for NonNativeAdditionGenerator<F, D, FF>
{
fn id(&self) -> String {
"NonNativeAdditionGenerator".into()
}
fn dependencies(&self) -> Vec<Target> {
self.a
.value
.limbs
.iter()
.cloned()
.chain(self.b.value.limbs.clone())
.map(|l| l.0)
.collect()
}
fn run_once(
&self,
witness: &PartitionWitness<F>,
out_buffer: &mut GeneratedValues<F>,
) -> 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<u8>, _common_data: &CommonCircuitData<F, D>) -> 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<F, D>) -> IoResult<Self>
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<F: RichField + Extendable<D>, const D: usize, FF: PrimeField>
{
summands: Vec<NonNativeTarget<FF>>,
sum: NonNativeTarget<FF>,
overflow: U32Target,
_phantom: PhantomData<F>,
}
impl<F: RichField + Extendable<D>, const D: usize, FF: PrimeField> SimpleGenerator<F, D>
for NonNativeMultipleAddsGenerator<F, D, FF>
{
fn id(&self) -> String {
"NonNativeMultipleAddsGenerator".into()
}
fn dependencies(&self) -> Vec<Target> {
self.summands
.iter()
.flat_map(|summand| summand.value.limbs.iter().map(|limb| limb.0))
.collect()
}
fn run_once(
&self,
witness: &PartitionWitness<F>,
out_buffer: &mut GeneratedValues<F>,
) -> 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<u8>, _common_data: &CommonCircuitData<F, D>) -> 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<F, D>) -> IoResult<Self>
where
Self: Sized,
{
let summands_len = src.read_usize()?;
let summands: Vec<_> = (0..summands_len)
.map(|_| {
Ok::<_, IoError>(NonNativeTarget::<FF>::from_target_vec(
&src.read_target_vec()?,
))
})
.collect::<IoResult<_>>()?;
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<F: RichField + Extendable<D>, const D: usize, FF: Field> {
a: NonNativeTarget<FF>,
b: NonNativeTarget<FF>,
diff: NonNativeTarget<FF>,
overflow: BoolTarget,
_phantom: PhantomData<F>,
}
impl<F: RichField + Extendable<D>, const D: usize, FF: PrimeField> SimpleGenerator<F, D>
for NonNativeSubtractionGenerator<F, D, FF>
{
fn id(&self) -> String {
"NonNativeSubtractionGenerator".into()
}
fn dependencies(&self) -> Vec<Target> {
self.a
.value
.limbs
.iter()
.cloned()
.chain(self.b.value.limbs.clone())
.map(|l| l.0)
.collect()
}
fn run_once(
&self,
witness: &PartitionWitness<F>,
out_buffer: &mut GeneratedValues<F>,
) -> 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<u8>, _common_data: &CommonCircuitData<F, D>) -> 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<F, D>) -> IoResult<Self>
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<F: RichField + Extendable<D>, const D: usize, FF: Field> {
a: NonNativeTarget<FF>,
b: NonNativeTarget<FF>,
prod: NonNativeTarget<FF>,
overflow: BigUintTarget,
_phantom: PhantomData<F>,
}
impl<F: RichField + Extendable<D>, const D: usize, FF: PrimeField> SimpleGenerator<F, D>
for NonNativeMultiplicationGenerator<F, D, FF>
{
fn id(&self) -> String {
"NonNativeMultiplicationGenerator".into()
}
fn dependencies(&self) -> Vec<Target> {
self.a
.value
.limbs
.iter()
.cloned()
.chain(self.b.value.limbs.clone())
.map(|l| l.0)
.collect()
}
fn run_once(
&self,
witness: &PartitionWitness<F>,
out_buffer: &mut GeneratedValues<F>,
) -> 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<u8>, _common_data: &CommonCircuitData<F, D>) -> 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<F, D>) -> IoResult<Self>
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<F: RichField + Extendable<D>, const D: usize, FF: PrimeField> {
x: NonNativeTarget<FF>,
inv: BigUintTarget,
div: BigUintTarget,
_phantom: PhantomData<F>,
}
impl<F: RichField + Extendable<D>, const D: usize, FF: PrimeField> SimpleGenerator<F, D>
for NonNativeInverseGenerator<F, D, FF>
{
fn id(&self) -> String {
"NonNativeInverseGenerator".into()
}
fn dependencies(&self) -> Vec<Target> {
self.x.value.limbs.iter().map(|&l| l.0).collect()
}
fn run_once(
&self,
witness: &PartitionWitness<F>,
out_buffer: &mut GeneratedValues<F>,
) -> 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<u8>, _common_data: &CommonCircuitData<F, D>) -> 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<F, D>) -> IoResult<Self>
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 = <C as GenericConfig<D>>::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::<F, D>::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::<C>();
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 = <C as GenericConfig<D>>::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::<F, D>::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::<C>();
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 = <C as GenericConfig<D>>::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::<F, D>::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::<C>();
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 = <C as GenericConfig<D>>::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::<F, D>::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::<C>();
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 = <C as GenericConfig<D>>::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::<F, D>::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::<C>();
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 = <C as GenericConfig<D>>::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::<F, D>::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::<C>();
let proof = data.prove(pw).unwrap();
data.verify(proof)
}
}