plonky2/evm/src/stark.rs

230 lines
8.3 KiB
Rust
Raw Normal View History

2022-05-13 10:35:59 +02:00
use std::iter::once;
2022-05-04 20:57:07 +02:00
use plonky2::field::extension_field::{Extendable, FieldExtension};
2022-05-10 15:08:08 +02:00
use plonky2::field::field_types::Field;
2022-05-04 20:57:07 +02:00
use plonky2::field::packed_field::PackedField;
use plonky2::fri::structure::{
FriBatchInfo, FriBatchInfoTarget, FriInstanceInfo, FriInstanceInfoTarget, FriOracleInfo,
FriPolynomialInfo,
};
use plonky2::hash::hash_types::RichField;
use plonky2::iop::ext_target::ExtensionTarget;
use plonky2::plonk::circuit_builder::CircuitBuilder;
use plonky2_util::ceil_div_usize;
use crate::config::StarkConfig;
use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer};
use crate::permutation::PermutationPair;
use crate::vars::StarkEvaluationTargets;
use crate::vars::StarkEvaluationVars;
/// Represents a STARK system.
pub trait Stark<F: RichField + Extendable<D>, const D: usize>: Sync {
/// The total number of columns in the trace.
const COLUMNS: usize;
/// The number of public inputs.
const PUBLIC_INPUTS: usize;
/// Evaluate constraints at a vector of points.
///
/// The points are elements of a field `FE`, a degree `D2` extension of `F`. This lets us
/// evaluate constraints over a larger domain if desired. This can also be called with `FE = F`
/// and `D2 = 1`, in which case we are using the trivial extension, i.e. just evaluating
/// constraints over `F`.
fn eval_packed_generic<FE, P, const D2: usize>(
&self,
2022-05-13 14:16:28 +02:00
vars: StarkEvaluationVars<FE, P, { Self::COLUMNS }, { Self::PUBLIC_INPUTS }>,
2022-05-04 20:57:07 +02:00
yield_constr: &mut ConstraintConsumer<P>,
) where
FE: FieldExtension<D2, BaseField = F>,
P: PackedField<Scalar = FE>;
/// Evaluate constraints at a vector of points from the base field `F`.
fn eval_packed_base<P: PackedField<Scalar = F>>(
&self,
2022-05-13 14:16:28 +02:00
vars: StarkEvaluationVars<F, P, { Self::COLUMNS }, { Self::PUBLIC_INPUTS }>,
2022-05-04 20:57:07 +02:00
yield_constr: &mut ConstraintConsumer<P>,
) {
self.eval_packed_generic(vars, yield_constr)
}
/// Evaluate constraints at a single point from the degree `D` extension field.
fn eval_ext(
&self,
2022-05-13 14:16:28 +02:00
vars: StarkEvaluationVars<
F::Extension,
F::Extension,
{ Self::COLUMNS },
{ Self::PUBLIC_INPUTS },
>,
2022-05-04 20:57:07 +02:00
yield_constr: &mut ConstraintConsumer<F::Extension>,
) {
self.eval_packed_generic(vars, yield_constr)
}
/// Evaluate constraints at a vector of points from the degree `D` extension field. This is like
/// `eval_ext`, except in the context of a recursive circuit.
/// Note: constraints must be added through`yeld_constr.constraint(builder, constraint)` in the
/// same order as they are given in `eval_packed_generic`.
2022-05-18 09:22:58 +02:00
fn eval_ext_circuit(
2022-05-04 20:57:07 +02:00
&self,
builder: &mut CircuitBuilder<F, D>,
vars: StarkEvaluationTargets<D, { Self::COLUMNS }, { Self::PUBLIC_INPUTS }>,
yield_constr: &mut RecursiveConstraintConsumer<F, D>,
);
/// The maximum constraint degree.
fn constraint_degree(&self) -> usize;
/// The maximum constraint degree.
fn quotient_degree_factor(&self) -> usize {
1.max(self.constraint_degree() - 1)
}
/// Computes the FRI instance used to prove this Stark.
fn fri_instance(
&self,
zeta: F::Extension,
g: F,
2022-05-10 15:08:08 +02:00
degree_bits: usize,
num_ctl_zs: usize,
2022-05-04 20:57:07 +02:00
config: &StarkConfig,
) -> FriInstanceInfo<F, D> {
let no_blinding_oracle = FriOracleInfo { blinding: false };
let mut oracle_indices = 0..;
let trace_info =
FriPolynomialInfo::from_range(oracle_indices.next().unwrap(), 0..Self::COLUMNS);
2022-05-10 15:08:08 +02:00
let num_permutation_batches = self.num_permutation_batches(config);
2022-05-13 11:20:29 +02:00
let permutation_ctl_zs_info = (num_permutation_batches + num_ctl_zs > 0).then(|| {
let permutation_ctl_index = oracle_indices.next().unwrap();
2022-05-13 10:35:59 +02:00
FriPolynomialInfo::from_range(
2022-05-13 11:20:29 +02:00
permutation_ctl_index,
2022-05-13 10:35:59 +02:00
0..num_permutation_batches + num_ctl_zs,
)
});
2022-05-13 11:20:29 +02:00
let ctl_zs_info = (num_ctl_zs > 0).then(|| {
let index = permutation_ctl_zs_info
2022-05-13 10:35:59 +02:00
.as_ref()
.map(|info| info[0].oracle_index)
.unwrap_or_else(|| oracle_indices.next().unwrap());
FriPolynomialInfo::from_range(
index,
num_permutation_batches..num_permutation_batches + num_ctl_zs,
)
});
2022-05-04 20:57:07 +02:00
let quotient_info = FriPolynomialInfo::from_range(
oracle_indices.next().unwrap(),
0..self.quotient_degree_factor() * config.num_challenges,
);
let zeta_batch = FriBatchInfo {
point: zeta,
2022-05-13 10:35:59 +02:00
polynomials: once(trace_info.clone())
2022-05-13 11:20:29 +02:00
.chain(permutation_ctl_zs_info.clone())
2022-05-13 10:35:59 +02:00
.chain(once(quotient_info))
.collect::<Vec<_>>()
.concat(),
2022-05-04 20:57:07 +02:00
};
let zeta_right_batch = FriBatchInfo {
point: zeta.scalar_mul(g),
2022-05-13 11:20:29 +02:00
polynomials: once(trace_info)
.chain(permutation_ctl_zs_info)
2022-05-13 10:35:59 +02:00
.collect::<Vec<_>>()
.concat(),
2022-05-10 15:08:08 +02:00
};
2022-05-13 11:20:29 +02:00
let ctl_last_batch = ctl_zs_info.map(|info| FriBatchInfo {
2022-05-10 15:08:08 +02:00
point: F::Extension::primitive_root_of_unity(degree_bits).inverse(),
2022-05-13 10:35:59 +02:00
polynomials: info,
});
2022-05-04 20:57:07 +02:00
FriInstanceInfo {
oracles: vec![no_blinding_oracle; oracle_indices.next().unwrap()],
2022-05-13 10:35:59 +02:00
batches: once(zeta_batch)
.chain(once(zeta_right_batch))
2022-05-13 11:20:29 +02:00
.chain(ctl_last_batch)
2022-05-13 10:35:59 +02:00
.collect::<Vec<_>>(),
2022-05-04 20:57:07 +02:00
}
}
/// Computes the FRI instance used to prove this Stark.
fn fri_instance_target(
&self,
builder: &mut CircuitBuilder<F, D>,
zeta: ExtensionTarget<D>,
g: F,
config: &StarkConfig,
) -> FriInstanceInfoTarget<D> {
let no_blinding_oracle = FriOracleInfo { blinding: false };
let mut oracle_indices = 0..;
let trace_info =
FriPolynomialInfo::from_range(oracle_indices.next().unwrap(), 0..Self::COLUMNS);
let permutation_zs_info = if self.uses_permutation_args() {
FriPolynomialInfo::from_range(
oracle_indices.next().unwrap(),
0..self.num_permutation_batches(config),
)
} else {
vec![]
};
let quotient_info = FriPolynomialInfo::from_range(
oracle_indices.next().unwrap(),
0..self.quotient_degree_factor() * config.num_challenges,
);
let zeta_batch = FriBatchInfoTarget {
point: zeta,
polynomials: [
trace_info.clone(),
permutation_zs_info.clone(),
quotient_info,
]
.concat(),
};
let zeta_right = builder.mul_const_extension(g, zeta);
let zeta_right_batch = FriBatchInfoTarget {
point: zeta_right,
polynomials: [trace_info, permutation_zs_info].concat(),
};
FriInstanceInfoTarget {
oracles: vec![no_blinding_oracle; oracle_indices.next().unwrap()],
batches: vec![zeta_batch, zeta_right_batch],
}
}
/// Pairs of lists of columns that should be permutations of one another. A permutation argument
/// will be used for each such pair. Empty by default.
fn permutation_pairs(&self) -> Vec<PermutationPair> {
vec![]
}
fn uses_permutation_args(&self) -> bool {
!self.permutation_pairs().is_empty()
}
/// The number of permutation argument instances that can be combined into a single constraint.
fn permutation_batch_size(&self) -> usize {
// The permutation argument constraints look like
// Z(x) \prod(...) = Z(g x) \prod(...)
// where each product has a number of terms equal to the batch size. So our batch size
// should be one less than our constraint degree, which happens to be our quotient degree.
self.quotient_degree_factor()
}
fn num_permutation_instances(&self, config: &StarkConfig) -> usize {
self.permutation_pairs().len() * config.num_challenges
}
fn num_permutation_batches(&self, config: &StarkConfig) -> usize {
ceil_div_usize(
self.num_permutation_instances(config),
self.permutation_batch_size(),
)
}
}