Recursive stark test (failing)

This commit is contained in:
wborgeaud 2022-02-10 16:14:18 +01:00
parent 3aa192a7f6
commit 24c201477c
4 changed files with 144 additions and 321 deletions

View File

@ -96,9 +96,9 @@ impl CircuitConfig {
/// Circuit data required by the prover or the verifier.
pub struct CircuitData<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize> {
pub(crate) prover_only: ProverOnlyCircuitData<F, C, D>,
pub(crate) verifier_only: VerifierOnlyCircuitData<C, D>,
pub(crate) common: CommonCircuitData<F, C, D>,
pub prover_only: ProverOnlyCircuitData<F, C, D>,
pub verifier_only: VerifierOnlyCircuitData<C, D>,
pub common: CommonCircuitData<F, C, D>,
}
impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
@ -181,7 +181,7 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
}
/// Circuit data required by the prover, but not the verifier.
pub(crate) struct ProverOnlyCircuitData<
pub struct ProverOnlyCircuitData<
F: RichField + Extendable<D>,
C: GenericConfig<D, F = F>,
const D: usize,
@ -209,7 +209,7 @@ pub(crate) struct ProverOnlyCircuitData<
/// Circuit data required by the verifier, but not the prover.
#[derive(Debug)]
pub(crate) struct VerifierOnlyCircuitData<C: GenericConfig<D>, const D: usize> {
pub struct VerifierOnlyCircuitData<C: GenericConfig<D>, const D: usize> {
/// A commitment to each constant polynomial and each permutation polynomial.
pub(crate) constants_sigmas_cap: MerkleCap<C::F, C::Hasher>,
}

View File

@ -26,7 +26,7 @@ use crate::util::partial_products::{partial_products_and_z_gx, quotient_chunk_pr
use crate::util::timing::TimingTree;
use crate::util::transpose;
pub(crate) fn prove<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>(
pub fn prove<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>(
prover_data: &ProverOnlyCircuitData<F, C, D>,
common_data: &CommonCircuitData<F, C, D>,
inputs: PartialWitness<F>,

View File

@ -105,317 +105,140 @@ impl<F: RichField + Extendable<D>, const D: usize> Stark<F, D> for FibonacciStar
}
}
// #[cfg(test)]
// mod tests {
// use anyhow::Result;
// use plonky2::field::extension_field::Extendable;
// use plonky2::field::field_types::Field;
// use plonky2::hash::hash_types::RichField;
// use plonky2::iop::witness::PartialWitness;
// use plonky2::plonk::circuit_builder::CircuitBuilder;
// use plonky2::plonk::circuit_data::CommonCircuitData;
// use plonky2::plonk::config::{AlgebraicHasher, GenericConfig, PoseidonGoldilocksConfig};
// use plonky2::plonk::proof::ProofWithPublicInputs;
// 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::add_virtual_stark_proof_with_pis;
// use crate::stark_testing::test_stark_low_degree;
// use crate::verifier::verify;
//
// 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(),
// )?;
//
// verify(stark, proof, &config)
// }
//
// #[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 config = StarkConfig::standard_fast_config();
// let num_rows = 1 << 5;
// let stark = S::new(num_rows);
// test_stark_low_degree(stark)
// }
//
// #[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(),
// )?;
//
// let (proof, _vd, cd) =
// recursive_proof::<F, C, C, D>(proof, vd, cd, &config, None, true, true)?;
// test_serialization(&proof, &cd)?;
//
// Ok(())
// }
//
// #[test]
// fn test_recursive_recursive_verifier() -> Result<()> {
// init_logger();
// const D: usize = 2;
// type C = PoseidonGoldilocksConfig;
// type F = <C as GenericConfig<D>>::F;
//
// let config = CircuitConfig::standard_recursion_config();
//
// // Start with a degree 2^14 proof
// let (proof, vd, cd) = dummy_proof::<F, C, D>(&config, 16_000)?;
// assert_eq!(cd.degree_bits, 14);
//
// // Shrink it to 2^13.
// let (proof, vd, cd) =
// recursive_proof::<F, C, C, D>(proof, vd, cd, &config, Some(13), false, false)?;
// assert_eq!(cd.degree_bits, 13);
//
// // Shrink it to 2^12.
// let (proof, _vd, cd) =
// recursive_proof::<F, C, C, D>(proof, vd, cd, &config, None, true, true)?;
// assert_eq!(cd.degree_bits, 12);
//
// test_serialization(&proof, &cd)?;
//
// Ok(())
// }
//
// /// Creates a chain of recursive proofs where the last proof is made as small as reasonably
// /// possible, using a high rate, high PoW bits, etc.
// #[test]
// #[ignore]
// fn test_size_optimized_recursion() -> Result<()> {
// init_logger();
// const D: usize = 2;
// type C = PoseidonGoldilocksConfig;
// type KC = KeccakGoldilocksConfig;
// type F = <C as GenericConfig<D>>::F;
//
// let standard_config = CircuitConfig::standard_recursion_config();
//
// // An initial dummy proof.
// let (proof, vd, cd) = dummy_proof::<F, C, D>(&standard_config, 4_000)?;
// assert_eq!(cd.degree_bits, 12);
//
// // A standard recursive proof.
// let (proof, vd, cd) = recursive_proof(proof, vd, cd, &standard_config, None, false, false)?;
// assert_eq!(cd.degree_bits, 12);
//
// // A high-rate recursive proof, designed to be verifiable with fewer routed wires.
// let high_rate_config = CircuitConfig {
// fri_config: FriConfig {
// rate_bits: 7,
// proof_of_work_bits: 16,
// num_query_rounds: 12,
// ..standard_config.fri_config.clone()
// },
// ..standard_config
// };
// let (proof, vd, cd) =
// recursive_proof::<F, C, C, D>(proof, vd, cd, &high_rate_config, None, true, true)?;
// assert_eq!(cd.degree_bits, 12);
//
// // A final proof, optimized for size.
// let final_config = CircuitConfig {
// num_routed_wires: 37,
// fri_config: FriConfig {
// rate_bits: 8,
// cap_height: 0,
// proof_of_work_bits: 20,
// reduction_strategy: FriReductionStrategy::MinSize(None),
// num_query_rounds: 10,
// },
// ..high_rate_config
// };
// let (proof, _vd, cd) =
// recursive_proof::<F, KC, C, D>(proof, vd, cd, &final_config, None, true, true)?;
// assert_eq!(cd.degree_bits, 12, "final proof too large");
//
// test_serialization(&proof, &cd)?;
//
// Ok(())
// }
//
// #[test]
// fn test_recursive_verifier_multi_hash() -> Result<()> {
// init_logger();
// const D: usize = 2;
// type PC = PoseidonGoldilocksConfig;
// type KC = KeccakGoldilocksConfig;
// type F = <PC as GenericConfig<D>>::F;
//
// let config = CircuitConfig::standard_recursion_config();
// let (proof, vd, cd) = dummy_proof::<F, PC, D>(&config, 4_000)?;
//
// let (proof, vd, cd) =
// recursive_proof::<F, PC, PC, D>(proof, vd, cd, &config, None, false, false)?;
// test_serialization(&proof, &cd)?;
//
// let (proof, _vd, cd) =
// recursive_proof::<F, KC, PC, D>(proof, vd, cd, &config, None, false, false)?;
// test_serialization(&proof, &cd)?;
//
// Ok(())
// }
//
// /// Creates a dummy proof which should have roughly `num_dummy_gates` gates.
// fn dummy_proof<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>(
// config: &CircuitConfig,
// num_dummy_gates: u64,
// ) -> Result<(
// ProofWithPublicInputs<F, C, D>,
// VerifierOnlyCircuitData<C, D>,
// CommonCircuitData<F, C, D>,
// )> {
// let mut builder = CircuitBuilder::<F, D>::new(config.clone());
// for _ in 0..num_dummy_gates {
// builder.add_gate(NoopGate, vec![]);
// }
//
// let data = builder.build::<C>();
// let inputs = PartialWitness::new();
// let proof = data.prove(inputs)?;
// data.verify(proof.clone())?;
//
// Ok((proof, data.verifier_only, data.common))
// }
//
// fn recursive_proof<
// F: RichField + Extendable<D>,
// C: GenericConfig<D, F = F>,
// InnerC: GenericConfig<D, F = F>,
// const D: usize,
// >(
// inner_proof: StarkProofWithPublicInputs<F, InnerC, D>,
// config: &StarkConfig,
// print_gate_counts: bool,
// print_timing: bool,
// ) -> Result<(
// ProofWithPublicInputs<F, C, D>,
// VerifierOnlyCircuitData<C, D>,
// CommonCircuitData<F, C, D>,
// )>
// where
// InnerC::Hasher: AlgebraicHasher<F>,
// {
// let mut builder = CircuitBuilder::<F, D>::new(config.clone());
// let mut pw = PartialWitness::new();
// let degree_bits = inner_proof.proof.recover_degree_bits(config);
// let pt = add_virtual_stark_proof_with_pis(&mut builder, stark, config, degree_bits);
// pw.set_proof_with_pis_target(&pt, &inner_proof);
//
// let inner_data = VerifierCircuitTarget {
// constants_sigmas_cap: builder.add_virtual_cap(inner_cd.config.fri_config.cap_height),
// };
// pw.set_cap_target(
// &inner_data.constants_sigmas_cap,
// &inner_vd.constants_sigmas_cap,
// );
//
// builder.verify_proof(pt, &inner_data, &inner_cd);
//
// if print_gate_counts {
// builder.print_gate_counts(0);
// }
//
// if let Some(min_degree_bits) = min_degree_bits {
// // We don't want to pad all the way up to 2^min_degree_bits, as the builder will add a
// // few special gates afterward. So just pad to 2^(min_degree_bits - 1) + 1. Then the
// // builder will pad to the next power of two, 2^min_degree_bits.
// let min_gates = (1 << (min_degree_bits - 1)) + 1;
// for _ in builder.num_gates()..min_gates {
// builder.add_gate(NoopGate, vec![]);
// }
// }
//
// let data = builder.build::<C>();
//
// let mut timing = TimingTree::new("prove", Level::Debug);
// let proof = prove(&data.prover_only, &data.common, pw, &mut timing)?;
// if print_timing {
// timing.print();
// }
//
// data.verify(proof.clone())?;
//
// Ok((proof, data.verifier_only, data.common))
// }
//
// /// Test serialization and print some size info.
// fn test_serialization<
// F: RichField + Extendable<D>,
// C: GenericConfig<D, F = F>,
// const D: usize,
// >(
// proof: &ProofWithPublicInputs<F, C, D>,
// cd: &CommonCircuitData<F, C, D>,
// ) -> Result<()> {
// let proof_bytes = proof.to_bytes()?;
// info!("Proof length: {} bytes", proof_bytes.len());
// let proof_from_bytes = ProofWithPublicInputs::from_bytes(proof_bytes, cd)?;
// assert_eq!(proof, &proof_from_bytes);
//
// let now = std::time::Instant::now();
// let compressed_proof = proof.clone().compress(cd)?;
// let decompressed_compressed_proof = compressed_proof.clone().decompress(cd)?;
// info!("{:.4}s to compress proof", now.elapsed().as_secs_f64());
// assert_eq!(proof, &decompressed_compressed_proof);
//
// let compressed_proof_bytes = compressed_proof.to_bytes()?;
// info!(
// "Compressed proof length: {} bytes",
// compressed_proof_bytes.len()
// );
// let compressed_proof_from_bytes =
// CompressedProofWithPublicInputs::from_bytes(compressed_proof_bytes, cd)?;
// assert_eq!(compressed_proof, compressed_proof_from_bytes);
//
// Ok(())
// }
//
// fn init_logger() {
// let _ = env_logger::builder().format_timestamp(None).try_init();
// }
// }
#[cfg(test)]
mod tests {
use anyhow::Result;
use log::Level;
use plonky2::field::extension_field::Extendable;
use plonky2::field::field_types::Field;
use plonky2::hash::hash_types::RichField;
use plonky2::iop::witness::PartialWitness;
use plonky2::plonk::circuit_builder::CircuitBuilder;
use plonky2::plonk::circuit_data::{CircuitConfig, CommonCircuitData};
use plonky2::plonk::config::{AlgebraicHasher, GenericConfig, PoseidonGoldilocksConfig};
use plonky2::plonk::proof::ProofWithPublicInputs;
use plonky2::util::timing::TimingTree;
use plonky2_util::reverse_index_bits_in_place;
use crate::config::StarkConfig;
use crate::fibonacci_stark::FibonacciStark;
use crate::proof::StarkProofWithPublicInputs;
use crate::prover::prove;
use crate::recursive_verifier::{
add_virtual_stark_proof_with_pis, set_startk_proof_with_pis_target, verify_stark_proof,
};
use crate::stark::Stark;
use crate::stark_testing::test_stark_low_degree;
use crate::verifier::verify;
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(),
)?;
verify(stark, proof, &config)
}
#[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 config = StarkConfig::standard_fast_config();
let num_rows = 1 << 5;
let stark = S::new(num_rows);
test_stark_low_degree(stark)
}
#[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(),
)?;
recursive_proof::<F, C, S, C, D>(stark, proof, &config, true, true)
}
fn recursive_proof<
F: RichField + Extendable<D>,
C: GenericConfig<D, F = F>,
S: Stark<F, D>,
InnerC: GenericConfig<D, F = F>,
const D: usize,
>(
stark: S,
inner_proof: StarkProofWithPublicInputs<F, InnerC, D>,
inner_config: &StarkConfig,
print_gate_counts: bool,
print_timing: bool,
) -> Result<()>
where
InnerC::Hasher: AlgebraicHasher<F>,
[(); S::COLUMNS]:,
[(); S::PUBLIC_INPUTS]:,
{
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);
set_startk_proof_with_pis_target(&mut pw, &pt, &inner_proof);
verify_stark_proof::<F, InnerC, S, D>(&mut builder, stark, pt, inner_config);
if print_gate_counts {
builder.print_gate_counts(0);
}
let data = builder.build::<C>();
let mut timing = TimingTree::new("prove", Level::Debug);
let proof =
plonky2::plonk::prover::prove(&data.prover_only, &data.common, pw, &mut timing)?;
if print_timing {
timing.print();
}
data.verify(proof.clone())
}
fn init_logger() {
let _ = env_logger::builder().format_timestamp(None).try_init();
}
}

View File

@ -14,7 +14,7 @@ use crate::vars::StarkEvaluationVars;
/// Represents a STARK system.
// TODO: Add a `constraint_degree` fn that returns the maximum constraint degree.
pub trait Stark<F: RichField + Extendable<D>, const D: usize>: Sync {
pub trait Stark<F: RichField + Extendable<D>, const D: usize>: Sync + Copy {
/// The total number of columns in the trace.
const COLUMNS: usize;
/// The number of public inputs.