use ff::PrimeField;
use bellman::{Circuit, ConstraintSystem, SynthesisError};
pub const MIMC_ROUNDS: usize = 322;
pub fn mimc<S: PrimeField>(mut xl: S, mut xr: S, constants: &[S]) -> S {
assert_eq!(constants.len(), MIMC_ROUNDS);
for c in constants {
let mut tmp1 = xl;
tmp1.add_assign(c);
let mut tmp2 = tmp1.square();
tmp2.mul_assign(&tmp1);
tmp2.add_assign(&xr);
xr = xl;
xl = tmp2;
}
xl
}
#[allow(clippy::upper_case_acronyms)]
pub struct MiMCDemo<'a, S: PrimeField> {
pub xl: Option<S>,
pub xr: Option<S>,
pub constants: &'a [S],
}
impl<'a, S: PrimeField> Circuit<S> for MiMCDemo<'a, S> {
fn synthesize<CS: ConstraintSystem<S>>(self, cs: &mut CS) -> Result<(), SynthesisError> {
assert_eq!(self.constants.len(), MIMC_ROUNDS);
let mut xl_value = self.xl;
let mut xl = cs.alloc(
|| "preimage xl",
|| xl_value.ok_or(SynthesisError::AssignmentMissing),
)?;
let mut xr_value = self.xr;
let mut xr = cs.alloc(
|| "preimage xr",
|| xr_value.ok_or(SynthesisError::AssignmentMissing),
)?;
for i in 0..MIMC_ROUNDS {
let cs = &mut cs.namespace(|| format!("round {}", i));
let tmp_value = xl_value.map(|mut e| {
e.add_assign(&self.constants[i]);
e.square()
});
let tmp = cs.alloc(
|| "tmp",
|| tmp_value.ok_or(SynthesisError::AssignmentMissing),
)?;
cs.enforce(
|| "tmp = (xL + Ci)^2",
|lc| lc + xl + (self.constants[i], CS::one()),
|lc| lc + xl + (self.constants[i], CS::one()),
|lc| lc + tmp,
);
let new_xl_value = xl_value.map(|mut e| {
e.add_assign(&self.constants[i]);
e.mul_assign(&tmp_value.unwrap());
e.add_assign(&xr_value.unwrap());
e
});
let new_xl = if i == (MIMC_ROUNDS - 1) {
cs.alloc_input(
|| "image",
|| new_xl_value.ok_or(SynthesisError::AssignmentMissing),
)?
} else {
cs.alloc(
|| "new_xl",
|| new_xl_value.ok_or(SynthesisError::AssignmentMissing),
)?
};
cs.enforce(
|| "new_xL = xR + (xL + Ci)^3",
|lc| lc + tmp,
|lc| lc + xl + (self.constants[i], CS::one()),
|lc| lc + new_xl - xr,
);
xr = xl;
xr_value = xl_value;
xl = new_xl;
xl_value = new_xl_value;
}
Ok(())
}
}