use midnight_proofs::{
circuit::{Layouter, Value},
plonk::Error,
};
use crate::{
types::{AssignedNative, Instantiable},
CircuitField,
};
pub trait PublicInputInstructions<F, Assigned>
where
F: CircuitField,
Assigned: Instantiable<F>,
{
fn as_public_input(
&self,
layouter: &mut impl Layouter<F>,
assigned: &Assigned,
) -> Result<Vec<AssignedNative<F>>, Error>;
fn constrain_as_public_input(
&self,
layouter: &mut impl Layouter<F>,
assigned: &Assigned,
) -> Result<(), Error>;
fn assign_as_public_input(
&self,
layouter: &mut impl Layouter<F>,
value: Value<Assigned::Element>,
) -> Result<Assigned, Error>;
}
pub trait CommittedInstanceInstructions<F, Assigned>
where
F: CircuitField,
Assigned: Instantiable<F>,
{
fn constrain_as_committed_public_input(
&self,
layouter: &mut impl Layouter<F>,
assigned: &Assigned,
) -> Result<(), Error>;
}
#[cfg(test)]
pub(crate) mod tests {
use std::marker::PhantomData;
use ff::FromUniformBytes;
use midnight_proofs::{
circuit::{Layouter, SimpleFloorPlanner, Value},
dev::MockProver,
plonk::{Circuit, ConstraintSystem},
};
use rand::{rngs::OsRng, SeedableRng};
use rand_chacha::ChaCha8Rng;
use super::*;
use crate::{
instructions::AssignmentInstructions,
testing_utils::{FromScratch, Sampleable},
types::{InnerConstants, InnerValue},
utils::circuit_modeling::{circuit_to_json, cost_measure_end, cost_measure_start},
};
#[derive(Clone, Debug)]
enum Operation {
Constrain,
Assign,
}
#[derive(Clone, Debug)]
struct TestCircuit<F, Assigned, Chip>
where
Assigned: InnerValue,
{
x: Assigned::Element,
must_pass: bool,
operation: Operation,
_marker: PhantomData<(F, Assigned, Chip)>,
}
impl<F, Assigned, Chip> Circuit<F> for TestCircuit<F, Assigned, Chip>
where
F: CircuitField,
Assigned: Instantiable<F> + Sampleable,
Chip: AssignmentInstructions<F, Assigned>
+ PublicInputInstructions<F, Assigned>
+ FromScratch<F>,
{
type Config = <Chip as FromScratch<F>>::Config;
type FloorPlanner = SimpleFloorPlanner;
type Params = ();
fn without_witnesses(&self) -> Self {
unreachable!()
}
fn configure(meta: &mut ConstraintSystem<F>) -> Self::Config {
let committed_instance_column = meta.instance_column();
let instance_column = meta.instance_column();
Chip::configure_from_scratch(
meta,
&mut vec![],
&mut vec![],
&[committed_instance_column, instance_column],
)
}
fn synthesize(
&self,
config: Self::Config,
mut layouter: impl Layouter<F>,
) -> Result<(), Error> {
let chip = Chip::new_from_scratch(&config);
let x_val = if self.must_pass {
self.x.clone()
} else {
Assigned::sample_inner(OsRng)
};
cost_measure_start(&mut layouter);
let x = match self.operation {
Operation::Constrain => {
let x = chip.assign(&mut layouter, Value::known(x_val.clone()))?;
chip.constrain_as_public_input(&mut layouter, &x)?;
x
}
Operation::Assign => {
chip.assign_as_public_input(&mut layouter, Value::known(x_val.clone()))?
}
};
cost_measure_end(&mut layouter);
if self.must_pass {
chip.as_public_input(&mut layouter, &x)?
.iter()
.zip(Assigned::as_public_input(&x_val))
.for_each(|(xi, ci)| {
xi.value().map(|v| assert_eq!(*v, ci));
});
}
chip.load_from_scratch(&mut layouter)
}
}
fn run<F, Assigned, Chip>(
x: &Assigned::Element,
operation: Operation,
must_pass: bool,
cost_model: bool,
chip_name: &str,
) where
F: CircuitField + FromUniformBytes<64> + Ord,
Assigned: Instantiable<F> + Sampleable,
Chip: AssignmentInstructions<F, Assigned>
+ PublicInputInstructions<F, Assigned>
+ FromScratch<F>,
{
let circuit = TestCircuit::<F, Assigned, Chip> {
x: x.clone(),
must_pass,
operation,
_marker: PhantomData,
};
let pi = Assigned::as_public_input(x);
match MockProver::run(&circuit, vec![vec![], pi.clone()]) {
Ok(prover) => match prover.verify() {
Ok(()) => assert!(must_pass),
Err(e) => assert!(!must_pass, "Failed verifier with error {e:?}"),
},
Err(e) => assert!(!must_pass, "Failed prover with error {e:?}"),
}
if cost_model {
circuit_to_json(chip_name, "public_inputs", circuit);
}
}
pub fn test_public_inputs<F, Assigned, Chip>(name: &str)
where
F: CircuitField + FromUniformBytes<64> + Ord,
Assigned: Instantiable<F> + InnerConstants + Sampleable,
Chip: AssignmentInstructions<F, Assigned>
+ PublicInputInstructions<F, Assigned>
+ FromScratch<F>,
{
let mut rng = ChaCha8Rng::seed_from_u64(0xc0ffee);
let mut cost_model = true;
[
Assigned::sample_inner(&mut rng),
Assigned::inner_zero(),
Assigned::inner_one(),
]
.into_iter()
.for_each(|x| {
run::<F, Assigned, Chip>(&x, Operation::Constrain, true, cost_model, name);
cost_model = false;
run::<F, Assigned, Chip>(&x, Operation::Assign, true, cost_model, name);
run::<F, Assigned, Chip>(&x, Operation::Constrain, false, cost_model, name);
run::<F, Assigned, Chip>(&x, Operation::Assign, false, cost_model, name);
});
}
}