diff --git a/evm/src/all_stark.rs b/evm/src/all_stark.rs index 68b65d41..fd9ce01f 100644 --- a/evm/src/all_stark.rs +++ b/evm/src/all_stark.rs @@ -755,12 +755,13 @@ mod tests { let circuit_config = CircuitConfig::standard_recursion_config(); let mut builder = CircuitBuilder::::new(circuit_config); - let mut pw = PartialWitness::new(); - recursive_all_proof.verify_circuit(&mut builder, &mut pw); - - let data = builder.build::(); - let proof = data.prove(pw)?; - data.verify(proof) + // let mut pw = PartialWitness::new(); + recursive_all_proof.verify(inner_config) + // recursive_all_proof.verify_circuit(&mut builder, &mut pw); + // + // let data = builder.build::(); + // let proof = data.prove(pw)?; + // data.verify(proof) } fn init_logger() { diff --git a/evm/src/get_challenges.rs b/evm/src/get_challenges.rs index 8b254ee4..e39e3e23 100644 --- a/evm/src/get_challenges.rs +++ b/evm/src/get_challenges.rs @@ -229,6 +229,7 @@ impl StarkProofTarget { challenger.observe_cap(quotient_polys_cap); let stark_zeta = challenger.get_extension_challenge(builder); + dbg!(stark_zeta); challenger.observe_openings(&openings.to_fri_openings(builder.zero())); diff --git a/evm/src/permutation.rs b/evm/src/permutation.rs index 0bb8ab1d..b081c309 100644 --- a/evm/src/permutation.rs +++ b/evm/src/permutation.rs @@ -1,5 +1,7 @@ //! Permutation arguments. +use std::fmt::Debug; + use itertools::Itertools; use maybe_rayon::*; use plonky2::field::batch_util::batch_multiply_inplace; @@ -42,14 +44,14 @@ impl PermutationPair { } /// A single instance of a permutation check protocol. -pub(crate) struct PermutationInstance<'a, T: Copy> { +pub(crate) struct PermutationInstance<'a, T: Copy + Eq + PartialEq + Debug> { pub(crate) pair: &'a PermutationPair, pub(crate) challenge: GrandProductChallenge, } /// Randomness for a single instance of a permutation check protocol. -#[derive(Copy, Clone)] -pub(crate) struct GrandProductChallenge { +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +pub(crate) struct GrandProductChallenge { /// Randomness used to combine multiple columns into one. pub(crate) beta: T, /// Random offset that's added to the beta-reduced column values. @@ -92,8 +94,8 @@ impl GrandProductChallenge { } /// Like `PermutationChallenge`, but with `num_challenges` copies to boost soundness. -#[derive(Clone)] -pub(crate) struct GrandProductChallengeSet { +#[derive(Clone, Eq, PartialEq, Debug)] +pub(crate) struct GrandProductChallengeSet { pub(crate) challenges: Vec>, } @@ -261,7 +263,7 @@ pub(crate) fn get_n_grand_product_challenge_sets_target< /// Before batching, each permutation pair leads to `num_challenges` permutation arguments, so we /// start with the cartesian product of `permutation_pairs` and `0..num_challenges`. Then we /// chunk these arguments based on our batch size. -pub(crate) fn get_permutation_batches<'a, T: Copy>( +pub(crate) fn get_permutation_batches<'a, T: Copy + Eq + PartialEq + Debug>( permutation_pairs: &'a [PermutationPair], permutation_challenge_sets: &[GrandProductChallengeSet], num_challenges: usize, diff --git a/evm/src/recursive_verifier.rs b/evm/src/recursive_verifier.rs index 4f08a053..fca634a7 100644 --- a/evm/src/recursive_verifier.rs +++ b/evm/src/recursive_verifier.rs @@ -1,16 +1,19 @@ -use anyhow::Result; +use anyhow::{ensure, Result}; use itertools::Itertools; use plonky2::field::extension::Extendable; use plonky2::field::types::Field; use plonky2::fri::witness_util::set_fri_proof_target; -use plonky2::hash::hash_types::RichField; +use plonky2::hash::hash_types::{HashOut, RichField}; use plonky2::hash::hashing::SPONGE_WIDTH; -use plonky2::iop::challenger::RecursiveChallenger; +use plonky2::hash::merkle_tree::MerkleCap; +use plonky2::hash::poseidon::PoseidonHash; +use plonky2::iop::challenger::{Challenger, RecursiveChallenger}; use plonky2::iop::ext_target::ExtensionTarget; use plonky2::iop::target::Target; use plonky2::iop::witness::{PartialWitness, Witness}; use plonky2::plonk::circuit_builder::CircuitBuilder; use plonky2::plonk::circuit_data::{CircuitConfig, VerifierCircuitData, VerifierCircuitTarget}; +use plonky2::plonk::config::GenericHashOut; use plonky2::plonk::config::Hasher; use plonky2::plonk::config::{AlgebraicHasher, GenericConfig}; use plonky2::plonk::proof::ProofWithPublicInputs; @@ -29,7 +32,8 @@ use crate::keccak_memory::keccak_memory_stark::KeccakMemoryStark; use crate::logic::LogicStark; use crate::memory::memory_stark::MemoryStark; use crate::permutation::{ - GrandProductChallenge, GrandProductChallengeSet, PermutationCheckDataTarget, + get_grand_product_challenge_set, GrandProductChallenge, GrandProductChallengeSet, + PermutationCheckDataTarget, }; use crate::proof::{ AllChallengerState, AllProof, AllProofChallengesTarget, AllProofTarget, BlockMetadata, @@ -52,14 +56,88 @@ pub struct RecursiveAllProof< [(ProofWithPublicInputs, VerifierCircuitData); NUM_TABLES], } +struct PublicInputs, C: GenericConfig, const D: usize> { + trace_cap: MerkleCap, + ctl_zs_last: Vec, + ctl_challenges: GrandProductChallengeSet, + challenger_state_before: [F; SPONGE_WIDTH], + challenger_state_after: [F; SPONGE_WIDTH], +} + +impl, C: GenericConfig, const D: usize> + PublicInputs +{ + fn from_vec(v: &[F], config: &StarkConfig) -> Self { + let mut start = 0; + let trace_cap = MerkleCap( + v[start..4 * (1 << config.fri_config.cap_height)] + .chunks(4) + .map(|chunk| >::Hash::from_vec(chunk)) + .collect(), + ); + start += 4 * (1 << config.fri_config.cap_height); + let ctl_challenges = GrandProductChallengeSet { + challenges: (0..config.num_challenges) + .map(|i| GrandProductChallenge { + beta: v[start + 2 * i], + gamma: v[start + 2 * i + 1], + }) + .collect(), + }; + start += 2 * config.num_challenges; + let challenger_state_before = v[start..start + SPONGE_WIDTH].try_into().unwrap(); + let challenger_state_after = v[start + SPONGE_WIDTH..start + 2 * SPONGE_WIDTH] + .try_into() + .unwrap(); + + start += 2 * SPONGE_WIDTH; + let ctl_zs_last = v[start..].to_vec(); + + Self { + trace_cap, + ctl_zs_last, + ctl_challenges, + challenger_state_before, + challenger_state_after, + } + } +} + impl, C: GenericConfig, const D: usize> RecursiveAllProof { /// Verify every recursive proof. - pub fn verify(self) -> Result<()> + pub fn verify(self, inner_config: &StarkConfig) -> Result<()> where [(); C::Hasher::HASH_SIZE]:, { + let pis: [_; NUM_TABLES] = std::array::from_fn(|i| { + PublicInputs::::from_vec( + &self.recursive_proofs[i].0.public_inputs, + inner_config, + ) + }); + + let mut challenger = Challenger::::new(); + for pi in &pis { + challenger.observe_cap(&pi.trace_cap); + } + let ctl_challenges = + get_grand_product_challenge_set(&mut challenger, inner_config.num_challenges); + for pi in &pis { + ensure!(ctl_challenges == pi.ctl_challenges); + } + challenger.duplexing(); + let state = challenger.state(); + ensure!(state == pis[0].challenger_state_before); + for i in 1..NUM_TABLES { + dbg!(i); + dbg!( + pis[i].challenger_state_before, + pis[i - 1].challenger_state_after + ); + ensure!(pis[i].challenger_state_before == pis[i - 1].challenger_state_after); + } for (proof, verifier_data) in self.recursive_proofs { verifier_data.verify(proof)?; } @@ -124,6 +202,14 @@ where proof.num_ctl_zs(), ); set_stark_proof_target(&mut pw, &proof_target, proof, builder.zero()); + builder.register_public_inputs( + &proof_target + .trace_cap + .0 + .iter() + .flat_map(|h| h.elements) + .collect::>(), + ); let ctl_challenges_target = GrandProductChallengeSet { challenges: (0..inner_config.num_challenges) @@ -166,6 +252,8 @@ where let challenger_state = challenger.state(); builder.register_public_inputs(&challenger_state); + builder.register_public_inputs(&proof_target.openings.ctl_zs_last); + verify_stark_proof_with_challenges_circuit::( &mut builder, &stark, diff --git a/plonky2/src/hash/hash_types.rs b/plonky2/src/hash/hash_types.rs index 14303ad3..955f539a 100644 --- a/plonky2/src/hash/hash_types.rs +++ b/plonky2/src/hash/hash_types.rs @@ -78,6 +78,12 @@ impl GenericHashOut for HashOut { fn to_vec(&self) -> Vec { self.elements.to_vec() } + + fn from_vec(v: &[F]) -> Self { + Self { + elements: v.try_into().unwrap(), + } + } } impl Default for HashOut { @@ -148,6 +154,10 @@ impl GenericHashOut for BytesHash { }) .collect() } + + fn from_vec(_v: &[F]) -> Self { + todo!() + } } impl Serialize for BytesHash { diff --git a/plonky2/src/iop/challenger.rs b/plonky2/src/iop/challenger.rs index 4e83b1c6..1a9e3f13 100644 --- a/plonky2/src/iop/challenger.rs +++ b/plonky2/src/iop/challenger.rs @@ -287,16 +287,20 @@ impl, H: AlgebraicHasher, const D: usize> } pub fn duplexing(&mut self, builder: &mut CircuitBuilder) { - for input_chunk in self.input_buffer.chunks(SPONGE_RATE) { - // Overwrite the first r elements with the inputs. This differs from a standard sponge, - // where we would xor or add in the inputs. This is a well-known variant, though, - // sometimes called "overwrite mode". - for (i, &input) in input_chunk.iter().enumerate() { - self.sponge_state[i] = input; - } - - // Apply the permutation. + if self.input_buffer.is_empty() { self.sponge_state = builder.permute::(self.sponge_state); + } else { + for input_chunk in self.input_buffer.chunks(SPONGE_RATE) { + // Overwrite the first r elements with the inputs. This differs from a standard sponge, + // where we would xor or add in the inputs. This is a well-known variant, though, + // sometimes called "overwrite mode". + for (i, &input) in input_chunk.iter().enumerate() { + self.sponge_state[i] = input; + } + + // Apply the permutation. + self.sponge_state = builder.permute::(self.sponge_state); + } } self.output_buffer = self.sponge_state[0..SPONGE_RATE].to_vec(); diff --git a/plonky2/src/plonk/config.rs b/plonky2/src/plonk/config.rs index f55bad1f..51dacdde 100644 --- a/plonky2/src/plonk/config.rs +++ b/plonky2/src/plonk/config.rs @@ -20,6 +20,7 @@ pub trait GenericHashOut: fn from_bytes(bytes: &[u8]) -> Self; fn to_vec(&self) -> Vec; + fn from_vec(v: &[F]) -> Self; } /// Trait for hash functions.