commit bd6f23c5909a4f3d2e01333ac181d3464a9132dd Author: arnaucube Date: Wed Jan 29 16:06:20 2025 +0100 port fibonacci example from starky docs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..96ef6c0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..79a0e63 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "starky-tmp" +version = "0.1.0" +edition = "2021" + +[dependencies] +plonky2 = { git = "https://github.com/0xPolygonZero/plonky2" } +starky = { git = "https://github.com/0xPolygonZero/plonky2" } +anyhow = "1.0.86" +itertools = "0.13" +rand = "0.8.5" diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..8af7564 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,183 @@ +/// The code of this file is an adaptation from the Starky example from the plonky2 repo. +/// +use plonky2::field::extension::{Extendable, FieldExtension}; +use plonky2::field::packed::PackedField; +use plonky2::field::polynomial::PolynomialValues; +use plonky2::hash::hash_types::RichField; +use std::marker::PhantomData; + +// Imports to define the constraints of our STARK. +use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; +use starky::evaluation_frame::{StarkEvaluationFrame, StarkFrame}; +use starky::stark::Stark; + +// Imports to define the recursive constraints of our STARK. +use plonky2::iop::ext_target::ExtensionTarget; +use plonky2::plonk::circuit_builder::CircuitBuilder; +use starky::util::trace_rows_to_poly_values; + +// Imports to generate a STARK instance, compute the trace and prove it +use plonky2::field::types::Field; +use plonky2::plonk::config::GenericConfig; +use plonky2::plonk::config::PoseidonGoldilocksConfig; +use plonky2::util::timing::TimingTree; +use starky::config::StarkConfig; +use starky::prover::prove; +use starky::verifier::verify_stark_proof; + +const D: usize = 2; +const CONFIG: StarkConfig = StarkConfig::standard_fast_config(); +type C = PoseidonGoldilocksConfig; +type F = >::F; +type S = FibonacciStark; + +#[derive(Copy, Clone)] +pub struct FibonacciStark, const D: usize> { + num_rows: usize, + _phantom: PhantomData, +} +// Define witness generation. +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; + + pub(crate) fn new(num_rows: usize) -> Self { + Self { + num_rows, + _phantom: PhantomData, + } + } + + /// Generate the trace using `x0, x1, 0` as initial state values. + fn generate_trace(&self, x0: F, x1: F) -> Vec> { + let mut trace_rows = (0..self.num_rows) + .scan([x0, x1, F::ZERO], |acc, _| { + let tmp = *acc; + acc[0] = tmp[1]; + acc[1] = tmp[0] + tmp[1]; + acc[2] = tmp[2] + F::ONE; + Some(tmp) + }) + .collect::>(); + // Transpose the row-wise trace for the prover. + trace_rows_to_poly_values(trace_rows) + } +} + +// Define constraints. +const COLUMNS: usize = 3; +const PUBLIC_INPUTS: usize = 3; + +impl, const D: usize> Stark for FibonacciStark { + type EvaluationFrame + = StarkFrame + where + FE: FieldExtension, + P: PackedField; + + type EvaluationFrameTarget = + StarkFrame, ExtensionTarget, COLUMNS, PUBLIC_INPUTS>; + + // Define this STARK's constraints. + fn eval_packed_generic( + &self, + vars: &Self::EvaluationFrame, + yield_constr: &mut ConstraintConsumer

, + ) where + FE: FieldExtension, + P: PackedField, + { + let local_values = vars.get_local_values(); + let next_values = vars.get_next_values(); + let public_inputs = vars.get_public_inputs(); + + // Check public inputs. + yield_constr.constraint_first_row(local_values[0] - public_inputs[Self::PI_INDEX_X0]); + yield_constr.constraint_first_row(local_values[1] - public_inputs[Self::PI_INDEX_X1]); + yield_constr.constraint_last_row(local_values[1] - public_inputs[Self::PI_INDEX_RES]); + + // Enforce the Fibonacci transition constraints. + // x0' <- x1 + yield_constr.constraint_transition(next_values[0] - local_values[1]); + // x1' <- x0 + x1 + yield_constr.constraint_transition(next_values[1] - local_values[0] - local_values[1]); + } + + // Define the constraints to recursively verify this STARK. + fn eval_ext_circuit( + &self, + builder: &mut CircuitBuilder, + vars: &Self::EvaluationFrameTarget, + yield_constr: &mut RecursiveConstraintConsumer, + ) { + let local_values = vars.get_local_values(); + let next_values = vars.get_next_values(); + let public_inputs = vars.get_public_inputs(); + + // Check public inputs. + let pis_constraints = [ + builder.sub_extension(local_values[0], public_inputs[Self::PI_INDEX_X0]), + builder.sub_extension(local_values[1], public_inputs[Self::PI_INDEX_X1]), + builder.sub_extension(local_values[1], public_inputs[Self::PI_INDEX_RES]), + ]; + + yield_constr.constraint_first_row(builder, pis_constraints[0]); + yield_constr.constraint_first_row(builder, pis_constraints[1]); + yield_constr.constraint_last_row(builder, pis_constraints[2]); + + // Enforce the Fibonacci transition constraints. + // x0' <- x1 + let first_col_constraint = builder.sub_extension(next_values[0], local_values[1]); + yield_constr.constraint_transition(builder, first_col_constraint); + // x1' <- x0 + x1 + let second_col_constraint = { + let tmp = builder.sub_extension(next_values[1], local_values[0]); + builder.sub_extension(tmp, local_values[1]) + }; + yield_constr.constraint_transition(builder, second_col_constraint); + } + + fn constraint_degree(&self) -> usize { + 2 + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn fibonacci_native(n: usize, x0: F, x1: F) -> F { + (0..n).fold((x0, x1), |acc, _| (acc.1, acc.0 + acc.1)).1 + } + + // test that instantiates a new `FibonacciStark` instance, generates an associated STARK trace, + // and generates a proof for it. + #[test] + fn test_fibonacci_stark() { + let num_rows = 1 << 10; + let x0 = F::from_canonical_u32(2); + let x1 = F::from_canonical_u32(7); + + let public_inputs = [x0, x1, fibonacci_native(num_rows - 1, x0, x1)]; + let stark = FibonacciStark::::new(num_rows); + let trace = stark.generate_trace(public_inputs[0], public_inputs[1]); + + let proof = prove::( + stark, + &CONFIG, + trace, + &public_inputs, + None, + &mut TimingTree::default(), + ) + .expect("We should have a valid proof!"); + + verify_stark_proof(stark, proof, &CONFIG, None) + .expect("We should be able to verify this proof!") + } +}