use std::marker::PhantomData; use plonky2::field::extension_field::{Extendable, FieldExtension}; use plonky2::field::packed_field::PackedField; use plonky2::hash::hash_types::RichField; use plonky2::plonk::circuit_builder::CircuitBuilder; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::stark::Stark; use crate::vars::{StarkEvaluationTargets, StarkEvaluationVars}; /// Toy STARK system used for testing. /// Computes a Fibonacci sequence with state `[x0, x1]` using the state transition /// `x0 <- x1, x1 <- x0 + x1`. #[derive(Copy, Clone)] struct FibonacciStark, const D: usize> { num_rows: usize, _phantom: PhantomData, } impl, const D: usize> FibonacciStark { // The first public input is `x0`. const PI_INDEX_X0: usize = 0; // The second public input is `x1`. const PI_INDEX_X1: usize = 1; // The third public input is the second element of the last row, which should be equal to the // `num_rows`-th Fibonacci number. const PI_INDEX_RES: usize = 2; fn new(num_rows: usize) -> Self { Self { num_rows, _phantom: PhantomData, } } /// Generate the trace using `x0, x1` as inital state values. fn generate_trace(&self, x0: F, x1: F) -> Vec<[F; Self::COLUMNS]> { (0..self.num_rows) .scan([x0, x1], |acc, _| { let tmp = *acc; acc[0] = tmp[1]; acc[1] = tmp[0] + tmp[1]; Some(tmp) }) .collect() } } impl, const D: usize> Stark for FibonacciStark { const COLUMNS: usize = 2; const PUBLIC_INPUTS: usize = 3; fn eval_packed_generic( &self, vars: StarkEvaluationVars, yield_constr: &mut ConstraintConsumer

, ) where FE: FieldExtension, P: PackedField, { // Check public inputs. yield_constr .constraint_first_row(vars.local_values[0] - vars.public_inputs[Self::PI_INDEX_X0]); yield_constr .constraint_first_row(vars.local_values[1] - vars.public_inputs[Self::PI_INDEX_X1]); yield_constr .constraint_last_row(vars.local_values[1] - vars.public_inputs[Self::PI_INDEX_RES]); // x0 <- x1 yield_constr.constraint(vars.next_values[0] - vars.local_values[1]); // x1 <- x0 + x1 yield_constr.constraint(vars.next_values[1] - vars.local_values[0] - vars.local_values[1]); } fn eval_ext_recursively( &self, builder: &mut CircuitBuilder, vars: StarkEvaluationTargets, yield_constr: &mut RecursiveConstraintConsumer, ) { todo!() } fn constraint_degree(&self) -> usize { 2 } } #[cfg(test)] mod tests { use anyhow::Result; use plonky2::field::field_types::Field; use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; use plonky2::util::timing::TimingTree; use crate::config::StarkConfig; use crate::fibonacci_stark::FibonacciStark; use crate::prover::prove; use crate::stark_testing::test_stark_low_degree; use crate::verifier::verify; fn fibonacci(n: usize, x0: F, x1: F) -> F { (0..n).fold((x0, x1), |x, _| (x.1, x.0 + x.1)).1 } #[test] fn test_fibonacci_stark() -> Result<()> { const D: usize = 2; type C = PoseidonGoldilocksConfig; type F = >::F; type S = FibonacciStark; let config = StarkConfig::standard_fast_config(); let num_rows = 1 << 5; let public_inputs = [F::ZERO, F::ONE, fibonacci(num_rows - 1, F::ZERO, F::ONE)]; let stark = S::new(num_rows); let trace = stark.generate_trace(public_inputs[0], public_inputs[1]); let proof = prove::( stark, &config, trace, public_inputs, &mut TimingTree::default(), )?; verify(stark, proof, &config) } #[test] fn test_fibonacci_stark_degree() -> Result<()> { const D: usize = 2; type C = PoseidonGoldilocksConfig; type F = >::F; type S = FibonacciStark; let config = StarkConfig::standard_fast_config(); let num_rows = 1 << 5; let stark = S::new(num_rows); test_stark_low_degree(stark) } }