use ff::{Field, PrimeField};
use midnight_proofs::{
circuit::Layouter,
plonk::{Any, Column, ColumnType, ConstraintSystem, Error},
poly::Rotation,
};
use crate::{
field::AssignedNative,
instructions::ArithInstructions,
verifier::{
permutation::{CommonEvaluated, Evaluated},
SelfEmulation,
},
};
#[allow(clippy::too_many_arguments)]
pub(crate) fn permutation_expressions<S: SelfEmulation>(
layouter: &mut impl Layouter<S::F>,
scalar_chip: &S::ScalarChip,
cs: &ConstraintSystem<S::F>,
permutation_evals: &Evaluated<S>,
permutations_common: &CommonEvaluated<S>,
advice_evals: &[AssignedNative<S::F>],
fixed_evals: &[AssignedNative<S::F>],
instance_evals: &[AssignedNative<S::F>],
l_0: &AssignedNative<S::F>,
l_last: &AssignedNative<S::F>,
l_blind: &AssignedNative<S::F>,
beta: &AssignedNative<S::F>,
gamma: &AssignedNative<S::F>,
x: &AssignedNative<S::F>,
) -> Result<Vec<AssignedNative<S::F>>, Error> {
let chunk_len = cs.degree() - 2;
let id_1 = {
let first_set = permutation_evals.sets.first().unwrap();
let z_0 = &first_set.permutation_product_eval;
scalar_chip.add_and_mul(
layouter,
(S::F::ONE, l_0),
(S::F::ZERO, z_0),
(S::F::ZERO, l_0),
S::F::ZERO,
-S::F::ONE,
)?
};
let id_2 = {
let last_set = permutation_evals.sets.last().unwrap();
let z_l = &last_set.permutation_product_eval;
let aux = scalar_chip.add_and_mul(
layouter,
(-S::F::ONE, z_l),
(S::F::ZERO, z_l),
(S::F::ZERO, z_l),
S::F::ZERO,
S::F::ONE,
)?;
scalar_chip.mul(layouter, l_last, &aux, None)?
};
let ids_3 = permutation_evals
.sets
.iter()
.skip(1)
.zip(permutation_evals.sets.iter())
.map(|(set, prev_set)| {
let z_i = &set.permutation_product_eval;
let z_i_prev = &prev_set.permutation_product_last_eval.clone().unwrap();
let aux = scalar_chip.sub(layouter, z_i, z_i_prev)?;
scalar_chip.mul(layouter, l_0, &aux, None)
})
.collect::<Result<Vec<AssignedNative<S::F>>, Error>>()?;
let ids_4 = permutation_evals
.sets
.iter()
.zip(cs.permutation().get_columns().chunks(chunk_len))
.zip(permutations_common.permutation_evals.chunks(chunk_len))
.enumerate()
.map(move |(chunk_index, ((set, columns), permutation_evals))| {
let mut left = set.permutation_product_next_eval.clone();
for (eval, permutation_eval) in columns
.iter()
.map(|&column| match column.column_type() {
Any::Advice(_) => {
advice_evals[get_query_index(column, cs.advice_queries())].clone()
}
Any::Fixed => fixed_evals[get_query_index(column, cs.fixed_queries())].clone(),
Any::Instance => {
instance_evals[get_query_index(column, cs.instance_queries())].clone()
}
})
.zip(permutation_evals.iter())
{
let aux = scalar_chip.mul(layouter, beta, permutation_eval, None)?;
let aux = scalar_chip.linear_combination(
layouter,
&[
(S::F::ONE, aux),
(S::F::ONE, gamma.clone()),
(S::F::ONE, eval),
],
S::F::ZERO,
)?;
left = scalar_chip.mul(layouter, &left, &aux, None)?;
}
let mut right = set.permutation_product_eval.clone();
let mut current_delta = {
let delta_power = S::F::DELTA.pow_vartime([(chunk_index * chunk_len) as u64]);
scalar_chip.mul(layouter, beta, x, Some(delta_power))?
};
for eval in columns.iter().map(|&column| match column.column_type() {
Any::Advice(_) => {
advice_evals[get_query_index(column, cs.advice_queries())].clone()
}
Any::Fixed => fixed_evals[get_query_index(column, cs.fixed_queries())].clone(),
Any::Instance => {
instance_evals[get_query_index(column, cs.instance_queries())].clone()
}
}) {
let aux = scalar_chip.linear_combination(
layouter,
&[
(S::F::ONE, eval),
(S::F::ONE, current_delta.clone()),
(S::F::ONE, gamma.clone()),
],
S::F::ZERO,
)?;
right = scalar_chip.mul(layouter, &right, &aux, None)?;
current_delta =
scalar_chip.mul_by_constant(layouter, ¤t_delta.clone(), S::F::DELTA)?;
}
let aux1 = scalar_chip.sub(layouter, &left, &right)?;
let aux2 = scalar_chip.linear_combination(
layouter,
&[(-S::F::ONE, l_last.clone()), (-S::F::ONE, l_blind.clone())],
S::F::ONE,
)?;
scalar_chip.mul(layouter, &aux1, &aux2, None)
})
.collect::<Result<Vec<AssignedNative<S::F>>, Error>>()?;
Ok([vec![id_1, id_2], ids_3, ids_4].concat())
}
fn get_query_index<C: ColumnType>(column: Column<Any>, queries: &[(Column<C>, Rotation)]) -> usize
where
Column<C>: TryFrom<Column<Any>>,
<Column<C> as TryFrom<Column<Any>>>::Error: std::fmt::Debug,
{
for (index, query) in queries.iter().enumerate() {
if query == &(Column::<C>::try_from(column).unwrap(), Rotation::cur()) {
return index;
}
}
panic!("Query index not found");
}