From 54b17c1e9378256036d350c6bec6e2b779d72ccf Mon Sep 17 00:00:00 2001 From: Sai <135601871+sai-deng@users.noreply.github.com> Date: Wed, 17 Jul 2024 14:25:42 +0800 Subject: [PATCH] Sync upstream (#109) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Linda Guiga <101227802+LindaGuiga@users.noreply.github.com> Co-authored-by: Hamy Ratoanina Co-authored-by: Daniel-Aaron-Bloom <76709210+Daniel-Aaron-Bloom@users.noreply.github.com> Co-authored-by: nuno <41233686+qope@users.noreply.github.com> Co-authored-by: Matthias Görgens Co-authored-by: Robin Salen <30937548+Nashtare@users.noreply.github.com> Co-authored-by: Gio <102917377+gio256@users.noreply.github.com> Co-authored-by: Robin Salen --- .github/workflows/pr_checking.yml | 56 +++++++++++++++++++ field/src/lib.rs | 2 +- field/src/types.rs | 48 +++++++++++++++- plonky2/Cargo.toml | 2 +- ...ld_merkle_tree.rs => batch_merkle_tree.rs} | 14 ++--- plonky2/benches/ffts.rs | 4 +- plonky2/benches/merkle.rs | 2 +- plonky2/src/batch_fri/mod.rs | 4 ++ .../batch_oracle.rs => batch_fri/oracle.rs} | 28 +++++----- .../batch_prover.rs => batch_fri/prover.rs} | 38 ++++++++----- .../recursive_verifier.rs} | 4 +- .../verifier.rs} | 6 +- plonky2/src/fri/mod.rs | 6 +- plonky2/src/fri/structure.rs | 4 +- plonky2/src/fri/validate_shape.rs | 48 +--------------- plonky2/src/gates/selectors.rs | 2 + .../x86_64/poseidon_goldilocks_avx2_bmi2.rs | 2 +- ...ld_merkle_tree.rs => batch_merkle_tree.rs} | 49 ++++++++-------- plonky2/src/hash/merkle_proofs.rs | 34 ++++------- plonky2/src/hash/merkle_tree.rs | 2 +- plonky2/src/hash/mod.rs | 2 +- plonky2/src/lib.rs | 1 + plonky2/src/plonk/circuit_builder.rs | 8 +++ plonky2/src/plonk/vanishing_poly.rs | 1 + .../conditional_recursive_verifier.rs | 31 ++++++---- plonky2/src/recursion/dummy_circuit.rs | 26 +++++---- starky/README.md | 14 +++++ starky/src/cross_table_lookup.rs | 36 +++++++----- starky/src/get_challenges.rs | 2 + starky/src/lookup.rs | 18 ++---- starky/src/prover.rs | 20 ++++--- starky/src/recursive_verifier.rs | 6 +- 32 files changed, 314 insertions(+), 206 deletions(-) create mode 100644 .github/workflows/pr_checking.yml rename plonky2/benches/{field_merkle_tree.rs => batch_merkle_tree.rs} (73%) create mode 100644 plonky2/src/batch_fri/mod.rs rename plonky2/src/{fri/batch_oracle.rs => batch_fri/oracle.rs} (96%) rename plonky2/src/{fri/batch_prover.rs => batch_fri/prover.rs} (92%) rename plonky2/src/{fri/batch_recursive_verifier.rs => batch_fri/recursive_verifier.rs} (99%) rename plonky2/src/{fri/batch_verifier.rs => batch_fri/verifier.rs} (98%) rename plonky2/src/hash/{field_merkle_tree.rs => batch_merkle_tree.rs} (87%) diff --git a/.github/workflows/pr_checking.yml b/.github/workflows/pr_checking.yml new file mode 100644 index 0000000000..d155461067 --- /dev/null +++ b/.github/workflows/pr_checking.yml @@ -0,0 +1,56 @@ +name: PR check + +on: + pull_request: + types: [opened, reopened, synchronize] + +permissions: + pull-requests: write + +jobs: + pr_check: + name: Validate PR + runs-on: ubuntu-latest + steps: + - name: Set up keywords + id: setup_keywords + run: echo "RESTRICTED_KEYWORDS=$(echo '${{ secrets.RESTRICTED_KEYWORDS }}' | jq -r '.[]' | tr '\n' ' ')" >> $GITHUB_ENV + + - name: Check for spam PR + id: check + run: | + # Initialize variables to track spam presence + title_is_spam=false + description_is_spam=false + + # Check title for spam + for keyword in $RESTRICTED_KEYWORDS; do + if echo "${{ github.event.pull_request.title }}" | grep -i -q "$keyword"; then + title_is_spam=true + break + fi + done + + # Check description for spam + for keyword in $RESTRICTED_KEYWORDS; do + if echo "${{ github.event.pull_request.body }}" | grep -i -q "$keyword"; then + description_is_spam=true + break + fi + done + + # Set the output based on the presence of spam + if [ "$title_is_spam" = true ] || [ "$description_is_spam" = true ]; then + echo "is_spam=true" >> $GITHUB_ENV + else + echo "is_spam=false" >> $GITHUB_ENV + fi + + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Close PR if spam are found and author is not a contributor or member + if: ${{ env.is_spam == 'true' && github.event.pull_request.author_association == 'FIRST_TIME_CONTRIBUTOR' }} + run: gh pr close ${{ github.event.pull_request.number }} --comment "Spam detected" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/field/src/lib.rs b/field/src/lib.rs index e21f042428..c713db8851 100644 --- a/field/src/lib.rs +++ b/field/src/lib.rs @@ -6,7 +6,7 @@ #![feature(specialization)] #![cfg_attr(target_arch = "x86_64", feature(stdarch_x86_avx512))] #![cfg_attr(not(test), no_std)] -#![cfg(not(test))] + extern crate alloc; pub(crate) mod arch; diff --git a/field/src/types.rs b/field/src/types.rs index c744f3a675..53b3d1e81f 100644 --- a/field/src/types.rs +++ b/field/src/types.rs @@ -427,9 +427,13 @@ pub trait Field: } fn powers(&self) -> Powers { + self.shifted_powers(Self::ONE) + } + + fn shifted_powers(&self, start: Self) -> Powers { Powers { base: *self, - current: Self::ONE, + current: start, } } @@ -571,6 +575,7 @@ pub trait PrimeField64: PrimeField + Field64 { } /// An iterator over the powers of a certain base element `b`: `b^0, b^1, b^2, ...`. +#[must_use = "iterators are lazy and do nothing unless consumed"] #[derive(Clone, Debug)] pub struct Powers { base: F, @@ -585,6 +590,24 @@ impl Iterator for Powers { self.current *= self.base; Some(result) } + + fn size_hint(&self) -> (usize, Option) { + (usize::MAX, None) + } + + fn nth(&mut self, n: usize) -> Option { + let result = self.current * self.base.exp_u64(n.try_into().unwrap()); + self.current = result * self.base; + Some(result) + } + + fn last(self) -> Option { + panic!("called `Iterator::last()` on an infinite sequence") + } + + fn count(self) -> usize { + panic!("called `Iterator::count()` on an infinite sequence") + } } impl Powers { @@ -600,3 +623,26 @@ impl Powers { } } } + +#[cfg(test)] +mod tests { + use super::Field; + use crate::goldilocks_field::GoldilocksField; + + #[test] + fn test_powers_nth() { + type F = GoldilocksField; + + const N: usize = 10; + let powers_of_two: Vec = F::TWO.powers().take(N).collect(); + + for (n, &expect) in powers_of_two.iter().enumerate() { + let mut iter = F::TWO.powers(); + assert_eq!(iter.nth(n), Some(expect)); + + for &expect_next in &powers_of_two[n + 1..] { + assert_eq!(iter.next(), Some(expect_next)); + } + } + } +} diff --git a/plonky2/Cargo.toml b/plonky2/Cargo.toml index eba775e27f..8b45db282b 100644 --- a/plonky2/Cargo.toml +++ b/plonky2/Cargo.toml @@ -65,7 +65,7 @@ name = "field_arithmetic" harness = false [[bench]] -name = "field_merkle_tree" +name = "batch_merkle_tree" harness = false [[bench]] diff --git a/plonky2/benches/field_merkle_tree.rs b/plonky2/benches/batch_merkle_tree.rs similarity index 73% rename from plonky2/benches/field_merkle_tree.rs rename to plonky2/benches/batch_merkle_tree.rs index dfaa301321..87c0ac940e 100644 --- a/plonky2/benches/field_merkle_tree.rs +++ b/plonky2/benches/batch_merkle_tree.rs @@ -2,7 +2,7 @@ mod allocator; use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion}; use plonky2::field::goldilocks_field::GoldilocksField; -use plonky2::hash::field_merkle_tree::FieldMerkleTree; +use plonky2::hash::batch_merkle_tree::BatchMerkleTree; use plonky2::hash::hash_types::RichField; use plonky2::hash::keccak::KeccakHash; use plonky2::hash::poseidon::PoseidonHash; @@ -13,9 +13,9 @@ const ELEMS_PER_LEAF_1: usize = 70; const ELEMS_PER_LEAF_2: usize = 5; const ELEMS_PER_LEAF_3: usize = 100; -pub(crate) fn bench_field_merkle_tree>(c: &mut Criterion) { - let mut group = c.benchmark_group(&format!( - "field-merkle-tree<{}, {}>", +pub(crate) fn bench_batch_merkle_tree>(c: &mut Criterion) { + let mut group = c.benchmark_group(format!( + "batch-merkle-tree<{}, {}>", type_name::(), type_name::() )); @@ -29,14 +29,14 @@ pub(crate) fn bench_field_merkle_tree>(c: &mut Criter vec![F::rand_vec(ELEMS_PER_LEAF_2); size >> 1], vec![F::rand_vec(ELEMS_PER_LEAF_3); size >> 2], ]; - b.iter(|| FieldMerkleTree::::new(black_box(leaves.clone()), black_box(5))); + b.iter(|| BatchMerkleTree::::new(black_box(leaves.clone()), black_box(5))); }); } } fn criterion_benchmark(c: &mut Criterion) { - bench_field_merkle_tree::(c); - bench_field_merkle_tree::>(c); + bench_batch_merkle_tree::(c); + bench_batch_merkle_tree::>(c); } criterion_group!(benches, criterion_benchmark); diff --git a/plonky2/benches/ffts.rs b/plonky2/benches/ffts.rs index 4149c02d46..54e0a7396a 100644 --- a/plonky2/benches/ffts.rs +++ b/plonky2/benches/ffts.rs @@ -7,7 +7,7 @@ use plonky2::field::types::Field; use tynm::type_name; pub(crate) fn bench_ffts(c: &mut Criterion) { - let mut group = c.benchmark_group(&format!("fft<{}>", type_name::())); + let mut group = c.benchmark_group(format!("fft<{}>", type_name::())); for size_log in [13, 14, 15, 16] { let size = 1 << size_log; @@ -21,7 +21,7 @@ pub(crate) fn bench_ffts(c: &mut Criterion) { pub(crate) fn bench_ldes(c: &mut Criterion) { const RATE_BITS: usize = 3; - let mut group = c.benchmark_group(&format!("lde<{}>", type_name::())); + let mut group = c.benchmark_group(format!("lde<{}>", type_name::())); for size_log in [13, 14, 15, 16] { let orig_size = 1 << (size_log - RATE_BITS); diff --git a/plonky2/benches/merkle.rs b/plonky2/benches/merkle.rs index f9bae127fa..6230c13436 100644 --- a/plonky2/benches/merkle.rs +++ b/plonky2/benches/merkle.rs @@ -12,7 +12,7 @@ use tynm::type_name; const ELEMS_PER_LEAF: usize = 135; pub(crate) fn bench_merkle_tree>(c: &mut Criterion) { - let mut group = c.benchmark_group(&format!( + let mut group = c.benchmark_group(format!( "merkle-tree<{}, {}>", type_name::(), type_name::() diff --git a/plonky2/src/batch_fri/mod.rs b/plonky2/src/batch_fri/mod.rs new file mode 100644 index 0000000000..8d7cc5e1f5 --- /dev/null +++ b/plonky2/src/batch_fri/mod.rs @@ -0,0 +1,4 @@ +pub mod oracle; +pub mod prover; +pub mod recursive_verifier; +pub mod verifier; diff --git a/plonky2/src/fri/batch_oracle.rs b/plonky2/src/batch_fri/oracle.rs similarity index 96% rename from plonky2/src/fri/batch_oracle.rs rename to plonky2/src/batch_fri/oracle.rs index 1be347a653..71d808ed9b 100644 --- a/plonky2/src/fri/batch_oracle.rs +++ b/plonky2/src/batch_fri/oracle.rs @@ -10,12 +10,12 @@ use plonky2_field::types::Field; use plonky2_maybe_rayon::*; use plonky2_util::{log2_strict, reverse_index_bits_in_place}; -use crate::fri::batch_prover::batch_fri_proof; +use crate::batch_fri::prover::batch_fri_proof; use crate::fri::oracle::PolynomialBatch; use crate::fri::proof::FriProof; use crate::fri::structure::{FriBatchInfo, FriInstanceInfo}; use crate::fri::FriParams; -use crate::hash::field_merkle_tree::FieldMerkleTree; +use crate::hash::batch_merkle_tree::BatchMerkleTree; use crate::hash::hash_types::RichField; use crate::iop::challenger::Challenger; use crate::plonk::config::GenericConfig; @@ -25,12 +25,12 @@ use crate::util::timing::TimingTree; use crate::util::{reverse_bits, transpose}; /// Represents a batch FRI oracle, i.e. a batch of polynomials with different degrees which have -/// been Merkle-ized in a Field Merkle Tree. +/// been Merkle-ized in a [`BatchMerkleTree`]. #[derive(Eq, PartialEq, Debug)] pub struct BatchFriOracle, C: GenericConfig, const D: usize> { pub polynomials: Vec>, - pub field_merkle_tree: FieldMerkleTree, + pub batch_merkle_tree: BatchMerkleTree, // The degree bits of each polynomial group. pub degree_bits: Vec, pub rate_bits: usize, @@ -105,19 +105,19 @@ impl, C: GenericConfig, const D: usize> } } - let field_merkle_tree = timed!( + let batch_merkle_tree = timed!( timing, "build Field Merkle tree", - FieldMerkleTree::new(leaves, cap_height) + BatchMerkleTree::new(leaves, cap_height) ); degree_bits.sort_unstable(); degree_bits.dedup(); degree_bits.reverse(); - assert_eq!(field_merkle_tree.leaves.len(), degree_bits.len()); + assert_eq!(batch_merkle_tree.leaves.len(), degree_bits.len()); Self { polynomials, - field_merkle_tree, + batch_merkle_tree, degree_bits, rate_bits, blinding, @@ -181,7 +181,7 @@ impl, C: GenericConfig, const D: usize> batch_fri_proof::( &oracles .iter() - .map(|o| &o.field_merkle_tree) + .map(|o| &o.batch_merkle_tree) .collect::>(), final_lde_polynomial_coeff[0].clone(), &final_lde_polynomial_values, @@ -202,7 +202,7 @@ impl, C: GenericConfig, const D: usize> ) -> &[F] { let index = index * step; let index = reverse_bits(index, self.degree_bits[degree_bits_index] + self.rate_bits); - let slice = &self.field_merkle_tree.leaves[degree_bits_index][index]; + let slice = &self.batch_merkle_tree.leaves[degree_bits_index][index]; &slice[slice_start..slice_start + slice_len] } @@ -257,8 +257,8 @@ mod test { use plonky2_field::types::Sample; use super::*; - use crate::fri::batch_oracle::BatchFriOracle; - use crate::fri::batch_verifier::verify_batch_fri_proof; + use crate::batch_fri::oracle::BatchFriOracle; + use crate::batch_fri::verifier::verify_batch_fri_proof; use crate::fri::reduction_strategies::FriReductionStrategy; use crate::fri::structure::{ FriBatchInfo, FriBatchInfoTarget, FriInstanceInfo, FriInstanceInfoTarget, FriOpeningBatch, @@ -323,7 +323,7 @@ mod test { ); let mut challenger = Challenger::::new(); - challenger.observe_cap(&trace_oracle.field_merkle_tree.cap); + challenger.observe_cap(&trace_oracle.batch_merkle_tree.cap); let zeta = challenger.get_extension_challenge::(); let eta = challenger.get_extension_challenge::(); let poly0 = &trace_oracle.polynomials[0]; @@ -452,7 +452,7 @@ mod test { &fri_params.config, ); let degree_bits = [k0, k1, k2]; - let merkle_cap = trace_oracle.field_merkle_tree.cap; + let merkle_cap = trace_oracle.batch_merkle_tree.cap; verify_batch_fri_proof::( °ree_bits, &fri_instances, diff --git a/plonky2/src/fri/batch_prover.rs b/plonky2/src/batch_fri/prover.rs similarity index 92% rename from plonky2/src/fri/batch_prover.rs rename to plonky2/src/batch_fri/prover.rs index bf41dff03b..770c2c2285 100644 --- a/plonky2/src/fri/batch_prover.rs +++ b/plonky2/src/batch_fri/prover.rs @@ -5,14 +5,14 @@ use plonky2_field::extension::flatten; #[allow(unused_imports)] use plonky2_field::types::Field; use plonky2_maybe_rayon::*; -use plonky2_util::reverse_index_bits_in_place; +use plonky2_util::{log2_strict, reverse_index_bits_in_place}; use crate::field::extension::{unflatten, Extendable}; use crate::field::polynomial::{PolynomialCoeffs, PolynomialValues}; use crate::fri::proof::{FriInitialTreeProof, FriProof, FriQueryRound, FriQueryStep}; use crate::fri::prover::{fri_proof_of_work, FriCommitedTrees}; use crate::fri::FriParams; -use crate::hash::field_merkle_tree::FieldMerkleTree; +use crate::hash::batch_merkle_tree::BatchMerkleTree; use crate::hash::hash_types::RichField; use crate::hash::merkle_tree::MerkleTree; use crate::iop::challenger::Challenger; @@ -23,7 +23,7 @@ use crate::util::timing::TimingTree; /// Builds a batch FRI proof. pub fn batch_fri_proof, C: GenericConfig, const D: usize>( - initial_merkle_trees: &[&FieldMerkleTree], + initial_merkle_trees: &[&BatchMerkleTree], lde_polynomial_coeffs: PolynomialCoeffs, lde_polynomial_values: &[PolynomialValues], challenger: &mut Challenger, @@ -36,6 +36,18 @@ pub fn batch_fri_proof, C: GenericConfig, assert!(lde_polynomial_values .windows(2) .all(|pair| { pair[0].len() > pair[1].len() })); + // Check that reduction_arity_bits covers all polynomials + let mut cur_n = log2_strict(n); + let mut cur_poly_index = 1; + for arity_bits in &fri_params.reduction_arity_bits { + cur_n -= arity_bits; + if cur_poly_index < lde_polynomial_values.len() + && cur_n == log2_strict(lde_polynomial_values[cur_poly_index].len()) + { + cur_poly_index += 1; + } + } + assert_eq!(cur_poly_index, lde_polynomial_values.len()); // Commit phase let (trees, final_coeffs) = timed!( @@ -139,7 +151,7 @@ fn batch_fri_prover_query_rounds< C: GenericConfig, const D: usize, >( - initial_merkle_trees: &[&FieldMerkleTree], + initial_merkle_trees: &[&BatchMerkleTree], trees: &[MerkleTree], challenger: &mut Challenger, n: usize, @@ -165,7 +177,7 @@ fn batch_fri_prover_query_round< C: GenericConfig, const D: usize, >( - initial_merkle_trees: &[&FieldMerkleTree], + initial_merkle_trees: &[&BatchMerkleTree], trees: &[MerkleTree], mut x_index: usize, fri_params: &FriParams, @@ -215,8 +227,8 @@ mod tests { use plonky2_field::types::{Field64, Sample}; use super::*; - use crate::fri::batch_oracle::BatchFriOracle; - use crate::fri::batch_verifier::verify_batch_fri_proof; + use crate::batch_fri::oracle::BatchFriOracle; + use crate::batch_fri::verifier::verify_batch_fri_proof; use crate::fri::reduction_strategies::FriReductionStrategy; use crate::fri::structure::{ FriBatchInfo, FriInstanceInfo, FriOpeningBatch, FriOpenings, FriOracleInfo, @@ -263,7 +275,7 @@ mod tests { ); let poly = &polynomial_batch.polynomials[0]; let mut challenger = Challenger::::new(); - challenger.observe_cap(&polynomial_batch.field_merkle_tree.cap); + challenger.observe_cap(&polynomial_batch.batch_merkle_tree.cap); let _alphas = challenger.get_n_challenges(2); let zeta = challenger.get_extension_challenge::(); challenger.observe_extension_element::(&poly.to_extension::().eval(zeta)); @@ -292,7 +304,7 @@ mod tests { let lde_final_values = lde_final_poly.coset_fft(F::coset_shift().into()); let proof = batch_fri_proof::( - &[&polynomial_batch.field_merkle_tree], + &[&polynomial_batch.batch_merkle_tree], lde_final_poly, &[lde_final_values], &mut challenger, @@ -318,7 +330,7 @@ mod tests { batches: vec![fri_opening_batch], }], &fri_challenges, - &[polynomial_batch.field_merkle_tree.cap], + &[polynomial_batch.batch_merkle_tree.cap], &proof, &fri_params, ) @@ -362,7 +374,7 @@ mod tests { ); let mut challenger = Challenger::::new(); - challenger.observe_cap(&trace_oracle.field_merkle_tree.cap); + challenger.observe_cap(&trace_oracle.batch_merkle_tree.cap); let _alphas = challenger.get_n_challenges(2); let zeta = challenger.get_extension_challenge::(); let poly0 = &trace_oracle.polynomials[0]; @@ -394,7 +406,7 @@ mod tests { let lde_final_values_2 = lde_final_poly_2.coset_fft(F::coset_shift().into()); let proof = batch_fri_proof::( - &[&trace_oracle.field_merkle_tree], + &[&trace_oracle.batch_merkle_tree], lde_final_poly_0, &[lde_final_values_0, lde_final_values_1, lde_final_values_2], &mut challenger, @@ -455,7 +467,7 @@ mod tests { &fri_instances, &fri_openings, &fri_challenges, - &[trace_oracle.field_merkle_tree.cap], + &[trace_oracle.batch_merkle_tree.cap], &proof, &fri_params, ) diff --git a/plonky2/src/fri/batch_recursive_verifier.rs b/plonky2/src/batch_fri/recursive_verifier.rs similarity index 99% rename from plonky2/src/fri/batch_recursive_verifier.rs rename to plonky2/src/batch_fri/recursive_verifier.rs index 90e7259fc9..95731ba403 100644 --- a/plonky2/src/fri/batch_recursive_verifier.rs +++ b/plonky2/src/batch_fri/recursive_verifier.rs @@ -55,7 +55,7 @@ impl, const D: usize> CircuitBuilder { ); let mut precomputed_reduced_evals = Vec::with_capacity(openings.len()); - for opn in openings.iter() { + for opn in openings { let pre = with_context!( self, "precompute reduced evaluations", @@ -132,7 +132,7 @@ impl, const D: usize> CircuitBuilder { with_context!( self, &format!("verify {i}'th initial Merkle proof"), - self.verify_field_merkle_proof_to_cap_with_cap_index::( + self.verify_batch_merkle_proof_to_cap_with_cap_index::( &leaves, degree_bits, x_index_bits, diff --git a/plonky2/src/fri/batch_verifier.rs b/plonky2/src/batch_fri/verifier.rs similarity index 98% rename from plonky2/src/fri/batch_verifier.rs rename to plonky2/src/batch_fri/verifier.rs index e978572d35..b7dd552b70 100644 --- a/plonky2/src/fri/batch_verifier.rs +++ b/plonky2/src/batch_fri/verifier.rs @@ -14,7 +14,7 @@ use crate::fri::verifier::{ }; use crate::fri::FriParams; use crate::hash::hash_types::RichField; -use crate::hash::merkle_proofs::{verify_field_merkle_proof_to_cap, verify_merkle_proof_to_cap}; +use crate::hash::merkle_proofs::{verify_batch_merkle_proof_to_cap, verify_merkle_proof_to_cap}; use crate::hash::merkle_tree::MerkleCap; use crate::plonk::config::{GenericConfig, Hasher}; use crate::util::reducing::ReducingFactor; @@ -45,7 +45,7 @@ pub fn verify_batch_fri_proof< ); let mut precomputed_reduced_evals = Vec::with_capacity(openings.len()); - for opn in openings.iter() { + for opn in openings { let pre = PrecomputedReducedOpenings::from_os_and_alpha(opn, challenges.fri_alpha); precomputed_reduced_evals.push(pre); } @@ -99,7 +99,7 @@ fn batch_fri_verify_initial_proof, H: Hasher, co }) .collect::>(); - verify_field_merkle_proof_to_cap::(&leaves, degree_bits, x_index, cap, merkle_proof)?; + verify_batch_merkle_proof_to_cap::(&leaves, degree_bits, x_index, cap, merkle_proof)?; } Ok(()) diff --git a/plonky2/src/fri/mod.rs b/plonky2/src/fri/mod.rs index 85ef65a29c..5f18600c3c 100644 --- a/plonky2/src/fri/mod.rs +++ b/plonky2/src/fri/mod.rs @@ -10,10 +10,6 @@ use serde::Serialize; use crate::fri::reduction_strategies::FriReductionStrategy; -pub mod batch_oracle; -pub mod batch_prover; -pub mod batch_recursive_verifier; -pub mod batch_verifier; mod challenges; pub mod oracle; pub mod proof; @@ -21,7 +17,7 @@ pub mod prover; pub mod recursive_verifier; pub mod reduction_strategies; pub mod structure; -mod validate_shape; +pub(crate) mod validate_shape; pub mod verifier; pub mod witness_util; diff --git a/plonky2/src/fri/structure.rs b/plonky2/src/fri/structure.rs index d066ca2b54..664badb688 100644 --- a/plonky2/src/fri/structure.rs +++ b/plonky2/src/fri/structure.rs @@ -10,7 +10,7 @@ use crate::hash::hash_types::RichField; use crate::iop::ext_target::ExtensionTarget; /// Describes an instance of a FRI-based batch opening. -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct FriInstanceInfo, const D: usize> { /// The oracles involved, not counting oracles created during the commit phase. pub oracles: Vec, @@ -34,7 +34,7 @@ pub struct FriOracleInfo { } /// A batch of openings at a particular point. -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct FriBatchInfo, const D: usize> { pub point: F::Extension, pub polynomials: Vec, diff --git a/plonky2/src/fri/validate_shape.rs b/plonky2/src/fri/validate_shape.rs index 6d7554ffc3..be675ed61b 100644 --- a/plonky2/src/fri/validate_shape.rs +++ b/plonky2/src/fri/validate_shape.rs @@ -20,53 +20,7 @@ where F: RichField + Extendable, C: GenericConfig, { - let FriProof { - commit_phase_merkle_caps, - query_round_proofs, - final_poly, - pow_witness: _pow_witness, - } = proof; - - let cap_height = params.config.cap_height; - for cap in commit_phase_merkle_caps { - ensure!(cap.height() == cap_height); - } - - for query_round in query_round_proofs { - let FriQueryRound { - initial_trees_proof, - steps, - } = query_round; - - ensure!(initial_trees_proof.evals_proofs.len() == instance.oracles.len()); - for ((leaf, merkle_proof), oracle) in initial_trees_proof - .evals_proofs - .iter() - .zip(&instance.oracles) - { - ensure!(leaf.len() == oracle.num_polys + salt_size(oracle.blinding && params.hiding)); - ensure!(merkle_proof.len() + cap_height == params.lde_bits()); - } - - ensure!(steps.len() == params.reduction_arity_bits.len()); - let mut codeword_len_bits = params.lde_bits(); - for (step, arity_bits) in steps.iter().zip(¶ms.reduction_arity_bits) { - let FriQueryStep { - evals, - merkle_proof, - } = step; - - let arity = 1 << arity_bits; - codeword_len_bits -= arity_bits; - - ensure!(evals.len() == arity); - ensure!(merkle_proof.len() + cap_height == codeword_len_bits); - } - } - - ensure!(final_poly.len() == params.final_poly_len()); - - Ok(()) + validate_batch_fri_proof_shape::(proof, &[instance.clone()], params) } pub(crate) fn validate_batch_fri_proof_shape( diff --git a/plonky2/src/gates/selectors.rs b/plonky2/src/gates/selectors.rs index c75f2e226e..9bd1217b50 100644 --- a/plonky2/src/gates/selectors.rs +++ b/plonky2/src/gates/selectors.rs @@ -42,9 +42,11 @@ pub enum LookupSelectors { /// Returns selector polynomials for each LUT. We have two constraint domains (remember that gates are stored upside down): /// - [last_lut_row, first_lut_row] (Sum and RE transition constraints), /// - [last_lu_row, last_lut_row - 1] (LDC column transition constraints). +/// /// We also add two more: /// - {first_lut_row + 1} where we check the initial values of sum and RE (which are 0), /// - {last_lu_row} where we check that the last value of LDC is 0. +/// /// Conceptually they're part of the selector ends lookups, but since we can have one polynomial for *all* LUTs it's here. pub(crate) fn selectors_lookup, const D: usize>( _gates: &[GateRef], diff --git a/plonky2/src/hash/arch/x86_64/poseidon_goldilocks_avx2_bmi2.rs b/plonky2/src/hash/arch/x86_64/poseidon_goldilocks_avx2_bmi2.rs index e56fea5ea7..7046a7fdd2 100644 --- a/plonky2/src/hash/arch/x86_64/poseidon_goldilocks_avx2_bmi2.rs +++ b/plonky2/src/hash/arch/x86_64/poseidon_goldilocks_avx2_bmi2.rs @@ -494,7 +494,7 @@ unsafe fn mds_multiply_and_add_round_const_s( // Fall through for MDS matrix multiplication on low 32 bits // This is a GCC _local label_. For details, see - // https://doc.rust-lang.org/beta/unstable-book/library-features/asm.html#labels + // https://doc.rust-lang.org/rust-by-example/unsafe/asm.html#labels // In short, the assembler makes sure to assign a unique name to replace `2:` with a unique // name, so the label does not clash with any compiler-generated label. `2:` can appear // multiple times; to disambiguate, we must refer to it as `2b` or `2f`, specifying the diff --git a/plonky2/src/hash/field_merkle_tree.rs b/plonky2/src/hash/batch_merkle_tree.rs similarity index 87% rename from plonky2/src/hash/field_merkle_tree.rs rename to plonky2/src/hash/batch_merkle_tree.rs index 6e1828c2da..eaa49977d5 100644 --- a/plonky2/src/hash/field_merkle_tree.rs +++ b/plonky2/src/hash/batch_merkle_tree.rs @@ -14,7 +14,7 @@ use crate::plonk::config::{GenericHashOut, Hasher}; use crate::util::log2_strict; #[derive(Clone, Debug, Default, Eq, PartialEq)] -pub struct FieldMerkleTree> { +pub struct BatchMerkleTree> { /// The data stored in the Merkle tree leaves. pub leaves: Vec>>, @@ -28,11 +28,10 @@ pub struct FieldMerkleTree> { pub leaf_heights: Vec, } -impl> FieldMerkleTree { +impl> BatchMerkleTree { /// Each element in the `leaves` vector represents a matrix (a vector of vectors). /// The height of each matrix should be a power of two. /// The `leaves` vector should be sorted by matrix height, from tallest to shortest, with no duplicate heights. - // TODO: FieldMerkleTree does not handle duplicates; this is deferred to the caller. Revisit when implementing batch FRI to potentially optimize. pub fn new(mut leaves: Vec>>, cap_height: usize) -> Self { assert!(!leaves.is_empty()); assert!(leaves.iter().all(|leaf| leaf.len().is_power_of_two())); @@ -40,17 +39,17 @@ impl> FieldMerkleTree { .windows(2) .all(|pair| { pair[0].len() > pair[1].len() })); - let leaves_len = leaves[0].len(); - let log2_leaves_len = log2_strict(leaves_len); + let last_leaves_cap_height = log2_strict(leaves.last().unwrap().len()); assert!( - cap_height <= log2_leaves_len, - "cap_height={} should be at most log2(leaves.len())={}", + cap_height <= last_leaves_cap_height, + "cap_height={} should be at most last_leaves_cap_height={}", cap_height, - log2_leaves_len + last_leaves_cap_height ); - let mut leaf_heights = vec![]; + let mut leaf_heights = Vec::with_capacity(leaves.len()); + let leaves_len = leaves[0].len(); let num_digests = 2 * (leaves_len - (1 << cap_height)); let mut digests = Vec::with_capacity(num_digests); let digests_buf = capacity_up_to_mut(&mut digests, num_digests); @@ -105,6 +104,8 @@ impl> FieldMerkleTree { } unsafe { + // SAFETY: `fill_digests_buf` and `cap` initialized the spare capacity up to + // `num_digests` and `len_cap`, resp. cap.set_len(next_cap_len); } @@ -131,7 +132,7 @@ impl> FieldMerkleTree { /// Create a Merkle proof from a leaf index. pub fn open_batch(&self, leaf_index: usize) -> MerkleProof { let mut digests_buf_pos = 0; - let leaves_cap_height = log2_strict(self.leaves[0].len()); + let initial_leaf_height = log2_strict(self.leaves[0].len()); let mut siblings = vec![]; let mut cap_heights = self.leaf_heights.clone(); cap_heights.push(log2_strict(self.cap.len())); @@ -140,7 +141,7 @@ impl> FieldMerkleTree { let next_cap_height = window[1]; let num_digests: usize = 2 * ((1 << cur_cap_height) - (1 << next_cap_height)); siblings.extend::>(merkle_tree_prove::( - leaf_index >> (leaves_cap_height - cur_cap_height), + leaf_index >> (initial_leaf_height - cur_cap_height), 1 << cur_cap_height, next_cap_height, &self.digests[digests_buf_pos..digests_buf_pos + num_digests], @@ -173,7 +174,7 @@ mod tests { use plonky2_field::types::Field; use super::*; - use crate::hash::merkle_proofs::verify_field_merkle_proof_to_cap; + use crate::hash::merkle_proofs::verify_batch_merkle_proof_to_cap; use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; const D: usize = 2; @@ -195,7 +196,7 @@ mod tests { vec![F::TWO, F::TWO], vec![F::ZERO, F::ZERO], ]; - let fmt: FieldMerkleTree = FieldMerkleTree::new(vec![mat_1], 0); + let fmt: BatchMerkleTree = BatchMerkleTree::new(vec![mat_1], 0); let mat_1_leaf_hashes = [ H::hash_or_noop(&[F::ZERO, F::ONE]), @@ -221,7 +222,7 @@ mod tests { let opened_values = fmt.values(2); assert_eq!(opened_values, [vec![F::TWO, F::TWO]]); - verify_field_merkle_proof_to_cap(&opened_values, &fmt.leaf_heights, 2, &fmt.cap, &proof)?; + verify_batch_merkle_proof_to_cap(&opened_values, &fmt.leaf_heights, 2, &fmt.cap, &proof)?; Ok(()) } @@ -246,8 +247,8 @@ mod tests { // ] let mat_2 = vec![vec![F::ONE, F::TWO, F::ONE], vec![F::ZERO, F::TWO, F::TWO]]; - let fmt: FieldMerkleTree = - FieldMerkleTree::new(vec![mat_1, mat_2.clone()], 0); + let fmt: BatchMerkleTree = + BatchMerkleTree::new(vec![mat_1, mat_2.clone()], 0); let mat_1_leaf_hashes = [ H::hash_or_noop(&[F::ZERO, F::ONE]), @@ -288,22 +289,22 @@ mod tests { [vec![F::TWO, F::ONE], vec![F::ONE, F::TWO, F::ONE]] ); - verify_field_merkle_proof_to_cap(&opened_values, &fmt.leaf_heights, 1, &fmt.cap, &proof)?; + verify_batch_merkle_proof_to_cap(&opened_values, &fmt.leaf_heights, 1, &fmt.cap, &proof)?; Ok(()) } #[test] - fn test_field_merkle_trees() -> Result<()> { + fn test_batch_merkle_trees() -> Result<()> { let leaves_1 = crate::hash::merkle_tree::tests::random_data::(1024, 7); let leaves_2 = crate::hash::merkle_tree::tests::random_data::(64, 3); let leaves_3 = crate::hash::merkle_tree::tests::random_data::(32, 100); - let fmt: FieldMerkleTree = - FieldMerkleTree::new(vec![leaves_1, leaves_2, leaves_3], 3); + let fmt: BatchMerkleTree = + BatchMerkleTree::new(vec![leaves_1, leaves_2, leaves_3], 3); for index in [0, 1023, 512, 255] { let proof = fmt.open_batch(index); let opened_values = fmt.values(index); - verify_field_merkle_proof_to_cap( + verify_batch_merkle_proof_to_cap( &opened_values, &fmt.leaf_heights, index, @@ -316,14 +317,14 @@ mod tests { } #[test] - fn test_field_merkle_trees_cap_at_leaves_height() -> Result<()> { + fn test_batch_merkle_trees_cap_at_leaves_height() -> Result<()> { let leaves_1 = crate::hash::merkle_tree::tests::random_data::(16, 7); - let fmt: FieldMerkleTree = FieldMerkleTree::new(vec![leaves_1], 4); + let fmt: BatchMerkleTree = BatchMerkleTree::new(vec![leaves_1], 4); for index in 0..16 { let proof = fmt.open_batch(index); let opened_values = fmt.values(index); - verify_field_merkle_proof_to_cap( + verify_batch_merkle_proof_to_cap( &opened_values, &fmt.leaf_heights, index, diff --git a/plonky2/src/hash/merkle_proofs.rs b/plonky2/src/hash/merkle_proofs.rs index d0620ac3a6..95a347ae55 100644 --- a/plonky2/src/hash/merkle_proofs.rs +++ b/plonky2/src/hash/merkle_proofs.rs @@ -57,28 +57,18 @@ pub fn verify_merkle_proof_to_cap>( merkle_cap: &MerkleCap, proof: &MerkleProof, ) -> Result<()> { - let mut index = leaf_index; - let mut current_digest = H::hash_or_noop(&leaf_data); - for &sibling_digest in proof.siblings.iter() { - let bit = index & 1; - index >>= 1; - current_digest = if bit == 1 { - H::two_to_one(sibling_digest, current_digest) - } else { - H::two_to_one(current_digest, sibling_digest) - } - } - ensure!( - current_digest == merkle_cap.0[index], - "Invalid Merkle proof." - ); - - Ok(()) + verify_batch_merkle_proof_to_cap( + &[leaf_data.clone()], + &[proof.siblings.len()], + leaf_index, + merkle_cap, + proof, + ) } /// Verifies that the given leaf data is present at the given index in the Field Merkle tree with the /// given cap. -pub fn verify_field_merkle_proof_to_cap>( +pub fn verify_batch_merkle_proof_to_cap>( leaf_data: &[Vec], leaf_heights: &[usize], mut leaf_index: usize, @@ -89,7 +79,7 @@ pub fn verify_field_merkle_proof_to_cap>( let mut current_digest = H::hash_or_noop(&leaf_data[0]); let mut current_height = leaf_heights[0]; let mut leaf_data_index = 1; - for &sibling_digest in proof.siblings.iter() { + for &sibling_digest in &proof.siblings { let bit = leaf_index & 1; leaf_index >>= 1; current_digest = if bit == 1 { @@ -190,9 +180,9 @@ impl, const D: usize> CircuitBuilder { } } - /// Same as `verify_field_merkle_proof_to_cap`, except with the final "cap index" as separate parameter, + /// Same as `verify_batch_merkle_proof_to_cap`, except with the final "cap index" as separate parameter, /// rather than being contained in `leaf_index_bits`. - pub(crate) fn verify_field_merkle_proof_to_cap_with_cap_index>( + pub(crate) fn verify_batch_merkle_proof_to_cap_with_cap_index>( &mut self, leaf_data: &[Vec], leaf_heights: &[usize], @@ -231,7 +221,7 @@ impl, const D: usize> CircuitBuilder { { let mut new_leaves = state.elements.to_vec(); new_leaves.extend_from_slice(&leaf_data[leaf_data_index]); - state = self.hash_or_noop::(new_leaves.clone()); + state = self.hash_or_noop::(new_leaves); leaf_data_index += 1; } diff --git a/plonky2/src/hash/merkle_tree.rs b/plonky2/src/hash/merkle_tree.rs index abc96dcf7c..31bcf5e37c 100644 --- a/plonky2/src/hash/merkle_tree.rs +++ b/plonky2/src/hash/merkle_tree.rs @@ -148,7 +148,7 @@ pub(crate) fn fill_digests_buf>( ); } -pub fn merkle_tree_prove>( +pub(crate) fn merkle_tree_prove>( leaf_index: usize, leaves_len: usize, cap_height: usize, diff --git a/plonky2/src/hash/mod.rs b/plonky2/src/hash/mod.rs index bb29664c52..51833ae2c0 100644 --- a/plonky2/src/hash/mod.rs +++ b/plonky2/src/hash/mod.rs @@ -2,7 +2,7 @@ //! as well as specific hash functions implementation. mod arch; -pub mod field_merkle_tree; +pub mod batch_merkle_tree; pub mod hash_types; pub mod hashing; pub mod keccak; diff --git a/plonky2/src/lib.rs b/plonky2/src/lib.rs index 8955194fc5..8772ecfc0e 100644 --- a/plonky2/src/lib.rs +++ b/plonky2/src/lib.rs @@ -11,6 +11,7 @@ pub extern crate alloc; #[doc(inline)] pub use plonky2_field as field; +pub mod batch_fri; pub mod fri; pub mod gadgets; pub mod gates; diff --git a/plonky2/src/plonk/circuit_builder.rs b/plonky2/src/plonk/circuit_builder.rs index 0e9de32e08..3be81b1599 100644 --- a/plonky2/src/plonk/circuit_builder.rs +++ b/plonky2/src/plonk/circuit_builder.rs @@ -536,6 +536,14 @@ impl, const D: usize> CircuitBuilder { } } + /// If `condition`, enforces that two routable `Target` values are equal, using Plonk's permutation argument. + pub fn conditional_assert_eq(&mut self, condition: Target, x: Target, y: Target) { + let zero = self.zero(); + let diff = self.sub(x, y); + let constr = self.mul(condition, diff); + self.connect(constr, zero); + } + /// Enforces that a routable `Target` value is 0, using Plonk's permutation argument. pub fn assert_zero(&mut self, x: Target) { let zero = self.zero(); diff --git a/plonky2/src/plonk/vanishing_poly.rs b/plonky2/src/plonk/vanishing_poly.rs index ef10108106..a5fac4ab05 100644 --- a/plonky2/src/plonk/vanishing_poly.rs +++ b/plonky2/src/plonk/vanishing_poly.rs @@ -329,6 +329,7 @@ pub(crate) fn eval_vanishing_poly_base_batch, const /// - RE ensures the well formation of lookup tables; /// - Sum is a running sum of m_i/(X - (input_i + a * output_i)) where (input_i, output_i) are input pairs in the lookup table (LUT); /// - LDC is a running sum of 1/(X - (input_i + a * output_i)) where (input_i, output_i) are input pairs that look in the LUT. +/// /// Sum and LDC are broken down in partial polynomials to lower the constraint degree, similarly to the permutation argument. /// They also share the same partial SLDC polynomials, so that the last SLDC value is Sum(end) - LDC(end). The final constraint /// Sum(end) = LDC(end) becomes simply SLDC(end) = 0, and we can remove the LDC initial constraint. diff --git a/plonky2/src/recursion/conditional_recursive_verifier.rs b/plonky2/src/recursion/conditional_recursive_verifier.rs index 238d1589f8..c41a9b583a 100644 --- a/plonky2/src/recursion/conditional_recursive_verifier.rs +++ b/plonky2/src/recursion/conditional_recursive_verifier.rs @@ -34,18 +34,8 @@ impl, const D: usize> CircuitBuilder { { let selected_proof = self.select_proof_with_pis(condition, proof_with_pis0, proof_with_pis1); - let selected_verifier_data = VerifierCircuitTarget { - constants_sigmas_cap: self.select_cap( - condition, - &inner_verifier_data0.constants_sigmas_cap, - &inner_verifier_data1.constants_sigmas_cap, - ), - circuit_digest: self.select_hash( - condition, - inner_verifier_data0.circuit_digest, - inner_verifier_data1.circuit_digest, - ), - }; + let selected_verifier_data = + self.select_verifier_data(condition, inner_verifier_data0, inner_verifier_data1); self.verify_proof::(&selected_proof, &selected_verifier_data, inner_common_data); } @@ -179,6 +169,23 @@ impl, const D: usize> CircuitBuilder { .collect() } + /// Computes `if b { vk0 } else { vk1 }`. + pub fn select_verifier_data( + &mut self, + b: BoolTarget, + vk0: &VerifierCircuitTarget, + vk1: &VerifierCircuitTarget, + ) -> VerifierCircuitTarget { + VerifierCircuitTarget { + constants_sigmas_cap: self.select_cap( + b, + &vk0.constants_sigmas_cap, + &vk1.constants_sigmas_cap, + ), + circuit_digest: self.select_hash(b, vk0.circuit_digest, vk1.circuit_digest), + } + } + /// Computes `if b { os0 } else { os1 }`. fn select_opening_set( &mut self, diff --git a/plonky2/src/recursion/dummy_circuit.rs b/plonky2/src/recursion/dummy_circuit.rs index 75b3929e82..1dc872db5a 100644 --- a/plonky2/src/recursion/dummy_circuit.rs +++ b/plonky2/src/recursion/dummy_circuit.rs @@ -67,11 +67,7 @@ where /// Generate a proof for a dummy circuit. The `public_inputs` parameter let the caller specify /// certain public inputs (identified by their indices) which should be given specific values. /// The rest will default to zero. -pub(crate) fn dummy_proof< - F: RichField + Extendable, - C: GenericConfig, - const D: usize, ->( +pub fn dummy_proof, C: GenericConfig, const D: usize>( circuit: &CircuitData, nonzero_public_inputs: HashMap, ) -> anyhow::Result> @@ -86,11 +82,7 @@ where } /// Generate a circuit matching a given `CommonCircuitData`. -pub(crate) fn dummy_circuit< - F: RichField + Extendable, - C: GenericConfig, - const D: usize, ->( +pub fn dummy_circuit, C: GenericConfig, const D: usize>( common_data: &CommonCircuitData, ) -> CircuitData { let config = common_data.config.clone(); @@ -143,6 +135,20 @@ impl, const D: usize> CircuitBuilder { Ok((dummy_proof_with_pis_target, dummy_verifier_data_target)) } + + pub fn dummy_proof_and_constant_vk_no_generator + 'static>( + &mut self, + common_data: &CommonCircuitData, + ) -> anyhow::Result<(ProofWithPublicInputsTarget, VerifierCircuitTarget)> + where + C::Hasher: AlgebraicHasher, + { + let dummy_circuit = dummy_circuit::(common_data); + let dummy_proof_with_pis_target = self.add_virtual_proof_with_pis(common_data); + let dummy_verifier_data_target = self.constant_verifier_data(&dummy_circuit.verifier_only); + + Ok((dummy_proof_with_pis_target, dummy_verifier_data_target)) + } } #[derive(Debug)] diff --git a/starky/README.md b/starky/README.md index bb4e2d8a92..3519b68696 100644 --- a/starky/README.md +++ b/starky/README.md @@ -1,3 +1,17 @@ +# Starky + +Starky is a FRI-based STARK implementation. + +It is built for speed, features highly efficient recursive verification through `plonky2` circuits and gadgets, and is +being used as backend proving system for the Polygon Zero Type-1 zkEVM. + +## Note on Zero-Knowledgeness + +While STARKs can be made Zero-Knowledge, the primary purpose of `starky` is to provide fast STARK proof generation. As such, +ZK is disabled by default on `starky`. Applications requiring their proof to be `zero-knowledge` would need to apply a +recursive wrapper on top of their STARK proof with the `zero_knowledge` parameter activated in their `CircuitConfig`. +See `plonky2` documentation for more info. + ## License Licensed under either of diff --git a/starky/src/cross_table_lookup.rs b/starky/src/cross_table_lookup.rs index 29ae6cbe04..dd81443117 100644 --- a/starky/src/cross_table_lookup.rs +++ b/starky/src/cross_table_lookup.rs @@ -19,7 +19,8 @@ //! - Z(gw) = Z(w) * combine(w) where combine(w) is the column combination at point w. //! - Z(g^(n-1)) = combine(1). //! - The verifier also checks that the product of looking table Z polynomials is equal -//! to the associated looked table Z polynomial. +//! to the associated looked table Z polynomial. +//! //! Note that the first two checks are written that way because Z polynomials are computed //! upside down for convenience. //! @@ -34,6 +35,7 @@ use core::fmt::Debug; use core::iter::once; use anyhow::{ensure, Result}; +use hashbrown::HashMap; use itertools::Itertools; use plonky2::field::extension::{Extendable, FieldExtension}; use plonky2::field::packed::PackedField; @@ -316,6 +318,7 @@ pub(crate) fn get_ctl_auxiliary_polys( /// - `cross_table_lookups` corresponds to all the cross-table lookups, i.e. the looked and looking tables, as described in `CrossTableLookup`. /// - `ctl_challenges` corresponds to the challenges used for CTLs. /// - `constraint_degree` is the maximal constraint degree for the table. +/// /// For each `CrossTableLookup`, and each looking/looked table, the partial products for the CTL are computed, and added to the said table's `CtlZData`. pub(crate) fn cross_table_lookup_data<'a, F: RichField, const D: usize, const N: usize>( trace_poly_values: &[Vec>; N], @@ -621,6 +624,7 @@ impl<'a, F: RichField + Extendable, const D: usize> /// Checks the cross-table lookup Z polynomials for each table: /// - Checks that the CTL `Z` partial sums are correctly updated. /// - Checks that the final value of the CTL sum is the combination of all STARKs' CTL polynomials. +/// /// CTL `Z` partial sums are upside down: the complete sum is on the first row, and /// the first term is on the last row. This allows the transition constraint to be: /// `combine(w) * (Z(w) - Z(gw)) = filter` where combine is called on the local row @@ -825,6 +829,7 @@ impl<'a, F: Field, const D: usize> CtlCheckVarsTarget { /// Circuit version of `eval_cross_table_lookup_checks`. Checks the cross-table lookup Z polynomials for each table: /// - Checks that the CTL `Z` partial sums are correctly updated. /// - Checks that the final value of the CTL sum is the combination of all STARKs' CTL polynomials. +/// /// CTL `Z` partial sums are upside down: the complete sum is on the first row, and /// the first term is on the last row. This allows the transition constraint to be: /// `combine(w) * (Z(w) - Z(gw)) = filter` where combine is called on the local row @@ -916,10 +921,11 @@ pub(crate) fn eval_cross_table_lookup_checks_circuit< } /// Verifies all cross-table lookups. +/// The key of `ctl_extra_looking_sums` is the corresponding CTL's position within `cross_table_lookups`. pub fn verify_cross_table_lookups, const D: usize, const N: usize>( cross_table_lookups: &[CrossTableLookup], ctl_zs_first: [Vec; N], - ctl_extra_looking_sums: Option<&[Vec]>, + ctl_extra_looking_sums: &HashMap>, config: &StarkConfig, ) -> Result<()> { let mut ctl_zs_openings = ctl_zs_first.iter().map(|v| v.iter()).collect::>(); @@ -931,6 +937,7 @@ pub fn verify_cross_table_lookups, const D: usize, }, ) in cross_table_lookups.iter().enumerate() { + let ctl_extra_looking_sum = ctl_extra_looking_sums.get(&index); // We want to iterate on each looking table only once. let mut filtered_looking_tables = vec![]; for table in looking_tables { @@ -946,8 +953,7 @@ pub fn verify_cross_table_lookups, const D: usize, .map(|&table| *ctl_zs_openings[table].next().unwrap()) .sum::() // Get elements looking into `looked_table` that are not associated to any STARK. - + ctl_extra_looking_sums - .map(|v| v[looked_table.table][c]).unwrap_or_default(); + + ctl_extra_looking_sum.map(|v| v[c]).unwrap_or_default(); // Get the looked table CTL polynomial opening. let looked_z = *ctl_zs_openings[looked_table.table].next().unwrap(); @@ -965,6 +971,7 @@ pub fn verify_cross_table_lookups, const D: usize, } /// Circuit version of `verify_cross_table_lookups`. Verifies all cross-table lookups. +/// The key of `ctl_extra_looking_sums` is the corresponding CTL's position within `cross_table_lookups`. pub fn verify_cross_table_lookups_circuit< F: RichField + Extendable, const D: usize, @@ -973,15 +980,19 @@ pub fn verify_cross_table_lookups_circuit< builder: &mut CircuitBuilder, cross_table_lookups: Vec>, ctl_zs_first: [Vec; N], - ctl_extra_looking_sums: Option<&[Vec]>, + ctl_extra_looking_sums: &HashMap>, inner_config: &StarkConfig, ) { let mut ctl_zs_openings = ctl_zs_first.iter().map(|v| v.iter()).collect::>(); - for CrossTableLookup { - looking_tables, - looked_table, - } in cross_table_lookups.into_iter() + for ( + index, + CrossTableLookup { + looking_tables, + looked_table, + }, + ) in cross_table_lookups.into_iter().enumerate() { + let ctl_extra_looking_sum = ctl_extra_looking_sums.get(&index); // We want to iterate on each looking table only once. let mut filtered_looking_tables = vec![]; for table in looking_tables { @@ -998,9 +1009,7 @@ pub fn verify_cross_table_lookups_circuit< ); // Get elements looking into `looked_table` that are not associated to any STARK. - let extra_sum = ctl_extra_looking_sums - .map(|v| v[looked_table.table][c]) - .unwrap_or_default(); + let extra_sum = ctl_extra_looking_sum.map(|v| v[c]).unwrap_or_default(); looking_zs_sum = builder.add(looking_zs_sum, extra_sum); // Get the looked table CTL polynomial opening. @@ -1029,10 +1038,11 @@ pub mod debug_utils { type MultiSet = HashMap, Vec<(TableIdx, usize)>>; /// Check that the provided traces and cross-table lookups are consistent. + /// The key of `extra_looking_values` is the corresponding CTL's position within `cross_table_lookups`. pub fn check_ctls( trace_poly_values: &[Vec>], cross_table_lookups: &[CrossTableLookup], - extra_looking_values: &HashMap>>, + extra_looking_values: &HashMap>>, ) { for (i, ctl) in cross_table_lookups.iter().enumerate() { check_ctl(trace_poly_values, ctl, i, extra_looking_values.get(&i)); diff --git a/starky/src/get_challenges.rs b/starky/src/get_challenges.rs index 8000a9ef90..e8904ffcaf 100644 --- a/starky/src/get_challenges.rs +++ b/starky/src/get_challenges.rs @@ -157,6 +157,7 @@ where ignore_trace_cap: bool, config: &StarkConfig, ) -> StarkProofChallenges { + challenger.observe_elements(&self.public_inputs); self.proof .get_challenges(challenger, challenges, ignore_trace_cap, config) } @@ -302,6 +303,7 @@ impl StarkProofWithPublicInputsTarget { C: GenericConfig, C::Hasher: AlgebraicHasher, { + challenger.observe_elements(&self.public_inputs); self.proof .get_challenges::(builder, challenger, challenges, ignore_trace_cap, config) } diff --git a/starky/src/lookup.rs b/starky/src/lookup.rs index f260400bf9..bc079fb1a5 100644 --- a/starky/src/lookup.rs +++ b/starky/src/lookup.rs @@ -321,24 +321,16 @@ impl Column { /// Evaluate on a row of a table given in column-major form. pub(crate) fn eval_table(&self, table: &[PolynomialValues], row: usize) -> F { - let mut res = self - .linear_combination + self.linear_combination .iter() .map(|&(c, f)| table[c].values[row] * f) .sum::() - + self.constant; - - // If we access the next row at the last row, for sanity, we consider the next row's values to be 0. - // If the lookups are correctly written, the filter should be 0 in that case anyway. - if !self.next_row_linear_combination.is_empty() && row < table[0].values.len() - 1 { - res += self + + self .next_row_linear_combination .iter() - .map(|&(c, f)| table[c].values[row + 1] * f) - .sum::(); - } - - res + .map(|&(c, f)| table[c].values[(row + 1) % table[c].values.len()] * f) + .sum::() + + self.constant } /// Evaluates the column on all rows. diff --git a/starky/src/prover.rs b/starky/src/prover.rs index 7a6b73f122..18de34afd4 100644 --- a/starky/src/prover.rs +++ b/starky/src/prover.rs @@ -71,6 +71,7 @@ where let trace_cap = trace_commitment.merkle_tree.cap.clone(); let mut challenger = Challenger::new(); + challenger.observe_elements(public_inputs); challenger.observe_cap(&trace_cap); prove_with_commitment( @@ -92,7 +93,7 @@ where /// - all the required Merkle caps, /// - all the required polynomial and FRI argument openings. /// - individual `ctl_data` and common `ctl_challenges` if the STARK is part -/// of a multi-STARK system. +/// of a multi-STARK system. pub fn prove_with_commitment( stark: &S, config: &StarkConfig, @@ -556,6 +557,8 @@ fn check_constraints<'a, F, C, S, const D: usize>( C: GenericConfig, S: Stark, { + use core::any::type_name; + let degree = 1 << degree_bits; let rate_bits = 0; // Set this to higher value to check constraint degree. let total_num_helper_cols: usize = num_ctl_helper_cols.iter().sum(); @@ -663,11 +666,14 @@ fn check_constraints<'a, F, C, S, const D: usize>( .collect::>(); // Assert that all constraints evaluate to 0 over our subgroup. - for v in constraint_values { - assert!( - v.iter().all(|x| x.is_zero()), - "Constraint failed in {}", - core::any::type_name::() - ); + for (row, v) in constraint_values.iter().enumerate() { + for x in v.iter() { + assert!( + x.is_zero(), + "Constraint failed in {} at row {}", + type_name::(), + row + ) + } } } diff --git a/starky/src/recursive_verifier.rs b/starky/src/recursive_verifier.rs index 3971719923..8deacef955 100644 --- a/starky/src/recursive_verifier.rs +++ b/starky/src/recursive_verifier.rs @@ -14,7 +14,7 @@ use plonky2::hash::hash_types::RichField; use plonky2::iop::challenger::RecursiveChallenger; use plonky2::iop::ext_target::ExtensionTarget; use plonky2::iop::target::Target; -use plonky2::iop::witness::Witness; +use plonky2::iop::witness::WitnessWrite; use plonky2::plonk::circuit_builder::CircuitBuilder; use plonky2::plonk::config::{AlgebraicHasher, GenericConfig}; use plonky2::util::reducing::ReducingFactorTarget; @@ -329,7 +329,7 @@ pub fn set_stark_proof_with_pis_target, W, const D ) where F: RichField + Extendable, C::Hasher: AlgebraicHasher, - W: Witness, + W: WitnessWrite, { let StarkProofWithPublicInputs { proof, @@ -358,7 +358,7 @@ pub fn set_stark_proof_target, W, const D: usize>( ) where F: RichField + Extendable, C::Hasher: AlgebraicHasher, - W: Witness, + W: WitnessWrite, { witness.set_cap_target(&proof_target.trace_cap, &proof.trace_cap); if let (Some(quotient_polys_cap_target), Some(quotient_polys_cap)) =