pub mod r1cs;
pub mod shape_cs;
pub mod solver;
pub mod test_r1cs;
pub mod test_shape_cs;
#[cfg(test)]
mod tests {
use crate::{
bellpepper::{
solver::SatisfyingAssignment,
test_r1cs::{TestSpartanShape, TestSpartanWitness},
test_shape_cs::TestShapeCS,
},
provider::PallasHyraxEngine,
traits::Engine,
};
use bellpepper_core::{ConstraintSystem, SynthesisError, num::AllocatedNum};
use ff::PrimeField;
fn synthesize_alloc_bit<Fr: PrimeField, CS: ConstraintSystem<Fr>>(
cs: &mut CS,
) -> Result<(), SynthesisError> {
let a = AllocatedNum::alloc(cs.namespace(|| "a"), || Ok(Fr::ONE))?;
let _ = a.inputize(cs.namespace(|| "a is input"));
cs.enforce(
|| "check a is 0 or 1",
|lc| lc + CS::one() - a.get_variable(),
|lc| lc + a.get_variable(),
|lc| lc,
);
let b = AllocatedNum::alloc(cs.namespace(|| "b"), || Ok(Fr::ONE))?;
let _ = b.inputize(cs.namespace(|| "b is input"));
cs.enforce(
|| "check b is 0 or 1",
|lc| lc + CS::one() - b.get_variable(),
|lc| lc + b.get_variable(),
|lc| lc,
);
Ok(())
}
fn test_alloc_bit_with<E: Engine>() {
let mut cs: TestShapeCS<E> = TestShapeCS::new();
let _ = synthesize_alloc_bit(&mut cs);
let (shape, ck, _vk) = cs.r1cs_shape().unwrap();
let mut cs = SatisfyingAssignment::<E>::new();
let _ = synthesize_alloc_bit(&mut cs);
let (inst, witness) = cs.r1cs_instance_and_witness(&shape, &ck, true).unwrap();
assert!(shape.is_sat(&ck, &inst, &witness).is_ok());
}
#[test]
fn test_alloc_bit() {
test_alloc_bit_with::<PallasHyraxEngine>();
}
use crate::{
bellpepper::{
r1cs::{MultiRoundSpartanShape, MultiRoundSpartanWitness},
shape_cs::ShapeCS,
},
traits::{circuit::MultiRoundCircuit, transcript::TranscriptEngineTrait},
};
#[derive(Clone)]
struct TwoRoundBitsCircuit;
impl<E: Engine> MultiRoundCircuit<E> for TwoRoundBitsCircuit {
fn num_challenges(&self, _round_index: usize) -> Result<usize, SynthesisError> {
Ok(0)
}
fn rounds<CS: ConstraintSystem<E::Scalar>>(
&self,
cs: &mut CS,
round_index: usize,
_prior_round_vars: &[Vec<AllocatedNum<E::Scalar>>],
_prev_challenges: &[Vec<AllocatedNum<E::Scalar>>],
_challenges: Option<&[E::Scalar]>,
) -> Result<(Vec<AllocatedNum<E::Scalar>>, Vec<AllocatedNum<E::Scalar>>), SynthesisError> {
let bit = AllocatedNum::alloc(cs.namespace(|| format!("bit_{round_index}")), || {
Ok(<E::Scalar as ff::Field>::ONE)
})?;
cs.enforce(
|| format!("bit_boolean_{round_index}"),
|lc| lc + bit.get_variable(),
|lc| lc + CS::one() - bit.get_variable(),
|lc| lc,
);
Ok((vec![bit], vec![]))
}
fn num_rounds(&self) -> usize {
2
}
}
fn test_multiround_bits_with<E: Engine>() {
let circuit = TwoRoundBitsCircuit;
let (shape, ck, _vk) =
<ShapeCS<E> as MultiRoundSpartanShape<E>>::multiround_r1cs_shape(&circuit).unwrap();
let mut state = SatisfyingAssignment::<E>::initialize_multiround_witness(&shape).unwrap();
let mut transcript = <E as Engine>::TE::new(b"test");
let num_rounds = <TwoRoundBitsCircuit as MultiRoundCircuit<E>>::num_rounds(&circuit);
for r in 0..num_rounds {
let _ = SatisfyingAssignment::<E>::process_round(
&mut state,
&shape,
&ck,
&circuit,
r,
&mut transcript,
)
.unwrap();
}
let (instance, witness) =
SatisfyingAssignment::<E>::finalize_multiround_witness(&mut state, &shape).unwrap();
let regular_shape = shape.to_regular_shape();
let regular_instance = instance.to_regular_instance().unwrap();
let sat_res = regular_shape.is_sat(&ck, ®ular_instance, &witness);
assert!(sat_res.is_ok());
}
#[test]
fn test_multiround_bits() {
test_multiround_bits_with::<PallasHyraxEngine>();
}
use ff::Field;
#[derive(Clone)]
struct TwoRoundPermutationCircuit;
impl<E: Engine> MultiRoundCircuit<E> for TwoRoundPermutationCircuit {
fn num_challenges(&self, round_index: usize) -> Result<usize, SynthesisError> {
if round_index >= 2 {
return Err(SynthesisError::Unsatisfiable);
}
Ok(match round_index {
0 => 1, 1 => 0, _ => 0, })
}
fn rounds<CS: ConstraintSystem<E::Scalar>>(
&self,
cs: &mut CS,
round_index: usize,
prior_round_vars: &[Vec<AllocatedNum<E::Scalar>>],
_prev_challenges: &[Vec<AllocatedNum<E::Scalar>>],
challenges: Option<&[E::Scalar]>,
) -> Result<(Vec<AllocatedNum<E::Scalar>>, Vec<AllocatedNum<E::Scalar>>), SynthesisError> {
match round_index {
0 => {
let one = E::Scalar::from(1u64);
let two = E::Scalar::from(2u64);
let three = E::Scalar::from(3u64);
let a = AllocatedNum::alloc(cs.namespace(|| "a"), || Ok(one))?;
let b = AllocatedNum::alloc(cs.namespace(|| "b"), || Ok(two))?;
let c = AllocatedNum::alloc(cs.namespace(|| "c"), || Ok(three))?;
let d = AllocatedNum::alloc(cs.namespace(|| "d"), || Ok(three))?;
let e = AllocatedNum::alloc(cs.namespace(|| "e"), || Ok(one))?;
let f = AllocatedNum::alloc(cs.namespace(|| "f"), || Ok(two))?;
Ok((vec![a, b, c, d, e, f], vec![]))
}
1 => {
let round0_vars = &prior_round_vars[0];
let a = &round0_vars[0];
let b = &round0_vars[1];
let c = &round0_vars[2];
let d = &round0_vars[3];
let e = &round0_vars[4];
let f = &round0_vars[5];
let r_val = challenges.map(|v| v[0]).unwrap_or(E::Scalar::ZERO);
let r = AllocatedNum::alloc_input(cs.namespace(|| "r"), || Ok(r_val))?;
let sub_r = |cs: &mut CS, name: &'static str, x: &AllocatedNum<E::Scalar>| {
let val = x.get_value().zip(r.get_value()).map(|(xv, rv)| xv - rv);
AllocatedNum::alloc(cs.namespace(|| name), || {
val.ok_or(SynthesisError::AssignmentMissing)
})
};
let diff_a = sub_r(cs, "diff_a", a)?;
let diff_b = sub_r(cs, "diff_b", b)?;
let diff_c = sub_r(cs, "diff_c", c)?;
let diff_d = sub_r(cs, "diff_d", d)?;
let diff_e = sub_r(cs, "diff_e", e)?;
let diff_f = sub_r(cs, "diff_f", f)?;
let enforce_sub = |label: &'static str,
cs: &mut CS,
x: &AllocatedNum<E::Scalar>,
diff: &AllocatedNum<E::Scalar>| {
cs.enforce(
|| label,
|lc| lc + x.get_variable() - r.get_variable(),
|lc| lc + CS::one(),
|lc| lc + diff.get_variable(),
);
};
enforce_sub("a_minus_r", cs, a, &diff_a);
enforce_sub("b_minus_r", cs, b, &diff_b);
enforce_sub("c_minus_r", cs, c, &diff_c);
enforce_sub("d_minus_r", cs, d, &diff_d);
enforce_sub("e_minus_r", cs, e, &diff_e);
enforce_sub("f_minus_r", cs, f, &diff_f);
let lhs_ab = AllocatedNum::alloc(cs.namespace(|| "lhs_ab"), || {
diff_a
.get_value()
.zip(diff_b.get_value())
.map(|(x, y)| x * y)
.ok_or(SynthesisError::AssignmentMissing)
})?;
cs.enforce(
|| "lhs_ab_mul",
|lc| lc + diff_a.get_variable(),
|lc| lc + diff_b.get_variable(),
|lc| lc + lhs_ab.get_variable(),
);
let lhs = AllocatedNum::alloc(cs.namespace(|| "lhs"), || {
lhs_ab
.get_value()
.zip(diff_c.get_value())
.map(|(x, y)| x * y)
.ok_or(SynthesisError::AssignmentMissing)
})?;
cs.enforce(
|| "lhs_mul",
|lc| lc + lhs_ab.get_variable(),
|lc| lc + diff_c.get_variable(),
|lc| lc + lhs.get_variable(),
);
let rhs_de = AllocatedNum::alloc(cs.namespace(|| "rhs_de"), || {
diff_d
.get_value()
.zip(diff_e.get_value())
.map(|(x, y)| x * y)
.ok_or(SynthesisError::AssignmentMissing)
})?;
cs.enforce(
|| "rhs_de_mul",
|lc| lc + diff_d.get_variable(),
|lc| lc + diff_e.get_variable(),
|lc| lc + rhs_de.get_variable(),
);
let rhs = AllocatedNum::alloc(cs.namespace(|| "rhs"), || {
rhs_de
.get_value()
.zip(diff_f.get_value())
.map(|(x, y)| x * y)
.ok_or(SynthesisError::AssignmentMissing)
})?;
cs.enforce(
|| "rhs_mul",
|lc| lc + rhs_de.get_variable(),
|lc| lc + diff_f.get_variable(),
|lc| lc + rhs.get_variable(),
);
cs.enforce(
|| "lhs_eq_rhs",
|lc| lc + lhs.get_variable(),
|lc| lc + CS::one(),
|lc| lc + rhs.get_variable(),
);
Ok((vec![lhs, rhs], vec![r]))
}
_ => Err(SynthesisError::Unsatisfiable),
}
}
fn num_rounds(&self) -> usize {
2
}
}
fn test_multiround_permutation_with<E: Engine>() {
let circuit = TwoRoundPermutationCircuit;
let (shape, ck, _vk) =
<ShapeCS<E> as MultiRoundSpartanShape<E>>::multiround_r1cs_shape(&circuit).unwrap();
let mut state = SatisfyingAssignment::<E>::initialize_multiround_witness(&shape).unwrap();
let mut transcript = <E as Engine>::TE::new(b"test");
let num_rounds = <TwoRoundPermutationCircuit as MultiRoundCircuit<E>>::num_rounds(&circuit);
for r in 0..num_rounds {
let _ = SatisfyingAssignment::<E>::process_round(
&mut state,
&shape,
&ck,
&circuit,
r,
&mut transcript,
)
.unwrap();
}
let (instance, witness) =
SatisfyingAssignment::<E>::finalize_multiround_witness(&mut state, &shape).unwrap();
let regular_shape = shape.to_regular_shape();
let regular_instance = instance.to_regular_instance().unwrap();
let sat_res = regular_shape.is_sat(&ck, ®ular_instance, &witness);
assert!(sat_res.is_ok());
}
#[test]
fn test_multiround_permutation() {
test_multiround_permutation_with::<PallasHyraxEngine>();
}
}