use crate::ProofOptions;
use crypto::{Hasher, RandomCoin, RandomCoinError};
use math::{fft, ExtensibleField, ExtensionOf, FieldElement, StarkField};
use utils::{
collections::{BTreeMap, Vec},
Serializable,
};
mod trace_info;
pub use trace_info::{TraceInfo, TraceLayout};
mod context;
pub use context::AirContext;
mod assertions;
pub use assertions::Assertion;
mod boundary;
pub use boundary::{BoundaryConstraint, BoundaryConstraintGroup, BoundaryConstraints};
mod transition;
pub use transition::{
EvaluationFrame, TransitionConstraintDegree, TransitionConstraintGroup, TransitionConstraints,
};
mod coefficients;
pub use coefficients::{
AuxTraceRandElements, ConstraintCompositionCoefficients, DeepCompositionCoefficients,
};
mod divisor;
pub use divisor::ConstraintDivisor;
#[cfg(test)]
mod tests;
const MIN_CYCLE_LENGTH: usize = 2;
pub trait Air: Send + Sync {
type BaseField: StarkField + ExtensibleField<2> + ExtensibleField<3>;
type PublicInputs: Serializable;
fn new(trace_info: TraceInfo, pub_inputs: Self::PublicInputs, options: ProofOptions) -> Self;
fn context(&self) -> &AirContext<Self::BaseField>;
fn evaluate_transition<E: FieldElement<BaseField = Self::BaseField>>(
&self,
frame: &EvaluationFrame<E>,
periodic_values: &[E],
result: &mut [E],
);
fn get_assertions(&self) -> Vec<Assertion<Self::BaseField>>;
#[allow(unused_variables)]
fn evaluate_aux_transition<F, E>(
&self,
main_frame: &EvaluationFrame<F>,
aux_frame: &EvaluationFrame<E>,
periodic_values: &[F],
aux_rand_elements: &AuxTraceRandElements<E>,
result: &mut [E],
) where
F: FieldElement<BaseField = Self::BaseField>,
E: FieldElement<BaseField = Self::BaseField> + ExtensionOf<F>,
{
unimplemented!("evaluation of auxiliary transition constraints has not been implemented");
}
#[allow(unused_variables)]
fn get_aux_assertions<E: FieldElement<BaseField = Self::BaseField>>(
&self,
aux_rand_elements: &AuxTraceRandElements<E>,
) -> Vec<Assertion<E>> {
Vec::new()
}
fn get_periodic_column_values(&self) -> Vec<Vec<Self::BaseField>> {
Vec::new()
}
fn get_periodic_column_polys(&self) -> Vec<Vec<Self::BaseField>> {
let mut twiddle_map = BTreeMap::new();
self.get_periodic_column_values()
.into_iter()
.map(|mut column| {
let cycle_length = column.len();
assert!(
cycle_length >= MIN_CYCLE_LENGTH,
"number of values in a periodic column must be at least {}, but was {}",
MIN_CYCLE_LENGTH,
cycle_length
);
assert!(
cycle_length.is_power_of_two(),
"number of values in a periodic column must be a power of two, but was {}",
cycle_length
);
assert!(cycle_length <= self.trace_length(),
"number of values in a periodic column cannot exceed trace length {}, but was {}",
self.trace_length(),
cycle_length
);
let inv_twiddles = twiddle_map
.entry(cycle_length)
.or_insert_with(|| fft::get_inv_twiddles::<Self::BaseField>(cycle_length));
fft::interpolate_poly(&mut column, inv_twiddles);
column
})
.collect()
}
fn get_transition_constraints<E: FieldElement<BaseField = Self::BaseField>>(
&self,
composition_coefficients: &[(E, E)],
) -> TransitionConstraints<E> {
TransitionConstraints::new(self.context(), composition_coefficients)
}
fn get_boundary_constraints<E: FieldElement<BaseField = Self::BaseField>>(
&self,
aux_rand_elements: &AuxTraceRandElements<E>,
composition_coefficients: &[(E, E)],
) -> BoundaryConstraints<E> {
BoundaryConstraints::new(
self.context(),
self.get_assertions(),
self.get_aux_assertions(aux_rand_elements),
composition_coefficients,
)
}
fn options(&self) -> &ProofOptions {
&self.context().options
}
fn trace_info(&self) -> &TraceInfo {
&self.context().trace_info
}
fn trace_length(&self) -> usize {
self.context().trace_info.length()
}
fn trace_layout(&self) -> &TraceLayout {
self.context().trace_info.layout()
}
fn trace_poly_degree(&self) -> usize {
self.context().trace_poly_degree()
}
fn trace_domain_generator(&self) -> Self::BaseField {
self.context().trace_domain_generator
}
fn ce_blowup_factor(&self) -> usize {
self.context().ce_blowup_factor
}
fn ce_domain_size(&self) -> usize {
self.context().ce_domain_size()
}
fn composition_degree(&self) -> usize {
self.context().composition_degree()
}
fn lde_blowup_factor(&self) -> usize {
self.context().options.blowup_factor()
}
fn lde_domain_size(&self) -> usize {
self.context().lde_domain_size()
}
fn lde_domain_generator(&self) -> Self::BaseField {
self.context().lde_domain_generator
}
fn domain_offset(&self) -> Self::BaseField {
self.context().options.domain_offset()
}
fn get_aux_trace_segment_random_elements<E, H>(
&self,
aux_segment_idx: usize,
public_coin: &mut RandomCoin<Self::BaseField, H>,
) -> Result<Vec<E>, RandomCoinError>
where
E: FieldElement<BaseField = Self::BaseField>,
H: Hasher,
{
let num_elements = self
.trace_info()
.layout()
.get_aux_segment_rand_elements(aux_segment_idx);
let mut result = Vec::with_capacity(num_elements);
for _ in 0..num_elements {
result.push(public_coin.draw()?);
}
Ok(result)
}
fn get_constraint_composition_coefficients<E, H>(
&self,
public_coin: &mut RandomCoin<Self::BaseField, H>,
) -> Result<ConstraintCompositionCoefficients<E>, RandomCoinError>
where
E: FieldElement<BaseField = Self::BaseField>,
H: Hasher,
{
let mut t_coefficients = Vec::new();
for _ in 0..self.context().num_transition_constraints() {
t_coefficients.push(public_coin.draw_pair()?);
}
let mut b_coefficients = Vec::new();
for _ in 0..self.context().num_assertions() {
b_coefficients.push(public_coin.draw_pair()?);
}
Ok(ConstraintCompositionCoefficients {
transition: t_coefficients,
boundary: b_coefficients,
})
}
fn get_deep_composition_coefficients<E, H>(
&self,
public_coin: &mut RandomCoin<Self::BaseField, H>,
) -> Result<DeepCompositionCoefficients<E>, RandomCoinError>
where
E: FieldElement<BaseField = Self::BaseField>,
H: Hasher,
{
let mut t_coefficients = Vec::new();
for _ in 0..self.trace_info().width() {
t_coefficients.push(public_coin.draw_triple()?);
}
let mut c_coefficients = Vec::new();
for _ in 0..self.ce_blowup_factor() {
c_coefficients.push(public_coin.draw()?);
}
Ok(DeepCompositionCoefficients {
trace: t_coefficients,
constraints: c_coefficients,
degree: public_coin.draw_pair()?,
})
}
}