#![no_std]
#[macro_use]
extern crate alloc;
pub use air::{
proof, proof::Proof, Air, AirContext, Assertion, BoundaryConstraint, BoundaryConstraintGroup,
ConstraintCompositionCoefficients, ConstraintDivisor, DeepCompositionCoefficients,
EvaluationFrame, FieldExtension, ProofOptions, TraceInfo, TransitionConstraintDegree,
};
use air::{AuxRandElements, PartitionOptions};
pub use crypto;
use crypto::{ElementHasher, RandomCoin, VectorCommitment};
use fri::FriProver;
pub use math;
use math::{
fft::infer_degree,
fields::{CubeExtension, QuadExtension},
ExtensibleField, FieldElement, StarkField, ToElements,
};
use tracing::{event, info_span, instrument, Level};
pub use utils::{
iterators, ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable,
SliceReader,
};
mod domain;
pub use domain::StarkDomain;
pub mod matrix;
use matrix::{ColMatrix, RowMatrix};
mod constraints;
pub use constraints::{
CompositionPoly, CompositionPolyTrace, ConstraintCommitment, ConstraintEvaluator,
DefaultConstraintCommitment, DefaultConstraintEvaluator,
};
mod composer;
use composer::DeepCompositionPoly;
mod trace;
use maybe_async::{maybe_async, maybe_await};
pub use trace::{
AuxTraceWithMetadata, DefaultTraceLde, Trace, TraceLde, TracePolyTable, TraceTable,
TraceTableFragment,
};
mod channel;
use channel::ProverChannel;
mod errors;
pub use errors::ProverError;
#[cfg(test)]
pub mod tests;
const DEFAULT_SEGMENT_WIDTH: usize = 8;
pub trait Prover {
type BaseField: StarkField + ExtensibleField<2> + ExtensibleField<3>;
type Air: Air<BaseField = Self::BaseField>;
type Trace: Trace<BaseField = Self::BaseField> + Send + Sync;
type HashFn: ElementHasher<BaseField = Self::BaseField>;
type VC: VectorCommitment<Self::HashFn>;
type RandomCoin: RandomCoin<BaseField = Self::BaseField, Hasher = Self::HashFn>;
type TraceLde<E>: TraceLde<E, HashFn = Self::HashFn, VC = Self::VC>
where
E: FieldElement<BaseField = Self::BaseField>;
type ConstraintEvaluator<'a, E>: ConstraintEvaluator<E, Air = Self::Air>
where
E: FieldElement<BaseField = Self::BaseField>;
type ConstraintCommitment<E>: ConstraintCommitment<E, HashFn = Self::HashFn, VC = Self::VC>
where
E: FieldElement<BaseField = Self::BaseField>;
fn get_pub_inputs(&self, trace: &Self::Trace) -> <<Self as Prover>::Air as Air>::PublicInputs;
fn options(&self) -> &ProofOptions;
#[maybe_async]
fn new_trace_lde<E>(
&self,
trace_info: &TraceInfo,
main_trace: &ColMatrix<Self::BaseField>,
domain: &StarkDomain<Self::BaseField>,
partition_option: PartitionOptions,
) -> (Self::TraceLde<E>, TracePolyTable<E>)
where
E: FieldElement<BaseField = Self::BaseField>;
#[maybe_async]
fn new_evaluator<'a, E>(
&self,
air: &'a Self::Air,
aux_rand_elements: Option<AuxRandElements<E>>,
composition_coefficients: ConstraintCompositionCoefficients<E>,
) -> Self::ConstraintEvaluator<'a, E>
where
E: FieldElement<BaseField = Self::BaseField>;
#[maybe_async]
fn build_constraint_commitment<E>(
&self,
composition_poly_trace: CompositionPolyTrace<E>,
num_constraint_composition_columns: usize,
domain: &StarkDomain<Self::BaseField>,
partition_options: PartitionOptions,
) -> (Self::ConstraintCommitment<E>, CompositionPoly<E>)
where
E: FieldElement<BaseField = Self::BaseField>;
#[allow(unused_variables)]
#[maybe_async]
#[instrument(skip_all)]
fn build_aux_trace<E>(
&self,
main_trace: &Self::Trace,
aux_rand_elements: &AuxRandElements<E>,
) -> ColMatrix<E>
where
E: FieldElement<BaseField = Self::BaseField>,
{
unimplemented!("`Prover::build_aux_trace` needs to be implemented when the trace has an auxiliary segment.")
}
#[maybe_async]
fn prove(&self, trace: Self::Trace) -> Result<Proof, ProverError>
where
<Self::Air as Air>::PublicInputs: Send,
{
match self.options().field_extension() {
FieldExtension::None => maybe_await!(self.generate_proof::<Self::BaseField>(trace)),
FieldExtension::Quadratic => {
if !<QuadExtension<Self::BaseField>>::is_supported() {
return Err(ProverError::UnsupportedFieldExtension(2));
}
maybe_await!(self.generate_proof::<QuadExtension<Self::BaseField>>(trace))
},
FieldExtension::Cubic => {
if !<CubeExtension<Self::BaseField>>::is_supported() {
return Err(ProverError::UnsupportedFieldExtension(3));
}
maybe_await!(self.generate_proof::<CubeExtension<Self::BaseField>>(trace))
},
}
}
#[doc(hidden)]
#[maybe_async]
fn generate_proof<E>(&self, trace: Self::Trace) -> Result<Proof, ProverError>
where
E: FieldElement<BaseField = Self::BaseField>,
<Self::Air as Air>::PublicInputs: Send,
{
let pub_inputs = self.get_pub_inputs(&trace);
let pub_inputs_elements = pub_inputs.to_elements();
let air = Self::Air::new(trace.info().clone(), pub_inputs, self.options().clone());
let mut channel =
ProverChannel::<Self::Air, E, Self::HashFn, Self::RandomCoin, Self::VC>::new(
&air,
pub_inputs_elements,
);
let lde_domain_size = air.lde_domain_size();
let trace_length = air.trace_length();
let domain = info_span!("build_domain", trace_length, lde_domain_size)
.in_scope(|| StarkDomain::new(&air));
assert_eq!(domain.lde_domain_size(), lde_domain_size);
assert_eq!(domain.trace_length(), trace_length);
let (mut trace_lde, mut trace_polys) =
maybe_await!(self.commit_to_main_trace_segment(&trace, &domain, &mut channel));
let aux_trace_with_metadata = if air.trace_info().is_multi_segment() {
let aux_rand_elements = air
.get_aux_rand_elements(channel.public_coin())
.expect("failed to draw random elements for the auxiliary trace segment");
let aux_trace = maybe_await!(self.build_aux_trace(&trace, &aux_rand_elements));
let aux_segment_polys = {
let span = info_span!("commit_to_aux_trace_segment").entered();
let (aux_segment_polys, aux_segment_commitment) =
trace_lde.set_aux_trace(&aux_trace, &domain);
channel.commit_trace(aux_segment_commitment);
drop(span);
aux_segment_polys
};
trace_polys.add_aux_segment(aux_segment_polys);
Some(AuxTraceWithMetadata { aux_trace, aux_rand_elements })
} else {
None
};
#[cfg(debug_assertions)]
trace.validate(&air, aux_trace_with_metadata.as_ref());
let (aux_trace, aux_rand_elements) = match aux_trace_with_metadata {
Some(atm) => (Some(atm.aux_trace), Some(atm.aux_rand_elements)),
None => (None, None),
};
drop(trace);
drop(aux_trace);
let ce_domain_size = air.ce_domain_size();
let composition_poly_trace = maybe_await!(self.new_evaluator(
&air,
aux_rand_elements,
channel.get_constraint_composition_coeffs()
))
.evaluate(&trace_lde, &domain);
assert_eq!(composition_poly_trace.num_rows(), ce_domain_size);
let (constraint_commitment, composition_poly) = maybe_await!(self
.commit_to_constraint_evaluations(&air, composition_poly_trace, &domain, &mut channel));
let deep_composition_poly = {
let span = info_span!("build_deep_composition_poly").entered();
let z = channel.get_ood_point();
let ood_trace_states = trace_polys.get_ood_frame(z);
let ood_evaluations = composition_poly.get_ood_frame(z);
channel.send_ood_evaluations(&ood_trace_states, &ood_evaluations);
let deep_coefficients = channel.get_deep_composition_coeffs();
let mut deep_composition_poly = DeepCompositionPoly::new(z, deep_coefficients);
deep_composition_poly.add_trace_polys(
trace_polys,
composition_poly,
ood_trace_states,
ood_evaluations,
);
event!(Level::DEBUG, "degree: {}", deep_composition_poly.degree());
drop(span);
deep_composition_poly
};
assert_eq!(trace_length - 2, deep_composition_poly.degree());
let deep_evaluations = {
let span = info_span!("evaluate_deep_composition_poly").entered();
let deep_evaluations = deep_composition_poly.evaluate(&domain);
debug_assert_eq!(trace_length - 2, infer_degree(&deep_evaluations, domain.offset()));
drop(span);
deep_evaluations
};
let fri_options = air.options().to_fri_options();
let num_layers = fri_options.num_fri_layers(lde_domain_size);
let mut fri_prover = FriProver::<_, _, _, Self::VC>::new(fri_options);
info_span!("compute_fri_layers", num_layers)
.in_scope(|| fri_prover.build_layers(&mut channel, deep_evaluations));
let query_positions = {
let grinding_factor = air.options().grinding_factor();
let num_positions = air.options().num_queries();
let span =
info_span!("determine_query_positions", grinding_factor, num_positions,).entered();
channel.grind_query_seed();
let query_positions = channel.get_query_positions();
event!(Level::DEBUG, "query_positions_len: {}", query_positions.len());
drop(span);
query_positions
};
let proof = {
let span = info_span!("build_proof_object").entered();
let fri_proof = fri_prover.build_proof(&query_positions);
let trace_queries = trace_lde.query(&query_positions);
let constraint_queries = constraint_commitment.query(&query_positions);
let proof = channel.build_proof(
trace_queries,
constraint_queries,
fri_proof,
query_positions.len(),
);
drop(span);
proof
};
Ok(proof)
}
#[doc(hidden)]
#[instrument(skip_all)]
#[maybe_async]
fn commit_to_main_trace_segment<E>(
&self,
trace: &Self::Trace,
domain: &StarkDomain<Self::BaseField>,
channel: &mut ProverChannel<'_, Self::Air, E, Self::HashFn, Self::RandomCoin, Self::VC>,
) -> (Self::TraceLde<E>, TracePolyTable<E>)
where
E: FieldElement<BaseField = Self::BaseField>,
{
let (trace_lde, trace_polys) = maybe_await!(self.new_trace_lde(
trace.info(),
trace.main_segment(),
domain,
self.options().partition_options(),
));
let main_trace_commitment = trace_lde.get_main_trace_commitment();
channel.commit_trace(main_trace_commitment);
(trace_lde, trace_polys)
}
#[doc(hidden)]
#[instrument(skip_all)]
#[maybe_async]
fn commit_to_constraint_evaluations<E>(
&self,
air: &Self::Air,
composition_poly_trace: CompositionPolyTrace<E>,
domain: &StarkDomain<Self::BaseField>,
channel: &mut ProverChannel<'_, Self::Air, E, Self::HashFn, Self::RandomCoin, Self::VC>,
) -> (Self::ConstraintCommitment<E>, CompositionPoly<E>)
where
E: FieldElement<BaseField = Self::BaseField>,
{
let (constraint_commitment, composition_poly) = maybe_await!(self
.build_constraint_commitment::<E>(
composition_poly_trace,
air.context().num_constraint_composition_columns(),
domain,
self.options().partition_options()
));
channel.commit_constraints(constraint_commitment.commitment());
(constraint_commitment, composition_poly)
}
}