use midnight_proofs::{circuit::Layouter, plonk::Error};
use crate::{types::InnerValue, CircuitField};
pub trait ConversionInstructions<F, AssignedSource, AssignedTarget>
where
F: CircuitField,
AssignedSource: InnerValue,
AssignedTarget: InnerValue,
{
fn convert_value(&self, x: &AssignedSource::Element) -> Option<AssignedTarget::Element>;
fn convert(
&self,
layouter: &mut impl Layouter<F>,
x: &AssignedSource,
) -> Result<AssignedTarget, Error>;
}
pub trait UnsafeConversionInstructions<F, AssignedSource, AssignedTarget>:
ConversionInstructions<F, AssignedSource, AssignedTarget>
where
F: CircuitField,
AssignedSource: InnerValue,
AssignedTarget: InnerValue,
{
fn convert_unsafe(
&self,
layouter: &mut impl Layouter<F>,
x: &AssignedSource,
) -> Result<AssignedTarget, 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 super::*;
use crate::{
instructions::{AssertionInstructions, AssignmentInstructions},
testing_utils::FromScratch,
utils::circuit_modeling::{circuit_to_json, cost_measure_end, cost_measure_start},
};
#[derive(Clone, Debug)]
pub enum Operation {
Convert,
UnsafeConvert,
}
#[derive(Clone, Debug)]
struct TestCircuit<F, AssignedSource, AssignedTarget, ConversionChip>
where
AssignedSource: InnerValue,
AssignedTarget: InnerValue,
{
x: AssignedSource::Element,
expected: Option<AssignedTarget::Element>,
operation: Operation,
_marker: PhantomData<(F, AssignedSource, AssignedTarget, ConversionChip)>,
}
impl<F, AssignedSource, AssignedTarget, ConversionChip> Circuit<F>
for TestCircuit<F, AssignedSource, AssignedTarget, ConversionChip>
where
F: CircuitField,
AssignedSource: InnerValue,
AssignedTarget: InnerValue,
AssignedSource::Element: Clone + Default,
AssignedTarget::Element: Clone + Default,
ConversionChip: ConversionInstructions<F, AssignedSource, AssignedTarget>
+ UnsafeConversionInstructions<F, AssignedSource, AssignedTarget>
+ AssignmentInstructions<F, AssignedSource>
+ AssertionInstructions<F, AssignedTarget>
+ FromScratch<F>,
{
type Config = <ConversionChip 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();
ConversionChip::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 = ConversionChip::new_from_scratch(&config);
let x = chip.assign(&mut layouter, Value::known(self.x.clone()))?;
cost_measure_start(&mut layouter);
let y = match self.operation {
Operation::Convert => chip.convert(&mut layouter, &x),
Operation::UnsafeConvert => chip.convert_unsafe(&mut layouter, &x),
}?;
cost_measure_end(&mut layouter);
if let Some(expected) = self.expected.clone() {
chip.assert_equal_to_fixed(&mut layouter, &y, expected)?;
}
Ok(())
}
}
pub fn run<F, AssignedSource, AssignedTarget, ConversionChip>(
x: AssignedSource::Element,
expected: Option<AssignedTarget::Element>,
operation: Operation,
must_pass: bool,
cost_model: bool,
chip_name: &str,
op_name: &str,
) where
F: CircuitField + FromUniformBytes<64> + Ord,
AssignedSource: InnerValue,
AssignedSource::Element: Clone + Default,
AssignedTarget: InnerValue,
AssignedTarget::Element: Clone + Default,
ConversionChip: ConversionInstructions<F, AssignedSource, AssignedTarget>
+ UnsafeConversionInstructions<F, AssignedSource, AssignedTarget>
+ AssignmentInstructions<F, AssignedSource>
+ AssertionInstructions<F, AssignedTarget>
+ FromScratch<F>,
{
let circuit = TestCircuit::<F, AssignedSource, AssignedTarget, ConversionChip> {
x,
expected,
operation,
_marker: PhantomData,
};
let public_inputs = vec![vec![], vec![]];
match MockProver::run(&circuit, public_inputs) {
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, op_name, circuit);
}
}
}