From ae6c3c46935194c5c3c9af9c413dd704b8d22dee Mon Sep 17 00:00:00 2001 From: Sai <135601871+sai-deng@users.noreply.github.com> Date: Fri, 18 Oct 2024 18:21:14 +0000 Subject: [PATCH 1/2] feat: Enable more optional tables (#724) This PR enables BytePacking, Logic, MemAfter, and Poseidon tables as optional tables in the root circuit, reducing table proving time and recursion time. --- evm_arithmetization/src/all_stark.rs | 21 ++- .../src/fixed_recursive_verifier.rs | 148 ++++++++++------- evm_arithmetization/src/generation/mod.rs | 37 ++++- evm_arithmetization/src/get_challenges.rs | 4 +- evm_arithmetization/src/proof.rs | 24 ++- evm_arithmetization/src/prover.rs | 151 ++++++++---------- evm_arithmetization/src/testing_utils.rs | 6 +- evm_arithmetization/src/verifier.rs | 143 +++++++++-------- 8 files changed, 308 insertions(+), 226 deletions(-) diff --git a/evm_arithmetization/src/all_stark.rs b/evm_arithmetization/src/all_stark.rs index 0be307db5..41f4010fe 100644 --- a/evm_arithmetization/src/all_stark.rs +++ b/evm_arithmetization/src/all_stark.rs @@ -106,9 +106,24 @@ pub const NUM_TABLES: usize = if cfg!(feature = "cdk_erigon") { Table::MemAfter as usize + 1 }; -/// Indices of Keccak Tables -pub const KECCAK_TABLES_INDICES: [usize; 2] = - [Table::Keccak as usize, Table::KeccakSponge as usize]; +/// Indices of optional Tables +#[cfg(not(feature = "cdk_erigon"))] +pub const OPTIONAL_TABLE_INDICES: [usize; 5] = [ + Table::BytePacking as usize, + Table::Keccak as usize, + Table::KeccakSponge as usize, + Table::Logic as usize, + Table::MemAfter as usize, +]; +#[cfg(feature = "cdk_erigon")] +pub const OPTIONAL_TABLE_INDICES: [usize; 6] = [ + Table::BytePacking as usize, + Table::Keccak as usize, + Table::KeccakSponge as usize, + Table::Logic as usize, + Table::MemAfter as usize, + Table::Poseidon as usize, +]; impl Table { /// Returns all STARK table indices. diff --git a/evm_arithmetization/src/fixed_recursive_verifier.rs b/evm_arithmetization/src/fixed_recursive_verifier.rs index 6521724a7..adde60d1b 100644 --- a/evm_arithmetization/src/fixed_recursive_verifier.rs +++ b/evm_arithmetization/src/fixed_recursive_verifier.rs @@ -37,8 +37,8 @@ use starky::proof::StarkProofWithMetadata; use starky::stark::Stark; use crate::all_stark::{ - all_cross_table_lookups, AllStark, Table, KECCAK_TABLES_INDICES, MEMORY_CTL_IDX, NUM_CTLS, - NUM_TABLES, + all_cross_table_lookups, AllStark, Table, MEMORY_CTL_IDX, NUM_CTLS, NUM_TABLES, + OPTIONAL_TABLE_INDICES, }; use crate::cpu::kernel::aggregator::KERNEL; use crate::generation::segments::{GenerationSegmentData, SegmentDataIterator}; @@ -156,8 +156,8 @@ where /// for EVM root proofs; the circuit has them just to match the /// structure of aggregation proofs. cyclic_vk: VerifierCircuitTarget, - /// We can skip verifying Keccak tables when they are not in use. - use_keccak_tables: BoolTarget, + /// We can skip verifying tables when they are not in use. + table_in_use: [BoolTarget; NUM_TABLES], } impl RootCircuitData @@ -180,7 +180,9 @@ where } self.public_values.to_buffer(buffer)?; buffer.write_target_verifier_circuit(&self.cyclic_vk)?; - buffer.write_target_bool(self.use_keccak_tables)?; + for table_in_use in self.table_in_use { + buffer.write_target_bool(table_in_use)?; + } Ok(()) } @@ -200,7 +202,10 @@ where } let public_values = PublicValuesTarget::from_buffer(buffer)?; let cyclic_vk = buffer.read_target_verifier_circuit()?; - let use_keccak_tables = buffer.read_target_bool()?; + let mut table_in_use = Vec::with_capacity(NUM_TABLES); + for _ in 0..NUM_TABLES { + table_in_use.push(buffer.read_target_bool()?); + } Ok(Self { circuit, @@ -208,7 +213,7 @@ where index_verifier_data: index_verifier_data.try_into().unwrap(), public_values, cyclic_vk, - use_keccak_tables, + table_in_use: table_in_use.try_into().unwrap(), }) } } @@ -832,26 +837,24 @@ where let block_wrapper = Self::create_block_wrapper_circuit(&block); let two_to_one_block = Self::create_two_to_one_block_circuit(&block_wrapper); - // TODO(sdeng): enable more optional Tables let table_dummy_proofs = core::array::from_fn(|i| { - if KECCAK_TABLES_INDICES.contains(&i) { + if OPTIONAL_TABLE_INDICES.contains(&i) { let init_degree = degree_bits_ranges[i].start; - let common_circuit_data = by_table[i] + let chain = by_table[i] .by_stark_size .get(&init_degree) - .expect("Unable to get the shrinking circuits") + .expect("Unable to get the shrinking circuits"); + let common_circuit_data = chain .shrinking_wrappers .last() - .expect("Unable to get the last shrinking circuit") - .circuit - .common - .clone(); - let dummy_circuit: CircuitData = dummy_circuit(&common_circuit_data); + .map(|wrapper| &wrapper.circuit.common) + .unwrap_or(&chain.initial_wrapper.circuit.common); + let dummy_circuit: CircuitData = dummy_circuit(common_circuit_data); let dummy_pis = HashMap::new(); let proof = dummy_proof(&dummy_circuit, dummy_pis) .expect("Unable to generate dummy proofs"); Some(ShrunkProofData { - common_circuit_data, + common_circuit_data: common_circuit_data.clone(), proof, }) } else { @@ -900,8 +903,10 @@ where let mut builder = CircuitBuilder::new(CircuitConfig::standard_recursion_config()); - let use_keccak_tables = builder.add_virtual_bool_target_safe(); - let skip_keccak_tables = builder.not(use_keccak_tables); + let table_in_use: [BoolTarget; NUM_TABLES] = + core::array::from_fn(|_| builder.add_virtual_bool_target_safe()); + let table_not_in_use: [BoolTarget; NUM_TABLES] = + core::array::from_fn(|i| builder.not(table_in_use[i])); let public_values = add_virtual_public_values_public_input(&mut builder); let recursive_proofs = @@ -921,11 +926,17 @@ where } } + for (i, table) in table_in_use.iter().enumerate() { + if !OPTIONAL_TABLE_INDICES.contains(&i) { + builder.assert_one(table.target); + } + } + // Ensures that the trace cap is set to 0 when skipping Keccak tables. - for i in KECCAK_TABLES_INDICES { + for i in OPTIONAL_TABLE_INDICES { for h in &pis[i].trace_cap { for t in h { - let trace_cap_check = builder.mul(skip_keccak_tables.target, *t); + let trace_cap_check = builder.mul(table_not_in_use[i].target, *t); builder.assert_zero(trace_cap_check); } } @@ -941,16 +952,16 @@ where // Check that the correct CTL challenges are used in every proof. for (i, pi) in pis.iter().enumerate() { for j in 0..stark_config.num_challenges { - if KECCAK_TABLES_INDICES.contains(&i) { - // Ensures that the correct CTL challenges are used in Keccak tables when - // `enable_keccak_tables` is true. + if OPTIONAL_TABLE_INDICES.contains(&i) { + // Ensures that the correct CTL challenges are used when an optional table + // is in use. builder.conditional_assert_eq( - use_keccak_tables.target, + table_in_use[i].target, ctl_challenges.challenges[j].beta, pi.ctl_challenges.challenges[j].beta, ); builder.conditional_assert_eq( - use_keccak_tables.target, + table_in_use[i].target, ctl_challenges.challenges[j].gamma, pi.ctl_challenges.challenges[j].gamma, ); @@ -978,18 +989,18 @@ where let current_state_before = pis[i].challenger_state_before.as_ref(); let current_state_after = pis[i].challenger_state_after.as_ref(); for j in 0..state_len { - if KECCAK_TABLES_INDICES.contains(&i) { + if OPTIONAL_TABLE_INDICES.contains(&i) { // Ensure the challenger state: - // 1) prev == current_before when using Keccak + // 1) prev == current_before when using this table builder.conditional_assert_eq( - use_keccak_tables.target, + table_in_use[i].target, prev_state[j], current_state_before[j], ); - // 2) Update prev <- current_after when using Keccak - // 3) Keep prev <- prev when skipping Keccak + // 2) Update prev <- current_after when using this table + // 3) Keep prev <- prev when skipping this table prev_state[j] = - builder.select(use_keccak_tables, current_state_after[j], prev_state[j]); + builder.select(table_in_use[i], current_state_after[j], prev_state[j]); } else { builder.connect(prev_state[j], current_state_before[j]); prev_state[j] = current_state_after[j]; @@ -1017,11 +1028,10 @@ where .collect_vec(), ); - // Ensure that when Keccak tables are skipped, the Keccak tables' ctl_zs_first - // are all zeros. - for &i in KECCAK_TABLES_INDICES.iter() { + // Ensure that when a table is skipped, the table's ctl_zs_first are all zeros. + for &i in OPTIONAL_TABLE_INDICES.iter() { for &t in pis[i].ctl_zs_first.iter() { - let ctl_check = builder.mul(skip_keccak_tables.target, t); + let ctl_check = builder.mul(table_not_in_use[i].target, t); builder.assert_zero(ctl_check); } } @@ -1055,10 +1065,10 @@ where let inner_verifier_data = builder.random_access_verifier_data(index_verifier_data[i], possible_vks); - if KECCAK_TABLES_INDICES.contains(&i) { + if OPTIONAL_TABLE_INDICES.contains(&i) { builder .conditionally_verify_proof_or_dummy::( - use_keccak_tables, + table_in_use[i], &recursive_proofs[i], &inner_verifier_data, inner_common_data[i], @@ -1101,7 +1111,7 @@ where index_verifier_data, public_values, cyclic_vk, - use_keccak_tables, + table_in_use, } } @@ -2014,16 +2024,7 @@ where for table in 0..NUM_TABLES { let table_circuits = &self.by_table[table]; - if KECCAK_TABLES_INDICES.contains(&table) && !all_proof.use_keccak_tables { - let dummy_proof_data = self.table_dummy_proofs[table] - .as_ref() - .ok_or_else(|| anyhow::format_err!("No dummy_proof_data"))?; - root_inputs.set_target(self.root.index_verifier_data[table], F::ZERO)?; - root_inputs.set_proof_with_pis_target( - &self.root.proof_with_pis[table], - &dummy_proof_data.proof, - )?; - } else { + if all_proof.table_in_use[table] { let stark_proof = &all_proof.multi_proof.stark_proofs[table] .as_ref() .ok_or_else(|| anyhow::format_err!("Unable to get stark proof"))?; @@ -2050,6 +2051,16 @@ where )?; root_inputs .set_proof_with_pis_target(&self.root.proof_with_pis[table], &shrunk_proof)?; + } else { + assert!(OPTIONAL_TABLE_INDICES.contains(&table)); + let dummy_proof_data = self.table_dummy_proofs[table] + .as_ref() + .ok_or_else(|| anyhow::format_err!("No dummy_proof_data"))?; + root_inputs.set_target(self.root.index_verifier_data[table], F::ZERO)?; + root_inputs.set_proof_with_pis_target( + &self.root.proof_with_pis[table], + &dummy_proof_data.proof, + )?; } check_abort_signal(abort_signal.clone())?; @@ -2069,7 +2080,11 @@ where anyhow::Error::msg("Invalid conversion when setting public values targets.") })?; - root_inputs.set_bool_target(self.root.use_keccak_tables, all_proof.use_keccak_tables)?; + self.root + .table_in_use + .iter() + .zip(all_proof.table_in_use.iter()) + .try_for_each(|(target, value)| root_inputs.set_bool_target(*target, *value))?; let root_proof = self.root.circuit.prove(root_inputs)?; @@ -2142,16 +2157,7 @@ where let mut root_inputs = PartialWitness::new(); for table in 0..NUM_TABLES { - if KECCAK_TABLES_INDICES.contains(&table) && !all_proof.use_keccak_tables { - let dummy_proof = self.table_dummy_proofs[table] - .as_ref() - .ok_or_else(|| anyhow::format_err!("Unable to get dummpy proof"))?; - root_inputs.set_target(self.root.index_verifier_data[table], F::ZERO)?; - root_inputs.set_proof_with_pis_target( - &self.root.proof_with_pis[table], - &dummy_proof.proof, - )?; - } else { + if all_proof.table_in_use[table] { let (table_circuit, index_verifier_data) = &table_circuits[table] .as_ref() .ok_or_else(|| anyhow::format_err!("Unable to get circuits"))?; @@ -2166,6 +2172,16 @@ where table_circuit.shrink(stark_proof, &all_proof.multi_proof.ctl_challenges)?; root_inputs .set_proof_with_pis_target(&self.root.proof_with_pis[table], &shrunk_proof)?; + } else { + assert!(OPTIONAL_TABLE_INDICES.contains(&table)); + let dummy_proof = self.table_dummy_proofs[table] + .as_ref() + .ok_or_else(|| anyhow::format_err!("Unable to get dummpy proof"))?; + root_inputs.set_target(self.root.index_verifier_data[table], F::ZERO)?; + root_inputs.set_proof_with_pis_target( + &self.root.proof_with_pis[table], + &dummy_proof.proof, + )?; } check_abort_signal(abort_signal.clone())?; @@ -2185,7 +2201,11 @@ where anyhow::Error::msg("Invalid conversion when setting public values targets.") })?; - root_inputs.set_bool_target(self.root.use_keccak_tables, all_proof.use_keccak_tables)?; + self.root + .table_in_use + .iter() + .zip(all_proof.table_in_use.iter()) + .try_for_each(|(target, value)| root_inputs.set_bool_target(*target, *value))?; let root_proof = self.root.circuit.prove(root_inputs)?; @@ -3047,6 +3067,12 @@ where }); } + log::debug!( + "Table: {:?}, degree: {}, shrinking_wrappers_len: {}", + table, + degree_bits, + shrinking_wrappers.len() + ); Self { initial_wrapper, shrinking_wrappers, diff --git a/evm_arithmetization/src/generation/mod.rs b/evm_arithmetization/src/generation/mod.rs index a4994f686..db845f490 100644 --- a/evm_arithmetization/src/generation/mod.rs +++ b/evm_arithmetization/src/generation/mod.rs @@ -19,7 +19,8 @@ use GlobalMetadata::{ StateTrieRootDigestBefore, TransactionTrieRootDigestAfter, TransactionTrieRootDigestBefore, }; -use crate::all_stark::{AllStark, NUM_TABLES}; +use crate::all_stark::Table::MemAfter; +use crate::all_stark::{AllStark, Table, NUM_TABLES, OPTIONAL_TABLE_INDICES}; use crate::cpu::columns::CpuColumnsView; use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; @@ -488,7 +489,7 @@ fn get_all_memory_address_and_values(memory_before: &MemoryState) -> Vec<(Memory pub struct TablesWithPVs { pub tables: [Vec>; NUM_TABLES], - pub use_keccak_tables: bool, + pub table_in_use: [bool; NUM_TABLES], pub public_values: PublicValues, } @@ -583,7 +584,28 @@ pub fn generate_traces, const D: usize>( mem_after: MemCap::default(), }; - let use_keccak_tables = !state.traces.keccak_inputs.is_empty(); + let mut table_in_use = [true; NUM_TABLES]; + if state.traces.keccak_inputs.is_empty() && OPTIONAL_TABLE_INDICES.contains(&Table::Keccak) { + assert!(OPTIONAL_TABLE_INDICES.contains(&Table::KeccakSponge)); + log::debug!("Keccak and KeccakSponge tables not in use"); + table_in_use[*Table::Keccak] = false; + table_in_use[*Table::KeccakSponge] = false; + } + if state.traces.logic_ops.is_empty() && OPTIONAL_TABLE_INDICES.contains(&Table::Logic) { + log::debug!("Logic table not in use"); + table_in_use[*Table::Logic] = false; + } + if state.traces.byte_packing_ops.is_empty() + && OPTIONAL_TABLE_INDICES.contains(&Table::BytePacking) + { + log::debug!("BytePacking table not in use"); + table_in_use[*Table::BytePacking] = false; + } + #[cfg(feature = "cdk_erigon")] + if state.traces.poseidon_ops.is_empty() && OPTIONAL_TABLE_INDICES.contains(&Table::Poseidon) { + log::debug!("Poseidon table not in use"); + table_in_use[*Table::Poseidon] = false; + } let tables = timed!( timing, @@ -598,9 +620,16 @@ pub fn generate_traces, const D: usize>( ) ); + let is_last_segment = + segment_data.registers_after.program_counter == KERNEL.global_labels["halt"]; + if is_last_segment && OPTIONAL_TABLE_INDICES.contains(&MemAfter) { + log::debug!("MemAfter table not in use"); + table_in_use[*MemAfter] = false; + } + Ok(TablesWithPVs { tables, - use_keccak_tables, + table_in_use, public_values, }) } diff --git a/evm_arithmetization/src/get_challenges.rs b/evm_arithmetization/src/get_challenges.rs index c7471471b..23a46d5c5 100644 --- a/evm_arithmetization/src/get_challenges.rs +++ b/evm_arithmetization/src/get_challenges.rs @@ -254,7 +254,7 @@ pub mod testing { use starky::lookup::{get_grand_product_challenge_set, GrandProductChallengeSet}; use starky::proof::StarkProofChallenges; - use crate::all_stark::KECCAK_TABLES_INDICES; + use crate::all_stark::OPTIONAL_TABLE_INDICES; use crate::get_challenges::observe_public_values; use crate::proof::*; use crate::witness::errors::ProgramError; @@ -282,7 +282,7 @@ pub mod testing { if let Some(stark_proof) = stark_proof { challenger.observe_cap(&stark_proof.proof.trace_cap); } else { - assert!(KECCAK_TABLES_INDICES.contains(&i) && !self.use_keccak_tables); + assert!(OPTIONAL_TABLE_INDICES.contains(&i) && !self.table_in_use[i]); let zero_cap = vec![F::ZERO; config.fri_config.num_cap_elements() * NUM_HASH_OUT_ELTS]; challenger.observe_elements(&zero_cap); diff --git a/evm_arithmetization/src/proof.rs b/evm_arithmetization/src/proof.rs index b0de028af..f501a96d3 100644 --- a/evm_arithmetization/src/proof.rs +++ b/evm_arithmetization/src/proof.rs @@ -4,6 +4,7 @@ use ethereum_types::{Address, H256, U256}; use itertools::Itertools; use plonky2::field::extension::Extendable; use plonky2::hash::hash_types::{HashOutTarget, MerkleCapTarget, RichField, NUM_HASH_OUT_ELTS}; +use plonky2::hash::merkle_tree::MerkleCap; use plonky2::iop::target::{BoolTarget, Target}; use plonky2::plonk::circuit_builder::CircuitBuilder; use plonky2::plonk::config::{GenericConfig, GenericHashOut, Hasher}; @@ -47,10 +48,9 @@ pub struct AllProof, C: GenericConfig, co pub multi_proof: MultiProof, /// Public memory values used for the recursive proofs. pub public_values: PublicValues, - /// A flag indicating whether the Keccak and KeccakSponge tables contain - /// only padding values (i.e., no meaningful data). This is set to false - /// when no actual Keccak operations were performed. - pub use_keccak_tables: bool, + /// A flag indicating whether the table only contains padding values (i.e., + /// no meaningful data). + pub table_in_use: [bool; NUM_TABLES], } impl, C: GenericConfig, const D: usize> AllProof { @@ -603,6 +603,22 @@ impl MemCap { Self { mem_cap } } + + pub fn from_merkle_cap>(merkle_cap: MerkleCap) -> Self { + let mem_cap = merkle_cap + .0 + .iter() + .map(|h| { + h.to_vec() + .iter() + .map(|hi| hi.to_canonical_u64().into()) + .collect::>() + .try_into() + .unwrap() + }) + .collect::>(); + Self { mem_cap } + } } /// Memory values which are public. diff --git a/evm_arithmetization/src/prover.rs b/evm_arithmetization/src/prover.rs index 4192db6c8..698c23934 100644 --- a/evm_arithmetization/src/prover.rs +++ b/evm_arithmetization/src/prover.rs @@ -2,15 +2,15 @@ use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; use anyhow::{anyhow, Result}; +use ethereum_types::U256; use itertools::Itertools; use once_cell::sync::Lazy; use plonky2::field::extension::Extendable; use plonky2::field::polynomial::PolynomialValues; use plonky2::fri::oracle::PolynomialBatch; use plonky2::hash::hash_types::RichField; -use plonky2::hash::merkle_tree::MerkleCap; use plonky2::iop::challenger::Challenger; -use plonky2::plonk::config::{GenericConfig, GenericHashOut}; +use plonky2::plonk::config::GenericConfig; use plonky2::timed; use plonky2::util::timing::TimingTree; use starky::config::StarkConfig; @@ -20,7 +20,7 @@ use starky::proof::StarkProofWithMetadata; use starky::prover::prove_with_commitment; use starky::stark::Stark; -use crate::all_stark::{AllStark, Table, KECCAK_TABLES_INDICES, NUM_TABLES}; +use crate::all_stark::{AllStark, Table, NUM_TABLES, OPTIONAL_TABLE_INDICES}; use crate::cpu::kernel::aggregator::KERNEL; use crate::generation::{generate_traces, GenerationInputs, TrimmedGenerationInputs}; use crate::get_challenges::observe_public_values; @@ -59,7 +59,7 @@ where all_stark, config, tables_with_pvs.tables, - tables_with_pvs.use_keccak_tables, + tables_with_pvs.table_in_use, &mut tables_with_pvs.public_values, timing, abort_signal, @@ -73,7 +73,7 @@ pub(crate) fn prove_with_traces( all_stark: &AllStark, config: &StarkConfig, trace_poly_values: [Vec>; NUM_TABLES], - use_keccak_tables: bool, + table_in_use: [bool; NUM_TABLES], public_values: &mut PublicValues, timing: &mut TimingTree, abort_signal: Option>, @@ -117,7 +117,7 @@ where .collect::>(); let mut challenger = Challenger::::new(); for (i, cap) in trace_caps.iter().enumerate() { - if KECCAK_TABLES_INDICES.contains(&i) && !use_keccak_tables { + if OPTIONAL_TABLE_INDICES.contains(&i) && !table_in_use[i] { // Observe zero merkle caps when skipping Keccak tables. let zero_merkle_cap = cap.flatten().iter().map(|_| F::ZERO).collect::>(); challenger.observe_elements(&zero_merkle_cap); @@ -151,7 +151,7 @@ where config, &trace_poly_values, trace_commitments, - use_keccak_tables, + table_in_use, ctl_data_per_table, &mut challenger, &ctl_challenges, @@ -159,34 +159,8 @@ where abort_signal, )? ); - public_values.mem_before = MemCap { - mem_cap: mem_before_cap - .0 - .iter() - .map(|h| { - h.to_vec() - .iter() - .map(|hi| hi.to_canonical_u64().into()) - .collect::>() - .try_into() - .unwrap() - }) - .collect::>(), - }; - public_values.mem_after = MemCap { - mem_cap: mem_after_cap - .0 - .iter() - .map(|h| { - h.to_vec() - .iter() - .map(|hi| hi.to_canonical_u64().into()) - .collect::>() - .try_into() - .unwrap() - }) - .collect::>(), - }; + public_values.mem_before = mem_before_cap; + public_values.mem_after = mem_after_cap; // This is an expensive check, hence is only run when `debug_assertions` are // enabled. @@ -215,14 +189,14 @@ where ctl_challenges, }, public_values: public_values.clone(), - use_keccak_tables, + table_in_use, }) } -type ProofWithMemCaps = ( +type ProofWithMemCaps = ( [Option>; NUM_TABLES], - MerkleCap, - MerkleCap, + MemCap, + MemCap, ); /// Generates a proof for each STARK. @@ -239,80 +213,85 @@ fn prove_with_commitments( config: &StarkConfig, trace_poly_values: &[Vec>; NUM_TABLES], trace_commitments: Vec>, - use_keccak_tables: bool, + table_in_use: [bool; NUM_TABLES], ctl_data_per_table: [CtlData; NUM_TABLES], challenger: &mut Challenger, ctl_challenges: &GrandProductChallengeSet, timing: &mut TimingTree, abort_signal: Option>, -) -> Result> +) -> Result> where F: RichField + Extendable, C: GenericConfig, { macro_rules! prove_table { ($stark:ident, $table:expr) => { - timed!( - timing, - &format!("prove {} STARK", stringify!($stark)), - prove_single_table( - &all_stark.$stark, - config, - &trace_poly_values[*$table], - &trace_commitments[*$table], - &ctl_data_per_table[*$table], - ctl_challenges, - challenger, + if table_in_use[*$table] { + Some(timed!( timing, - abort_signal.clone(), - )? - ) + &format!("prove {} STARK", stringify!($stark)), + prove_single_table( + &all_stark.$stark, + config, + &trace_poly_values[*$table], + &trace_commitments[*$table], + &ctl_data_per_table[*$table], + ctl_challenges, + challenger, + timing, + abort_signal.clone(), + )? + )) + } else { + None + } }; } - let (arithmetic_proof, _) = prove_table!(arithmetic_stark, Table::Arithmetic); - let (byte_packing_proof, _) = prove_table!(byte_packing_stark, Table::BytePacking); - let (cpu_proof, _) = prove_table!(cpu_stark, Table::Cpu); - let keccak_proof = if use_keccak_tables { - Some(prove_table!(keccak_stark, Table::Keccak).0) - } else { - None - }; - let keccak_sponge_proof = if use_keccak_tables { - Some(prove_table!(keccak_sponge_stark, Table::KeccakSponge).0) - } else { - None - }; - let (logic_proof, _) = prove_table!(logic_stark, Table::Logic); - let (memory_proof, _) = prove_table!(memory_stark, Table::Memory); - let (mem_before_proof, mem_before_cap) = prove_table!(mem_before_stark, Table::MemBefore); - let (mem_after_proof, mem_after_cap) = prove_table!(mem_after_stark, Table::MemAfter); + let arithmetic_proof = prove_table!(arithmetic_stark, Table::Arithmetic); + let byte_packing_proof = prove_table!(byte_packing_stark, Table::BytePacking); + let cpu_proof = prove_table!(cpu_stark, Table::Cpu); + let keccak_proof = prove_table!(keccak_stark, Table::Keccak); + let keccak_sponge_proof = prove_table!(keccak_sponge_stark, Table::KeccakSponge); + let logic_proof = prove_table!(logic_stark, Table::Logic); + let memory_proof = prove_table!(memory_stark, Table::Memory); + let mem_before_proof = prove_table!(mem_before_stark, Table::MemBefore); + let mem_after_proof = prove_table!(mem_after_stark, Table::MemAfter); + + let mem_before_cap = trace_commitments[*Table::MemBefore].merkle_tree.cap.clone(); + let mem_before_cap = MemCap::from_merkle_cap(mem_before_cap); + let mem_after_cap = trace_commitments[*Table::MemAfter].merkle_tree.cap.clone(); + let mut mem_after_cap = MemCap::from_merkle_cap(mem_after_cap); + if !table_in_use[*Table::MemAfter] { + for hash in &mut mem_after_cap.mem_cap { + for element in hash.iter_mut() { + *element = U256::zero(); + } + } + } #[cfg(feature = "cdk_erigon")] - let (poseidon_proof, _) = prove_table!(poseidon_stark, Table::Poseidon); + let poseidon_proof = prove_table!(poseidon_stark, Table::Poseidon); Ok(( [ - Some(arithmetic_proof), - Some(byte_packing_proof), - Some(cpu_proof), + arithmetic_proof, + byte_packing_proof, + cpu_proof, keccak_proof, keccak_sponge_proof, - Some(logic_proof), - Some(memory_proof), - Some(mem_before_proof), - Some(mem_after_proof), + logic_proof, + memory_proof, + mem_before_proof, + mem_after_proof, #[cfg(feature = "cdk_erigon")] - Some(poseidon_proof), + poseidon_proof, ], mem_before_cap, mem_after_cap, )) } -type ProofSingleWithCap = - (StarkProofWithMetadata, MerkleCap); - /// Computes a proof for a single STARK table, including: /// - the initial state of the challenger, /// - all the requires Merkle caps, @@ -329,7 +308,7 @@ pub(crate) fn prove_single_table( challenger: &mut Challenger, timing: &mut TimingTree, abort_signal: Option>, -) -> Result> +) -> Result> where F: RichField + Extendable, C: GenericConfig, @@ -356,7 +335,7 @@ where init_challenger_state, })?; - Ok((proof, trace_commitment.merkle_tree.cap.clone())) + Ok(proof) } /// Utility method that checks whether a kill signal has been emitted by one of diff --git a/evm_arithmetization/src/testing_utils.rs b/evm_arithmetization/src/testing_utils.rs index 039dc2504..7e10801f7 100644 --- a/evm_arithmetization/src/testing_utils.rs +++ b/evm_arithmetization/src/testing_utils.rs @@ -18,7 +18,7 @@ use crate::generation::{TrieInputs, TrimmedGenerationInputs}; use crate::proof::TrieRoots; use crate::witness::operation::Operation; use crate::{ - generation::mpt::AccountRlp, proof::BlockMetadata, util::h2u, GenerationInputs, + generation::mpt::AccountRlp, logic, proof::BlockMetadata, util::h2u, GenerationInputs, GenerationSegmentData, SegmentDataIterator, }; @@ -229,8 +229,12 @@ pub fn segment_with_empty_tables() -> Result<( SegmentDataIterator::::new(&payload, max_cpu_len_log); let (trimmed_inputs, segment_data) = segment_iterator.next().unwrap()?; + // Ensures that there are no Keccak and Logic ops in the segment. let opcode_counts = &segment_data.opcode_counts; assert!(!opcode_counts.contains_key(&Operation::KeccakGeneral)); + assert!(!opcode_counts.contains_key(&Operation::BinaryLogic(logic::Op::And))); + assert!(!opcode_counts.contains_key(&Operation::BinaryLogic(logic::Op::Or))); + assert!(!opcode_counts.contains_key(&Operation::BinaryLogic(logic::Op::Xor))); Ok((trimmed_inputs, segment_data)) } diff --git a/evm_arithmetization/src/verifier.rs b/evm_arithmetization/src/verifier.rs index e7b0c5198..1d4fdf387 100644 --- a/evm_arithmetization/src/verifier.rs +++ b/evm_arithmetization/src/verifier.rs @@ -93,7 +93,7 @@ pub mod testing { use starky::stark::Stark; use starky::verifier::verify_stark_proof_with_challenges; - use crate::all_stark::{Table, MEMORY_CTL_IDX, NUM_CTLS}; + use crate::all_stark::{Table, MEMORY_CTL_IDX, NUM_CTLS, OPTIONAL_TABLE_INDICES}; use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; use crate::get_challenges::testing::AllProofChallenges; @@ -124,6 +124,22 @@ pub mod testing { self.poseidon_stark.num_lookup_helper_columns(config), ] } + + pub fn get_constraint_degree(&self, table: Table) -> usize { + match table { + Table::Arithmetic => self.arithmetic_stark.constraint_degree(), + Table::BytePacking => self.byte_packing_stark.constraint_degree(), + Table::Cpu => self.cpu_stark.constraint_degree(), + Table::Keccak => self.keccak_stark.constraint_degree(), + Table::KeccakSponge => self.keccak_sponge_stark.constraint_degree(), + Table::Logic => self.logic_stark.constraint_degree(), + Table::Memory => self.memory_stark.constraint_degree(), + Table::MemBefore => self.mem_before_stark.constraint_degree(), + Table::MemAfter => self.mem_after_stark.constraint_degree(), + #[cfg(feature = "cdk_erigon")] + Table::Poseidon => self.poseidon_stark.constraint_degree(), + } + } } fn verify_initial_memory< @@ -191,48 +207,48 @@ pub mod testing { macro_rules! verify_table { ($stark:ident, $table:expr) => { - let stark_proof = &stark_proofs[*$table] - .as_ref() - .expect("Missing stark_proof") - .proof; - let ctl_vars = { - let (total_num_helpers, _, num_helpers_by_ctl) = - CrossTableLookup::num_ctl_helpers_zs_all( - &all_stark.cross_table_lookups, - *$table, - config.num_challenges, - $stark.constraint_degree(), - ); - CtlCheckVars::from_proof( - *$table, - &stark_proof, - &all_stark.cross_table_lookups, - &ctl_challenges, - num_lookup_columns[*$table], - total_num_helpers, - &num_helpers_by_ctl, - ) - }; - verify_stark_proof_with_challenges( - $stark, - stark_proof, - &stark_challenges[*$table] + if !OPTIONAL_TABLE_INDICES.contains(&*$table) || all_proof.table_in_use[*$table] { + let stark_proof = &stark_proofs[*$table] .as_ref() - .expect("Missing challenges"), - Some(&ctl_vars), - &[], - config, - )?; + .expect("Missing stark_proof") + .proof; + let ctl_vars = { + let (total_num_helpers, _, num_helpers_by_ctl) = + CrossTableLookup::num_ctl_helpers_zs_all( + &all_stark.cross_table_lookups, + *$table, + config.num_challenges, + $stark.constraint_degree(), + ); + CtlCheckVars::from_proof( + *$table, + &stark_proof, + &all_stark.cross_table_lookups, + &ctl_challenges, + num_lookup_columns[*$table], + total_num_helpers, + &num_helpers_by_ctl, + ) + }; + verify_stark_proof_with_challenges( + $stark, + stark_proof, + &stark_challenges[*$table] + .as_ref() + .expect("Missing challenges"), + Some(&ctl_vars), + &[], + config, + )?; + } }; } verify_table!(arithmetic_stark, Table::Arithmetic); verify_table!(byte_packing_stark, Table::BytePacking); verify_table!(cpu_stark, Table::Cpu); - if all_proof.use_keccak_tables { - verify_table!(keccak_stark, Table::Keccak); - verify_table!(keccak_sponge_stark, Table::KeccakSponge); - } + verify_table!(keccak_stark, Table::Keccak); + verify_table!(keccak_sponge_stark, Table::KeccakSponge); verify_table!(logic_stark, Table::Logic); verify_table!(memory_stark, Table::Memory); verify_table!(mem_before_stark, Table::MemBefore); @@ -263,37 +279,34 @@ pub mod testing { let all_ctls = &all_stark.cross_table_lookups; + let table_all = Table::all(); + let ctl_zs_first_values = core::array::from_fn(|i| { + let table = table_all[i]; + if let Some(stark_proof) = &stark_proofs[i] { + stark_proof + .proof + .openings + .ctl_zs_first + .as_ref() + .expect("Missing ctl_zs") + .clone() + } else if OPTIONAL_TABLE_INDICES.contains(&table) { + let degree = all_stark.get_constraint_degree(table); + let (_, n, _) = CrossTableLookup::num_ctl_helpers_zs_all( + all_ctls, + i, + config.num_challenges, + degree, + ); + vec![F::ZERO; n] + } else { + panic!("Unable to find stark_proof for table {:?}", table); + } + }); + verify_cross_table_lookups::( cross_table_lookups, - core::array::from_fn(|i| { - if let Some(stark_proof) = &stark_proofs[i] { - stark_proof - .proof - .openings - .ctl_zs_first - .as_ref() - .expect("Missing ctl_zs") - .clone() - } else if i == *Table::Keccak { - let (_, n, _) = CrossTableLookup::num_ctl_helpers_zs_all( - all_ctls, - *Table::Keccak, - config.num_challenges, - keccak_stark.constraint_degree(), - ); - vec![F::ZERO; n] - } else if i == *Table::KeccakSponge { - let (_, n, _) = CrossTableLookup::num_ctl_helpers_zs_all( - all_ctls, - *Table::KeccakSponge, - config.num_challenges, - keccak_sponge_stark.constraint_degree(), - ); - vec![F::ZERO; n] - } else { - panic!("Unable to find stark_proof"); - } - }), + ctl_zs_first_values, &extra_looking_sums, config, ) From f8c5f9cdab1dd2bbf7e54cd23a597e1c1b2db3e8 Mon Sep 17 00:00:00 2001 From: Sai <135601871+sai-deng@users.noreply.github.com> Date: Sun, 20 Oct 2024 00:03:12 +0000 Subject: [PATCH 2/2] fix (#738) --- evm_arithmetization/src/generation/mod.rs | 6 ++--- evm_arithmetization/src/witness/traces.rs | 31 +++++++++++++---------- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/evm_arithmetization/src/generation/mod.rs b/evm_arithmetization/src/generation/mod.rs index db845f490..6ec02ea73 100644 --- a/evm_arithmetization/src/generation/mod.rs +++ b/evm_arithmetization/src/generation/mod.rs @@ -607,7 +607,7 @@ pub fn generate_traces, const D: usize>( table_in_use[*Table::Poseidon] = false; } - let tables = timed!( + let (tables, final_len) = timed!( timing, "convert trace data to tables", state.traces.into_tables( @@ -620,9 +620,7 @@ pub fn generate_traces, const D: usize>( ) ); - let is_last_segment = - segment_data.registers_after.program_counter == KERNEL.global_labels["halt"]; - if is_last_segment && OPTIONAL_TABLE_INDICES.contains(&MemAfter) { + if final_len == 0 && OPTIONAL_TABLE_INDICES.contains(&MemAfter) { log::debug!("MemAfter table not in use"); table_in_use[*MemAfter] = false; } diff --git a/evm_arithmetization/src/witness/traces.rs b/evm_arithmetization/src/witness/traces.rs index cb9e605e8..e1daa53b0 100644 --- a/evm_arithmetization/src/witness/traces.rs +++ b/evm_arithmetization/src/witness/traces.rs @@ -144,7 +144,7 @@ impl Traces { mut trace_lengths: TraceCheckpoint, config: &StarkConfig, timing: &mut TimingTree, - ) -> [Vec>; NUM_TABLES] + ) -> ([Vec>; NUM_TABLES], usize) where T: RichField + Extendable, { @@ -240,19 +240,22 @@ impl Traces { final_values.len() ); - [ - arithmetic_trace, - byte_packing_trace, - cpu_trace, - keccak_trace, - keccak_sponge_trace, - logic_trace, - memory_trace, - mem_before_trace, - mem_after_trace, - #[cfg(feature = "cdk_erigon")] - poseidon_trace, - ] + ( + [ + arithmetic_trace, + byte_packing_trace, + cpu_trace, + keccak_trace, + keccak_sponge_trace, + logic_trace, + memory_trace, + mem_before_trace, + mem_after_trace, + #[cfg(feature = "cdk_erigon")] + poseidon_trace, + ], + final_values.len(), + ) } }