use std::fmt::Debug;
use midnight_proofs::{circuit::Layouter, plonk::Error};
use crate::{types::InnerValue, CircuitField};
pub trait SpongeCPU<Input, Output> {
type StateCPU;
fn init(input_len: Option<usize>) -> Self::StateCPU;
fn absorb(state: &mut Self::StateCPU, inputs: &[Input]);
fn squeeze(state: &mut Self::StateCPU) -> Output;
}
pub trait SpongeInstructions<F, Input, Output>: SpongeCPU<Input::Element, Output::Element>
where
F: CircuitField,
Input: InnerValue,
Output: InnerValue,
{
type State: Clone + Debug;
fn init(
&self,
layouter: &mut impl Layouter<F>,
input_len: Option<usize>,
) -> Result<Self::State, Error>;
fn absorb(
&self,
layouter: &mut impl Layouter<F>,
state: &mut Self::State,
inputs: &[Input],
) -> Result<(), Error>;
fn squeeze(
&self,
layouter: &mut impl Layouter<F>,
state: &mut Self::State,
) -> Result<Output, Error>;
}
#[cfg(test)]
pub(crate) mod tests {
use std::{fmt::Debug, marker::PhantomData};
use midnight_proofs::{
circuit::{SimpleFloorPlanner, Value},
dev::MockProver,
plonk::{Circuit, ConstraintSystem},
};
use rand::{Rng, SeedableRng};
use rand_chacha::ChaCha12Rng;
use super::*;
use crate::{
instructions::{AssertionInstructions, AssignmentInstructions},
testing_utils::{FromScratch, Sampleable},
utils::circuit_modeling::{circuit_to_json, cost_measure_end, cost_measure_start},
};
#[derive(Clone, Debug, Default)]
struct TestCircuit<F, Input, Output, SpongeChip, AssignChip>
where
Input: InnerValue,
Output: InnerValue,
{
inputs: Vec<Vec<Input::Element>>,
sequence: Vec<(usize, usize)>,
_marker: PhantomData<(F, Output, SpongeChip, AssignChip)>,
}
impl<F, Input, Output, SpongeChip, AssignChip> Circuit<F>
for TestCircuit<F, Input, Output, SpongeChip, AssignChip>
where
F: CircuitField,
Input: InnerValue,
Output: InnerValue,
SpongeChip: SpongeInstructions<F, Input, Output> + FromScratch<F>,
AssignChip:
AssignmentInstructions<F, Input> + AssertionInstructions<F, Output> + FromScratch<F>,
{
type Config = (
<SpongeChip as FromScratch<F>>::Config,
<AssignChip 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();
let instance_columns = [committed_instance_column, instance_column];
let mut advice_columns = vec![];
let mut fixed_columns = vec![];
(
SpongeChip::configure_from_scratch(
meta,
&mut advice_columns,
&mut fixed_columns,
&instance_columns,
),
AssignChip::configure_from_scratch(
meta,
&mut advice_columns,
&mut fixed_columns,
&instance_columns,
),
)
}
fn synthesize(
&self,
config: Self::Config,
mut layouter: impl Layouter<F>,
) -> Result<(), Error> {
let chip = SpongeChip::new_from_scratch(&config.0);
let assign_chip = AssignChip::new_from_scratch(&config.1);
let mut input_idx = 0;
let mut state = chip.init(&mut layouter, None)?;
let mut cpu_state =
<SpongeChip as SpongeCPU<Input::Element, Output::Element>>::init(None);
cost_measure_start(&mut layouter);
for step in self.sequence.iter() {
for _nr_absorb in 0..step.0 {
let input_vec = self.inputs[input_idx]
.iter()
.map(|input| Value::known(input.clone()))
.collect::<Vec<_>>();
let inputs = assign_chip.assign_many(&mut layouter, &input_vec)?;
chip.absorb(&mut layouter, &mut state, &inputs)?;
<SpongeChip as SpongeCPU<Input::Element, Output::Element>>::absorb(
&mut cpu_state,
&self.inputs[input_idx],
);
input_idx += 1;
}
for _nr_squeeze in 0..step.1 {
let out = chip.squeeze(&mut layouter, &mut state)?;
let expected_out =
<SpongeChip as SpongeCPU<Input::Element, Output::Element>>::squeeze(
&mut cpu_state,
);
assign_chip.assert_equal_to_fixed(&mut layouter, &out, expected_out)?;
}
}
cost_measure_end(&mut layouter);
chip.load_from_scratch(&mut layouter)?;
assign_chip.load_from_scratch(&mut layouter)
}
}
pub fn test_sponge<F, Input, Output, SpongeChip, AssignChip>(cost_model: bool, chip_name: &str)
where
F: CircuitField + ff::FromUniformBytes<64> + Ord,
Input: InnerValue + Sampleable,
Output: InnerValue,
SpongeChip: SpongeInstructions<F, Input, Output> + FromScratch<F>,
AssignChip:
AssignmentInstructions<F, Input> + AssertionInstructions<F, Output> + FromScratch<F>,
{
let mut rng = ChaCha12Rng::seed_from_u64(0xf007ba11);
let sequence = [(1, 1), (0, 1), (3, 3), (7, 2)];
let nb_absorb_calls = sequence.iter().map(|s| s.0).sum();
let inputs = (0..nb_absorb_calls).map(|_| {
let random_size: usize = rng.gen_range(1..10);
(0..random_size).map(|_| Input::sample_inner(&mut rng)).collect::<Vec<_>>()
});
let circuit = TestCircuit::<F, Input, Output, SpongeChip, AssignChip> {
inputs: inputs.collect(),
sequence: sequence.to_vec(),
_marker: PhantomData,
};
MockProver::run(&circuit, vec![vec![], vec![]]).unwrap().assert_satisfied();
if cost_model {
circuit_to_json(chip_name, "sponge", circuit);
}
}
}