#![cfg_attr(not(feature = "std"), no_std)]
#[cfg(not(feature = "std"))]
#[macro_use]
extern crate alloc;
pub use air::{
proof::StarkProof, Air, AirContext, Assertion, AuxTraceRandElements, BoundaryConstraint,
BoundaryConstraintGroup, ConstraintCompositionCoefficients, ConstraintDivisor,
DeepCompositionCoefficients, EvaluationFrame, FieldExtension, HashFunction, ProofOptions,
TraceInfo, TraceLayout, TransitionConstraintDegree, TransitionConstraintGroup,
};
pub use utils::{
iterators, ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable,
SliceReader,
};
use fri::FriProver;
use utils::collections::Vec;
pub use math;
use math::{
fft::infer_degree,
fields::{CubeExtension, QuadExtension},
ExtensibleField, FieldElement, StarkField,
};
pub use crypto;
use crypto::{
hashers::{Blake3_192, Blake3_256, Sha3_256},
ElementHasher, MerkleTree,
};
#[cfg(feature = "std")]
use log::debug;
#[cfg(feature = "std")]
use math::log2;
#[cfg(feature = "std")]
use std::time::Instant;
mod domain;
pub use domain::StarkDomain;
mod matrix;
pub use matrix::Matrix;
mod constraints;
use constraints::{CompositionPoly, ConstraintCommitment, ConstraintEvaluator};
mod composer;
use composer::DeepCompositionPoly;
mod trace;
pub use trace::{Trace, TraceTable, TraceTableFragment};
use trace::{TraceCommitment, TraceLde, TracePolyTable};
mod channel;
use channel::ProverChannel;
mod errors;
pub use errors::ProverError;
#[cfg(test)]
pub mod tests;
pub trait Prover {
type BaseField: StarkField + ExtensibleField<2> + ExtensibleField<3>;
type Air: Air<BaseField = Self::BaseField>;
type Trace: Trace<BaseField = Self::BaseField>;
fn get_pub_inputs(&self, trace: &Self::Trace) -> <<Self as Prover>::Air as Air>::PublicInputs;
fn options(&self) -> &ProofOptions;
#[rustfmt::skip]
fn prove(&self, trace: Self::Trace) -> Result<StarkProof, ProverError> {
match self.options().field_extension() {
FieldExtension::None => match self.options().hash_fn() {
HashFunction::Blake3_256 => self.generate_proof::<Self::BaseField, Blake3_256<Self::BaseField>>(trace),
HashFunction::Blake3_192 => self.generate_proof::<Self::BaseField, Blake3_192<Self::BaseField>>(trace),
HashFunction::Sha3_256 => self.generate_proof::<Self::BaseField, Sha3_256<Self::BaseField>>(trace),
},
FieldExtension::Quadratic => {
if !<QuadExtension<Self::BaseField>>::is_supported() {
return Err(ProverError::UnsupportedFieldExtension(2));
}
match self.options().hash_fn() {
HashFunction::Blake3_256 => self.generate_proof::<QuadExtension<Self::BaseField>, Blake3_256<Self::BaseField>>(trace),
HashFunction::Blake3_192 => self.generate_proof::<QuadExtension<Self::BaseField>, Blake3_192<Self::BaseField>>(trace),
HashFunction::Sha3_256 => self.generate_proof::<QuadExtension<Self::BaseField>, Sha3_256<Self::BaseField>>(trace),
}
}
FieldExtension::Cubic => {
if !<CubeExtension<Self::BaseField>>::is_supported() {
return Err(ProverError::UnsupportedFieldExtension(3));
}
match self.options().hash_fn() {
HashFunction::Blake3_256 => self.generate_proof::<CubeExtension<Self::BaseField>, Blake3_256<Self::BaseField>>(trace),
HashFunction::Blake3_192 => self.generate_proof::<CubeExtension<Self::BaseField>, Blake3_192<Self::BaseField>>(trace),
HashFunction::Sha3_256 => self.generate_proof::<CubeExtension<Self::BaseField>, Sha3_256<Self::BaseField>>(trace),
}
}
}
}
#[doc(hidden)]
fn generate_proof<E, H>(&self, mut trace: Self::Trace) -> Result<StarkProof, ProverError>
where
E: FieldElement<BaseField = Self::BaseField>,
H: ElementHasher<BaseField = Self::BaseField>,
{
let pub_inputs = self.get_pub_inputs(&trace);
let mut pub_inputs_bytes = Vec::new();
pub_inputs.write_into(&mut pub_inputs_bytes);
let air = Self::Air::new(trace.get_info(), pub_inputs, self.options().clone());
let mut channel = ProverChannel::<Self::Air, E, H>::new(&air, pub_inputs_bytes);
#[cfg(feature = "std")]
let now = Instant::now();
let domain = StarkDomain::new(&air);
#[cfg(feature = "std")]
debug!(
"Built domain of 2^{} elements in {} ms",
log2(domain.lde_domain_size()),
now.elapsed().as_millis()
);
let (main_trace_lde, main_trace_tree, main_trace_polys) =
self.build_trace_commitment::<Self::BaseField, H>(trace.main_segment(), &domain);
channel.commit_trace(*main_trace_tree.root());
let mut trace_commitment = TraceCommitment::new(
main_trace_lde,
main_trace_tree,
domain.trace_to_lde_blowup(),
);
let mut trace_polys = TracePolyTable::new(main_trace_polys);
let mut aux_trace_segments = Vec::new();
let mut aux_trace_rand_elements = AuxTraceRandElements::new();
for i in 0..trace.layout().num_aux_segments() {
#[cfg(feature = "std")]
let now = Instant::now();
let rand_elements = channel.get_aux_trace_segment_rand_elements(i);
let aux_segment = trace
.build_aux_segment(&aux_trace_segments, &rand_elements)
.expect("failed build auxiliary trace segment");
#[cfg(feature = "std")]
debug!(
"Built auxiliary trace segment of {} columns and 2^{} steps in {} ms",
aux_segment.num_cols(),
log2(aux_segment.num_rows()),
now.elapsed().as_millis()
);
let (aux_segment_lde, aux_segment_tree, aux_segment_polys) =
self.build_trace_commitment::<E, H>(&aux_segment, &domain);
channel.commit_trace(*aux_segment_tree.root());
trace_commitment.add_segment(aux_segment_lde, aux_segment_tree);
trace_polys.add_aux_segment(aux_segment_polys);
aux_trace_rand_elements.add_segment_elements(rand_elements);
aux_trace_segments.push(aux_segment);
}
#[cfg(debug_assertions)]
trace.validate(&air, &aux_trace_segments, &aux_trace_rand_elements);
#[cfg(feature = "std")]
let now = Instant::now();
let constraint_coeffs = channel.get_constraint_composition_coeffs();
let evaluator = ConstraintEvaluator::new(&air, aux_trace_rand_elements, constraint_coeffs);
let constraint_evaluations = evaluator.evaluate(trace_commitment.trace_table(), &domain);
#[cfg(feature = "std")]
debug!(
"Evaluated constraints over domain of 2^{} elements in {} ms",
log2(constraint_evaluations.num_rows()),
now.elapsed().as_millis()
);
#[cfg(feature = "std")]
let now = Instant::now();
let composition_poly = constraint_evaluations.into_poly()?;
#[cfg(feature = "std")]
debug!(
"Converted constraint evaluations into {} composition polynomial columns of degree {} in {} ms",
composition_poly.num_columns(),
composition_poly.column_degree(),
now.elapsed().as_millis()
);
let constraint_commitment =
self.build_constraint_commitment::<E, H>(&composition_poly, &domain);
channel.commit_constraints(constraint_commitment.root());
#[cfg(feature = "std")]
let now = Instant::now();
let z = channel.get_ood_point();
let ood_trace_states = trace_polys.get_ood_frame(z);
channel.send_ood_trace_states(&ood_trace_states);
let ood_evaluations = composition_poly.evaluate_at(z);
channel.send_ood_constraint_evaluations(&ood_evaluations);
let deep_coefficients = channel.get_deep_composition_coeffs();
let mut deep_composition_poly = DeepCompositionPoly::new(&air, z, deep_coefficients);
deep_composition_poly.add_trace_polys(trace_polys, ood_trace_states);
deep_composition_poly.add_composition_poly(composition_poly, ood_evaluations);
deep_composition_poly.adjust_degree();
#[cfg(feature = "std")]
debug!(
"Built DEEP composition polynomial of degree {} in {} ms",
deep_composition_poly.degree(),
now.elapsed().as_millis()
);
assert_eq!(domain.trace_length() - 1, deep_composition_poly.degree());
#[cfg(feature = "std")]
let now = Instant::now();
let deep_evaluations = deep_composition_poly.evaluate(&domain);
debug_assert_eq!(
domain.trace_length() - 1,
infer_degree(&deep_evaluations, domain.offset())
);
#[cfg(feature = "std")]
debug!(
"Evaluated DEEP composition polynomial over LDE domain (2^{} elements) in {} ms",
log2(domain.lde_domain_size()),
now.elapsed().as_millis()
);
#[cfg(feature = "std")]
let now = Instant::now();
let mut fri_prover = FriProver::new(air.options().to_fri_options());
fri_prover.build_layers(&mut channel, deep_evaluations);
#[cfg(feature = "std")]
debug!(
"Computed {} FRI layers from composition polynomial evaluations in {} ms",
fri_prover.num_layers(),
now.elapsed().as_millis()
);
#[cfg(feature = "std")]
let now = Instant::now();
channel.grind_query_seed();
let query_positions = channel.get_query_positions();
#[cfg(feature = "std")]
debug!(
"Determined {} query positions in {} ms",
query_positions.len(),
now.elapsed().as_millis()
);
#[cfg(feature = "std")]
let now = Instant::now();
let fri_proof = fri_prover.build_proof(&query_positions);
let trace_queries = trace_commitment.query(&query_positions);
let constraint_queries = constraint_commitment.query(&query_positions);
let proof = channel.build_proof(trace_queries, constraint_queries, fri_proof);
#[cfg(feature = "std")]
debug!("Built proof object in {} ms", now.elapsed().as_millis());
Ok(proof)
}
fn build_trace_commitment<E, H>(
&self,
trace: &Matrix<E>,
domain: &StarkDomain<Self::BaseField>,
) -> (Matrix<E>, MerkleTree<H>, Matrix<E>)
where
E: FieldElement<BaseField = Self::BaseField>,
H: ElementHasher<BaseField = Self::BaseField>,
{
#[cfg(feature = "std")]
let now = Instant::now();
let trace_polys = trace.interpolate_columns();
let trace_lde = trace_polys.evaluate_columns_over(domain);
#[cfg(feature = "std")]
debug!(
"Extended execution trace of {} columns from 2^{} to 2^{} steps ({}x blowup) in {} ms",
trace_lde.num_cols(),
log2(trace_polys.num_rows()),
log2(trace_lde.num_rows()),
domain.trace_to_lde_blowup(),
now.elapsed().as_millis()
);
#[cfg(feature = "std")]
let now = Instant::now();
let trace_tree = trace_lde.commit_to_rows();
#[cfg(feature = "std")]
debug!(
"Computed execution trace commitment (Merkle tree of depth {}) in {} ms",
trace_tree.depth(),
now.elapsed().as_millis()
);
(trace_lde, trace_tree, trace_polys)
}
fn build_constraint_commitment<E, H>(
&self,
composition_poly: &CompositionPoly<E>,
domain: &StarkDomain<Self::BaseField>,
) -> ConstraintCommitment<E, H>
where
E: FieldElement<BaseField = Self::BaseField>,
H: ElementHasher<BaseField = Self::BaseField>,
{
#[cfg(feature = "std")]
let now = Instant::now();
let composed_evaluations = composition_poly.evaluate(domain);
#[cfg(feature = "std")]
debug!(
"Evaluated {} composition polynomial columns over LDE domain (2^{} elements) in {} ms",
composed_evaluations.num_cols(),
log2(composed_evaluations.num_rows()),
now.elapsed().as_millis()
);
#[cfg(feature = "std")]
let now = Instant::now();
let commitment = composed_evaluations.commit_to_rows();
let constraint_commitment = ConstraintCommitment::new(composed_evaluations, commitment);
#[cfg(feature = "std")]
debug!(
"Computed constraint evaluation commitment (Merkle tree of depth {}) in {} ms",
constraint_commitment.tree_depth(),
now.elapsed().as_millis()
);
constraint_commitment
}
}