plonky2/starky/src/fibonacci_stark.rs

264 lines
9.3 KiB
Rust
Raw Normal View History

2022-01-27 07:56:22 +01:00
use std::marker::PhantomData;
use plonky2::field::extension::{Extendable, FieldExtension};
use plonky2::field::packed::PackedField;
use plonky2::field::polynomial::PolynomialValues;
2022-01-27 07:56:22 +01:00
use plonky2::hash::hash_types::RichField;
use plonky2::plonk::circuit_builder::CircuitBuilder;
use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer};
2022-02-21 18:00:03 +01:00
use crate::permutation::PermutationPair;
2022-01-27 07:56:22 +01:00
use crate::stark::Stark;
use crate::util::trace_rows_to_poly_values;
2022-01-27 07:56:22 +01:00
use crate::vars::{StarkEvaluationTargets, StarkEvaluationVars};
2022-01-27 13:27:06 +01:00
/// Toy STARK system used for testing.
2022-02-21 18:00:03 +01:00
/// Computes a Fibonacci sequence with state `[x0, x1, i, j]` using the state transition
/// `x0' <- x1, x1' <- x0 + x1, i' <- i+1, j' <- j+1`.
2022-02-22 17:00:08 +01:00
/// Note: The `i, j` columns are only used to test the permutation argument.
2022-01-31 18:00:07 +01:00
#[derive(Copy, Clone)]
2022-01-28 05:02:31 +01:00
struct FibonacciStark<F: RichField + Extendable<D>, const D: usize> {
num_rows: usize,
2022-01-27 07:56:22 +01:00
_phantom: PhantomData<F>,
}
2022-01-27 12:58:56 +01:00
impl<F: RichField + Extendable<D>, const D: usize> FibonacciStark<F, D> {
2022-01-28 05:02:31 +01:00
// 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.
2022-01-28 05:02:31 +01:00
const PI_INDEX_RES: usize = 2;
2022-01-27 07:56:22 +01:00
fn new(num_rows: usize) -> Self {
2022-01-27 07:56:22 +01:00
Self {
2022-01-28 05:02:31 +01:00
num_rows,
2022-01-27 07:56:22 +01:00
_phantom: PhantomData,
}
}
2022-02-21 18:00:03 +01:00
/// Generate the trace using `x0, x1, 0, 1` as initial state values.
fn generate_trace(&self, x0: F, x1: F) -> Vec<PolynomialValues<F>> {
let mut trace_rows = (0..self.num_rows)
2022-02-21 18:00:03 +01:00
.scan([x0, x1, F::ZERO, F::ONE], |acc, _| {
2022-01-27 07:56:22 +01:00
let tmp = *acc;
2022-01-27 12:58:56 +01:00
acc[0] = tmp[1];
acc[1] = tmp[0] + tmp[1];
2022-02-21 18:00:03 +01:00
acc[2] = tmp[2] + F::ONE;
acc[3] = tmp[3] + F::ONE;
2022-01-27 07:56:22 +01:00
Some(tmp)
})
2022-02-21 18:00:03 +01:00
.collect::<Vec<_>>();
trace_rows[self.num_rows - 1][3] = F::ZERO; // So that column 2 and 3 are permutation of one another.
trace_rows_to_poly_values(trace_rows)
2022-01-27 07:56:22 +01:00
}
}
2022-01-27 12:58:56 +01:00
impl<F: RichField + Extendable<D>, const D: usize> Stark<F, D> for FibonacciStark<F, D> {
2022-02-21 18:00:03 +01:00
const COLUMNS: usize = 4;
2022-01-28 05:02:31 +01:00
const PUBLIC_INPUTS: usize = 3;
2022-01-27 07:56:22 +01:00
fn eval_packed_generic<FE, P, const D2: usize>(
&self,
vars: StarkEvaluationVars<FE, P, { Self::COLUMNS }, { Self::PUBLIC_INPUTS }>,
yield_constr: &mut ConstraintConsumer<P>,
) where
FE: FieldExtension<D2, BaseField = F>,
P: PackedField<Scalar = FE>,
{
2022-02-01 10:48:53 +01:00
// 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]);
2022-02-15 08:35:57 +01:00
// x0' <- x1
yield_constr.constraint_transition(vars.next_values[0] - vars.local_values[1]);
2022-02-15 08:35:57 +01:00
// x1' <- x0 + x1
yield_constr.constraint_transition(
vars.next_values[1] - vars.local_values[0] - vars.local_values[1],
);
2022-01-27 07:56:22 +01:00
}
2022-05-17 11:04:35 +02:00
fn eval_ext_circuit(
2022-01-27 07:56:22 +01:00
&self,
builder: &mut CircuitBuilder<F, D>,
vars: StarkEvaluationTargets<D, { Self::COLUMNS }, { Self::PUBLIC_INPUTS }>,
yield_constr: &mut RecursiveConstraintConsumer<F, D>,
) {
2022-02-07 10:25:01 +01:00
// Check public inputs.
let pis_constraints = [
builder.sub_extension(vars.local_values[0], vars.public_inputs[Self::PI_INDEX_X0]),
builder.sub_extension(vars.local_values[1], vars.public_inputs[Self::PI_INDEX_X1]),
builder.sub_extension(vars.local_values[1], vars.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]);
2022-02-15 08:35:57 +01:00
// x0' <- x1
2022-02-07 10:25:01 +01:00
let first_col_constraint = builder.sub_extension(vars.next_values[0], vars.local_values[1]);
yield_constr.constraint_transition(builder, first_col_constraint);
2022-02-15 08:35:57 +01:00
// x1' <- x0 + x1
2022-02-07 10:25:01 +01:00
let second_col_constraint = {
let tmp = builder.sub_extension(vars.next_values[1], vars.local_values[0]);
builder.sub_extension(tmp, vars.local_values[1])
};
yield_constr.constraint_transition(builder, second_col_constraint);
2022-01-27 07:56:22 +01:00
}
2022-02-04 15:56:59 +01:00
2022-02-04 17:04:07 +01:00
fn constraint_degree(&self) -> usize {
2022-02-04 16:39:34 +01:00
2
2022-02-04 15:56:59 +01:00
}
2022-02-21 18:00:03 +01:00
fn permutation_pairs(&self) -> Vec<PermutationPair> {
vec![PermutationPair::singletons(2, 3)]
2022-02-21 18:00:03 +01:00
}
2022-01-27 07:56:22 +01:00
}
2022-02-10 16:14:18 +01:00
#[cfg(test)]
mod tests {
use anyhow::Result;
use plonky2::field::extension::Extendable;
use plonky2::field::types::Field;
2022-02-10 16:14:18 +01:00
use plonky2::hash::hash_types::RichField;
use plonky2::iop::witness::PartialWitness;
use plonky2::plonk::circuit_builder::CircuitBuilder;
2022-02-14 10:00:37 +01:00
use plonky2::plonk::circuit_data::CircuitConfig;
2022-02-15 08:17:07 +01:00
use plonky2::plonk::config::{
AlgebraicHasher, GenericConfig, Hasher, PoseidonGoldilocksConfig,
};
2022-02-10 16:14:18 +01:00
use plonky2::util::timing::TimingTree;
use crate::config::StarkConfig;
use crate::fibonacci_stark::FibonacciStark;
use crate::proof::StarkProofWithPublicInputs;
use crate::prover::prove;
use crate::recursive_verifier::{
2022-05-17 11:04:35 +02:00
add_virtual_stark_proof_with_pis, set_stark_proof_with_pis_target,
verify_stark_proof_circuit,
2022-02-10 16:14:18 +01:00
};
use crate::stark::Stark;
2022-05-19 10:22:57 +02:00
use crate::stark_testing::{test_stark_circuit_constraints, test_stark_low_degree};
2022-02-15 08:35:57 +01:00
use crate::verifier::verify_stark_proof;
2022-02-10 16:14:18 +01:00
fn fibonacci<F: Field>(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 = <C as GenericConfig<D>>::F;
type S = FibonacciStark<F, D>;
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::<F, C, S, D>(
stark,
&config,
trace,
public_inputs,
&mut TimingTree::default(),
)?;
2022-02-15 08:35:57 +01:00
verify_stark_proof(stark, proof, &config)
2022-02-10 16:14:18 +01:00
}
#[test]
fn test_fibonacci_stark_degree() -> Result<()> {
const D: usize = 2;
type C = PoseidonGoldilocksConfig;
type F = <C as GenericConfig<D>>::F;
type S = FibonacciStark<F, D>;
let num_rows = 1 << 5;
let stark = S::new(num_rows);
test_stark_low_degree(stark)
}
2022-05-19 10:22:57 +02:00
#[test]
fn test_fibonacci_stark_circuit() -> Result<()> {
const D: usize = 2;
type C = PoseidonGoldilocksConfig;
type F = <C as GenericConfig<D>>::F;
type S = FibonacciStark<F, D>;
let num_rows = 1 << 5;
let stark = S::new(num_rows);
2022-05-19 11:10:10 +02:00
test_stark_circuit_constraints::<F, C, S, D>(stark)
2022-05-19 10:22:57 +02:00
}
2022-02-10 16:14:18 +01:00
#[test]
fn test_recursive_stark_verifier() -> Result<()> {
init_logger();
const D: usize = 2;
type C = PoseidonGoldilocksConfig;
type F = <C as GenericConfig<D>>::F;
type S = FibonacciStark<F, D>;
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::<F, C, S, D>(
stark,
&config,
trace,
public_inputs,
&mut TimingTree::default(),
)?;
2022-02-15 08:35:57 +01:00
verify_stark_proof(stark, proof.clone(), &config)?;
2022-02-10 16:14:18 +01:00
2022-02-14 10:23:26 +01:00
recursive_proof::<F, C, S, C, D>(stark, proof, &config, true)
2022-02-10 16:14:18 +01:00
}
fn recursive_proof<
F: RichField + Extendable<D>,
C: GenericConfig<D, F = F>,
2022-02-14 10:23:26 +01:00
S: Stark<F, D> + Copy,
2022-02-10 16:14:18 +01:00
InnerC: GenericConfig<D, F = F>,
const D: usize,
>(
stark: S,
inner_proof: StarkProofWithPublicInputs<F, InnerC, D>,
inner_config: &StarkConfig,
print_gate_counts: bool,
) -> Result<()>
where
InnerC::Hasher: AlgebraicHasher<F>,
[(); S::COLUMNS]:,
[(); S::PUBLIC_INPUTS]:,
2022-02-15 08:17:07 +01:00
[(); C::Hasher::HASH_SIZE]:,
2022-02-10 16:14:18 +01:00
{
let circuit_config = CircuitConfig::standard_recursion_config();
let mut builder = CircuitBuilder::<F, D>::new(circuit_config);
let mut pw = PartialWitness::new();
let degree_bits = inner_proof.proof.recover_degree_bits(inner_config);
let pt = add_virtual_stark_proof_with_pis(&mut builder, stark, inner_config, degree_bits);
2022-02-15 08:35:57 +01:00
set_stark_proof_with_pis_target(&mut pw, &pt, &inner_proof);
2022-02-10 16:14:18 +01:00
2022-05-17 11:04:35 +02:00
verify_stark_proof_circuit::<F, InnerC, S, D>(&mut builder, stark, pt, inner_config);
2022-02-10 16:14:18 +01:00
if print_gate_counts {
builder.print_gate_counts(0);
}
let data = builder.build::<C>();
2022-02-14 10:12:24 +01:00
let proof = data.prove(pw)?;
2022-02-14 10:00:37 +01:00
data.verify(proof)
2022-02-10 16:14:18 +01:00
}
fn init_logger() {
let _ = env_logger::builder().format_timestamp(None).try_init();
}
}