2022-05-11 16:09:12 +02:00
|
|
|
use anyhow::{ensure, Result};
|
2022-05-06 14:55:54 +02:00
|
|
|
use plonky2::field::extension_field::{Extendable, FieldExtension};
|
2022-05-05 22:21:09 +02:00
|
|
|
use plonky2::field::field_types::Field;
|
|
|
|
|
use plonky2::field::packed_field::PackedField;
|
|
|
|
|
use plonky2::field::polynomial::PolynomialValues;
|
|
|
|
|
use plonky2::hash::hash_types::RichField;
|
|
|
|
|
use plonky2::iop::challenger::Challenger;
|
|
|
|
|
use plonky2::plonk::config::GenericConfig;
|
|
|
|
|
use plonky2::plonk::plonk_common::reduce_with_powers;
|
2022-05-06 14:55:54 +02:00
|
|
|
use plonky2::util::reducing::ReducingFactor;
|
2022-05-05 22:21:09 +02:00
|
|
|
|
2022-05-06 17:35:25 +02:00
|
|
|
use crate::all_stark::Table;
|
2022-05-05 22:21:09 +02:00
|
|
|
use crate::config::StarkConfig;
|
2022-05-06 14:55:54 +02:00
|
|
|
use crate::constraint_consumer::ConstraintConsumer;
|
2022-05-12 13:47:55 +02:00
|
|
|
use crate::permutation::{
|
2022-05-12 13:51:02 +02:00
|
|
|
get_grand_product_challenge_set, GrandProductChallenge, GrandProductChallengeSet,
|
2022-05-12 13:47:55 +02:00
|
|
|
};
|
2022-05-11 16:09:12 +02:00
|
|
|
use crate::proof::StarkProofWithPublicInputs;
|
2022-05-06 14:55:54 +02:00
|
|
|
use crate::stark::Stark;
|
|
|
|
|
use crate::vars::StarkEvaluationVars;
|
2022-05-05 22:21:09 +02:00
|
|
|
|
2022-05-12 13:47:55 +02:00
|
|
|
#[derive(Clone)]
|
2022-05-06 17:22:30 +02:00
|
|
|
pub struct CrossTableLookup {
|
|
|
|
|
pub looking_table: Table,
|
|
|
|
|
pub looking_columns: Vec<usize>,
|
2022-05-10 07:45:42 +02:00
|
|
|
pub looked_table: Table,
|
2022-05-06 17:22:30 +02:00
|
|
|
pub looked_columns: Vec<usize>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl CrossTableLookup {
|
|
|
|
|
pub fn new(
|
|
|
|
|
looking_table: Table,
|
|
|
|
|
looking_columns: Vec<usize>,
|
2022-05-10 07:45:42 +02:00
|
|
|
looked_table: Table,
|
2022-05-06 17:22:30 +02:00
|
|
|
looked_columns: Vec<usize>,
|
|
|
|
|
) -> Self {
|
|
|
|
|
assert_eq!(looking_columns.len(), looked_columns.len());
|
|
|
|
|
Self {
|
|
|
|
|
looking_table,
|
|
|
|
|
looking_columns,
|
|
|
|
|
looked_table,
|
|
|
|
|
looked_columns,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-05 22:21:09 +02:00
|
|
|
/// Lookup data for one table.
|
|
|
|
|
#[derive(Clone)]
|
2022-05-13 11:20:29 +02:00
|
|
|
pub struct CtlData<F: Field> {
|
|
|
|
|
// Challenges used in the lookup argument.
|
2022-05-12 13:51:02 +02:00
|
|
|
pub(crate) challenges: GrandProductChallengeSet<F>,
|
2022-05-13 11:20:29 +02:00
|
|
|
// Vector of `(Z, columns)` where `Z` is a Z-polynomial for a lookup on columns `columns`.
|
2022-05-11 14:35:33 +02:00
|
|
|
pub zs_columns: Vec<(PolynomialValues<F>, Vec<usize>)>,
|
2022-05-05 22:21:09 +02:00
|
|
|
}
|
|
|
|
|
|
2022-05-13 11:20:29 +02:00
|
|
|
impl<F: Field> CtlData<F> {
|
2022-05-12 13:51:02 +02:00
|
|
|
pub(crate) fn new(challenges: GrandProductChallengeSet<F>) -> Self {
|
2022-05-05 22:21:09 +02:00
|
|
|
Self {
|
2022-05-12 13:47:55 +02:00
|
|
|
challenges,
|
2022-05-11 14:35:33 +02:00
|
|
|
zs_columns: vec![],
|
2022-05-05 22:21:09 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-10 15:08:08 +02:00
|
|
|
pub fn len(&self) -> usize {
|
2022-05-11 14:35:33 +02:00
|
|
|
self.zs_columns.len()
|
2022-05-10 15:08:08 +02:00
|
|
|
}
|
|
|
|
|
|
2022-05-05 22:21:09 +02:00
|
|
|
pub fn is_empty(&self) -> bool {
|
2022-05-11 14:35:33 +02:00
|
|
|
self.zs_columns.is_empty()
|
2022-05-05 22:21:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn z_polys(&self) -> Vec<PolynomialValues<F>> {
|
2022-05-11 14:35:33 +02:00
|
|
|
self.zs_columns.iter().map(|(p, _)| p.clone()).collect()
|
2022-05-05 22:21:09 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-13 11:20:29 +02:00
|
|
|
pub fn cross_table_lookup_data<F: RichField, C: GenericConfig<D, F = F>, const D: usize>(
|
2022-05-05 22:21:09 +02:00
|
|
|
config: &StarkConfig,
|
|
|
|
|
trace_poly_values: &[Vec<PolynomialValues<F>>],
|
2022-05-06 17:22:30 +02:00
|
|
|
cross_table_lookups: &[CrossTableLookup],
|
2022-05-05 22:21:09 +02:00
|
|
|
challenger: &mut Challenger<F, C::Hasher>,
|
2022-05-13 11:20:29 +02:00
|
|
|
) -> Vec<CtlData<F>> {
|
2022-05-12 13:51:02 +02:00
|
|
|
let challenges = get_grand_product_challenge_set(challenger, config.num_challenges);
|
2022-05-05 22:21:09 +02:00
|
|
|
cross_table_lookups.iter().fold(
|
2022-05-13 11:20:29 +02:00
|
|
|
vec![CtlData::new(challenges.clone()); trace_poly_values.len()],
|
2022-05-05 22:21:09 +02:00
|
|
|
|mut acc, cross_table_lookup| {
|
|
|
|
|
let CrossTableLookup {
|
|
|
|
|
looking_table,
|
|
|
|
|
looking_columns,
|
|
|
|
|
looked_table,
|
|
|
|
|
looked_columns,
|
|
|
|
|
} = cross_table_lookup;
|
|
|
|
|
|
2022-05-12 13:51:02 +02:00
|
|
|
for &GrandProductChallenge { beta, gamma } in &challenges.challenges {
|
2022-05-05 22:21:09 +02:00
|
|
|
let z_looking = partial_products(
|
|
|
|
|
&trace_poly_values[*looking_table as usize],
|
2022-05-06 16:59:25 +02:00
|
|
|
looking_columns,
|
2022-05-05 22:21:09 +02:00
|
|
|
beta,
|
|
|
|
|
gamma,
|
|
|
|
|
);
|
|
|
|
|
let z_looked = partial_products(
|
|
|
|
|
&trace_poly_values[*looked_table as usize],
|
2022-05-06 16:59:25 +02:00
|
|
|
looked_columns,
|
2022-05-05 22:21:09 +02:00
|
|
|
beta,
|
|
|
|
|
gamma,
|
|
|
|
|
);
|
|
|
|
|
|
2022-05-11 14:35:33 +02:00
|
|
|
acc[*looking_table as usize]
|
|
|
|
|
.zs_columns
|
|
|
|
|
.push((z_looking, looking_columns.clone()));
|
|
|
|
|
acc[*looked_table as usize]
|
|
|
|
|
.zs_columns
|
|
|
|
|
.push((z_looked, looked_columns.clone()));
|
2022-05-05 22:21:09 +02:00
|
|
|
}
|
|
|
|
|
acc
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn partial_products<F: Field>(
|
|
|
|
|
trace: &[PolynomialValues<F>],
|
|
|
|
|
columns: &[usize],
|
|
|
|
|
beta: F,
|
|
|
|
|
gamma: F,
|
|
|
|
|
) -> PolynomialValues<F> {
|
|
|
|
|
let mut partial_prod = F::ONE;
|
2022-05-12 20:38:11 +02:00
|
|
|
let degree = trace[0].len();
|
|
|
|
|
let mut res = Vec::with_capacity(degree);
|
|
|
|
|
for i in 0..degree {
|
2022-05-05 22:21:09 +02:00
|
|
|
partial_prod *=
|
2022-05-06 16:59:25 +02:00
|
|
|
gamma + reduce_with_powers(columns.iter().map(|&j| &trace[j].values[i]), beta);
|
2022-05-05 22:21:09 +02:00
|
|
|
res.push(partial_prod);
|
|
|
|
|
}
|
|
|
|
|
res.into()
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-12 13:47:55 +02:00
|
|
|
#[derive(Clone)]
|
|
|
|
|
pub struct CTLCheckVars<'a, F, FE, P, const D2: usize>
|
2022-05-05 22:21:09 +02:00
|
|
|
where
|
|
|
|
|
F: Field,
|
|
|
|
|
FE: FieldExtension<D2, BaseField = F>,
|
|
|
|
|
P: PackedField<Scalar = FE>,
|
|
|
|
|
{
|
2022-05-06 14:55:54 +02:00
|
|
|
pub(crate) local_z: P,
|
|
|
|
|
pub(crate) next_z: P,
|
2022-05-12 13:51:02 +02:00
|
|
|
pub(crate) challenges: GrandProductChallenge<F>,
|
2022-05-12 13:47:55 +02:00
|
|
|
pub(crate) columns: &'a [usize],
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<'a, F: RichField + Extendable<D>, const D: usize>
|
|
|
|
|
CTLCheckVars<'a, F, F::Extension, F::Extension, D>
|
|
|
|
|
{
|
|
|
|
|
pub(crate) fn from_proofs<C: GenericConfig<D, F = F>>(
|
|
|
|
|
proofs: &[&StarkProofWithPublicInputs<F, C, D>],
|
|
|
|
|
cross_table_lookups: &'a [CrossTableLookup],
|
2022-05-12 13:51:02 +02:00
|
|
|
ctl_challenges: &'a GrandProductChallengeSet<F>,
|
2022-05-13 10:48:56 +02:00
|
|
|
num_permutation_zs: &[usize],
|
2022-05-12 13:47:55 +02:00
|
|
|
) -> Vec<Vec<Self>> {
|
2022-05-13 10:48:56 +02:00
|
|
|
debug_assert_eq!(proofs.len(), num_permutation_zs.len());
|
2022-05-12 13:47:55 +02:00
|
|
|
let mut ctl_zs = proofs
|
|
|
|
|
.iter()
|
2022-05-13 10:48:56 +02:00
|
|
|
.zip(num_permutation_zs)
|
|
|
|
|
.map(|(p, &num_permutation)| -> Box<dyn Iterator<Item = _>> {
|
2022-05-13 11:20:29 +02:00
|
|
|
if p.proof.openings.permutation_ctl_zs.is_some() {
|
2022-05-13 10:35:59 +02:00
|
|
|
Box::new(
|
2022-05-12 13:47:55 +02:00
|
|
|
p.proof
|
|
|
|
|
.openings
|
2022-05-13 11:20:29 +02:00
|
|
|
.permutation_ctl_zs
|
2022-05-12 13:47:55 +02:00
|
|
|
.as_ref()
|
|
|
|
|
.unwrap()
|
2022-05-12 22:29:10 +02:00
|
|
|
.iter()
|
2022-05-13 10:48:56 +02:00
|
|
|
.skip(num_permutation)
|
2022-05-13 10:35:59 +02:00
|
|
|
.zip(
|
|
|
|
|
p.proof
|
|
|
|
|
.openings
|
2022-05-13 11:20:29 +02:00
|
|
|
.permutation_ctl_zs_right
|
2022-05-13 10:35:59 +02:00
|
|
|
.as_ref()
|
|
|
|
|
.unwrap()
|
|
|
|
|
.iter()
|
2022-05-13 10:48:56 +02:00
|
|
|
.skip(num_permutation),
|
2022-05-13 10:35:59 +02:00
|
|
|
),
|
2022-05-12 13:47:55 +02:00
|
|
|
)
|
2022-05-13 10:35:59 +02:00
|
|
|
} else {
|
|
|
|
|
Box::new(std::iter::empty())
|
|
|
|
|
}
|
2022-05-12 13:47:55 +02:00
|
|
|
})
|
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
|
|
|
|
|
|
cross_table_lookups
|
|
|
|
|
.iter()
|
|
|
|
|
.fold(vec![vec![]; proofs.len()], |mut acc, ctl| {
|
|
|
|
|
let CrossTableLookup {
|
|
|
|
|
looking_table,
|
|
|
|
|
looking_columns,
|
|
|
|
|
looked_table,
|
|
|
|
|
looked_columns,
|
|
|
|
|
} = ctl;
|
|
|
|
|
|
|
|
|
|
for &challenges in &ctl_challenges.challenges {
|
|
|
|
|
let (looking_z, looking_z_next) =
|
|
|
|
|
ctl_zs[*looking_table as usize].next().unwrap();
|
|
|
|
|
acc[*looking_table as usize].push(Self {
|
|
|
|
|
local_z: *looking_z,
|
|
|
|
|
next_z: *looking_z_next,
|
|
|
|
|
challenges,
|
2022-05-12 14:07:03 +02:00
|
|
|
columns: looking_columns,
|
2022-05-12 13:47:55 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
let (looked_z, looked_z_next) = ctl_zs[*looked_table as usize].next().unwrap();
|
|
|
|
|
acc[*looked_table as usize].push(Self {
|
|
|
|
|
local_z: *looked_z,
|
|
|
|
|
next_z: *looked_z_next,
|
|
|
|
|
challenges,
|
2022-05-12 14:07:03 +02:00
|
|
|
columns: looked_columns,
|
2022-05-12 13:47:55 +02:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
acc
|
|
|
|
|
})
|
|
|
|
|
}
|
2022-05-05 22:21:09 +02:00
|
|
|
}
|
|
|
|
|
|
2022-05-06 14:55:54 +02:00
|
|
|
pub(crate) fn eval_cross_table_lookup_checks<F, FE, P, C, S, const D: usize, const D2: usize>(
|
2022-05-13 14:16:28 +02:00
|
|
|
vars: StarkEvaluationVars<FE, P, { S::COLUMNS }, { S::PUBLIC_INPUTS }>,
|
2022-05-13 11:20:29 +02:00
|
|
|
ctl_vars: &[CTLCheckVars<F, FE, P, D2>],
|
2022-05-05 22:21:09 +02:00
|
|
|
consumer: &mut ConstraintConsumer<P>,
|
|
|
|
|
) where
|
|
|
|
|
F: RichField + Extendable<D>,
|
|
|
|
|
FE: FieldExtension<D2, BaseField = F>,
|
|
|
|
|
P: PackedField<Scalar = FE>,
|
|
|
|
|
C: GenericConfig<D, F = F>,
|
|
|
|
|
S: Stark<F, D>,
|
|
|
|
|
{
|
2022-05-13 11:20:29 +02:00
|
|
|
for lookup_vars in ctl_vars {
|
2022-05-06 14:55:54 +02:00
|
|
|
let CTLCheckVars {
|
|
|
|
|
local_z,
|
|
|
|
|
next_z,
|
|
|
|
|
challenges,
|
|
|
|
|
columns,
|
2022-05-13 11:20:29 +02:00
|
|
|
} = lookup_vars;
|
2022-05-06 14:55:54 +02:00
|
|
|
let mut factor = ReducingFactor::new(challenges.beta);
|
|
|
|
|
let mut combine = |v: &[P]| -> P {
|
|
|
|
|
factor.reduce_ext(columns.iter().map(|&i| v[i])) + FE::from_basefield(challenges.gamma)
|
|
|
|
|
};
|
2022-05-05 22:21:09 +02:00
|
|
|
|
2022-05-06 14:55:54 +02:00
|
|
|
// Check value of `Z(1)`
|
|
|
|
|
consumer.constraint_first_row(*local_z - combine(vars.local_values));
|
|
|
|
|
// Check `Z(gw) = combination * Z(w)`
|
|
|
|
|
consumer.constraint_transition(*next_z - *local_z * combine(vars.next_values));
|
2022-05-05 22:21:09 +02:00
|
|
|
}
|
|
|
|
|
}
|
2022-05-11 16:09:12 +02:00
|
|
|
|
|
|
|
|
pub(crate) fn verify_cross_table_lookups<
|
|
|
|
|
F: RichField + Extendable<D>,
|
|
|
|
|
C: GenericConfig<D, F = F>,
|
|
|
|
|
const D: usize,
|
|
|
|
|
>(
|
|
|
|
|
cross_table_lookups: Vec<CrossTableLookup>,
|
|
|
|
|
proofs: &[&StarkProofWithPublicInputs<F, C, D>],
|
2022-05-12 13:51:02 +02:00
|
|
|
challenges: GrandProductChallengeSet<F>,
|
2022-05-11 16:09:12 +02:00
|
|
|
config: &StarkConfig,
|
|
|
|
|
) -> Result<()> {
|
|
|
|
|
let degrees_bits = proofs
|
|
|
|
|
.iter()
|
|
|
|
|
.map(|p| p.proof.recover_degree_bits(config))
|
|
|
|
|
.collect::<Vec<_>>();
|
2022-05-13 11:20:29 +02:00
|
|
|
let mut ctl_zs_openings = proofs
|
2022-05-11 16:09:12 +02:00
|
|
|
.iter()
|
2022-05-13 11:20:29 +02:00
|
|
|
.map(|p| p.proof.openings.ctl_zs_last.iter())
|
2022-05-11 16:09:12 +02:00
|
|
|
.collect::<Vec<_>>();
|
2022-05-12 13:47:55 +02:00
|
|
|
for (
|
|
|
|
|
i,
|
|
|
|
|
CrossTableLookup {
|
|
|
|
|
looking_table,
|
|
|
|
|
looked_table,
|
|
|
|
|
..
|
|
|
|
|
},
|
|
|
|
|
) in cross_table_lookups.into_iter().enumerate()
|
2022-05-11 16:09:12 +02:00
|
|
|
{
|
|
|
|
|
let looking_degree = 1 << degrees_bits[looking_table as usize];
|
|
|
|
|
let looked_degree = 1 << degrees_bits[looked_table as usize];
|
2022-05-13 11:20:29 +02:00
|
|
|
let looking_z = *ctl_zs_openings[looking_table as usize].next().unwrap();
|
|
|
|
|
let looked_z = *ctl_zs_openings[looked_table as usize].next().unwrap();
|
2022-05-11 16:09:12 +02:00
|
|
|
ensure!(
|
2022-05-12 13:47:55 +02:00
|
|
|
looking_z
|
|
|
|
|
== looked_z
|
|
|
|
|
* challenges.challenges[i % config.num_challenges]
|
|
|
|
|
.gamma
|
|
|
|
|
.exp_u64(looking_degree - looked_degree),
|
2022-05-11 16:09:12 +02:00
|
|
|
"Cross-table lookup verification failed."
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|