From c9391be0244b5aba207dfc72cac100028b4eea3e Mon Sep 17 00:00:00 2001 From: Robin Salen <30937548+Nashtare@users.noreply.github.com> Date: Mon, 16 Oct 2023 08:53:42 -0400 Subject: [PATCH] Update check_ctls with extra looking values (#1290) --- evm/src/cross_table_lookup.rs | 17 +++- evm/src/prover.rs | 27 +++++-- evm/src/verifier.rs | 146 ++++++++++++++++++++++++++++++++++ 3 files changed, 182 insertions(+), 8 deletions(-) diff --git a/evm/src/cross_table_lookup.rs b/evm/src/cross_table_lookup.rs index 2ab9dbf8..621403f9 100644 --- a/evm/src/cross_table_lookup.rs +++ b/evm/src/cross_table_lookup.rs @@ -838,13 +838,13 @@ pub(crate) mod testutils { type MultiSet = HashMap, Vec<(Table, usize)>>; /// Check that the provided traces and cross-table lookups are consistent. - #[allow(unused)] // TODO: used later? pub(crate) fn check_ctls( trace_poly_values: &[Vec>], cross_table_lookups: &[CrossTableLookup], + extra_memory_looking_values: &[Vec], ) { for (i, ctl) in cross_table_lookups.iter().enumerate() { - check_ctl(trace_poly_values, ctl, i); + check_ctl(trace_poly_values, ctl, i, extra_memory_looking_values); } } @@ -852,6 +852,7 @@ pub(crate) mod testutils { trace_poly_values: &[Vec>], ctl: &CrossTableLookup, ctl_index: usize, + extra_memory_looking_values: &[Vec], ) { let CrossTableLookup { looking_tables, @@ -868,6 +869,18 @@ pub(crate) mod testutils { } process_table(trace_poly_values, looked_table, &mut looked_multiset); + // Extra looking values for memory + if ctl_index == Table::Memory as usize { + for row in extra_memory_looking_values.iter() { + // The table and the row index don't matter here, as we just want to enforce + // that the special extra values do appear when looking against the Memory table. + looking_multiset + .entry(row.to_vec()) + .or_default() + .push((Table::Cpu, 0)); + } + } + let empty = &vec![]; // Check that every row in the looking tables appears in the looked table the same number of times. for (row, looking_locations) in &looking_multiset { diff --git a/evm/src/prover.rs b/evm/src/prover.rs index 5aa9dc6f..c5729a57 100644 --- a/evm/src/prover.rs +++ b/evm/src/prover.rs @@ -1,5 +1,3 @@ -use std::any::type_name; - use anyhow::{ensure, Result}; use itertools::Itertools; use once_cell::sync::Lazy; @@ -35,6 +33,10 @@ use crate::lookup::{lookup_helper_columns, Lookup, LookupCheckVars}; use crate::proof::{AllProof, PublicValues, StarkOpeningSet, StarkProof, StarkProofWithMetadata}; use crate::stark::Stark; use crate::vanishing_poly::eval_vanishing_poly; +#[cfg(test)] +use crate::{ + cross_table_lookup::testutils::check_ctls, verifier::testutils::get_memory_extra_looking_values, +}; /// Generate traces, then create all STARK proofs. pub fn prove( @@ -142,7 +144,7 @@ where prove_with_commitments( all_stark, config, - trace_poly_values, + &trace_poly_values, trace_commitments, ctl_data_per_table, &mut challenger, @@ -151,6 +153,15 @@ where )? ); + #[cfg(test)] + { + check_ctls( + &trace_poly_values, + &all_stark.cross_table_lookups, + &get_memory_extra_looking_values(&public_values), + ); + } + Ok(AllProof { stark_proofs, ctl_challenges, @@ -161,7 +172,7 @@ where fn prove_with_commitments( all_stark: &AllStark, config: &StarkConfig, - trace_poly_values: [Vec>; NUM_TABLES], + trace_poly_values: &[Vec>; NUM_TABLES], trace_commitments: Vec>, ctl_data_per_table: [CtlData; NUM_TABLES], challenger: &mut Challenger, @@ -365,7 +376,9 @@ where challenger.observe_cap(&auxiliary_polys_cap); let alphas = challenger.get_n_challenges(config.num_challenges); - if cfg!(test) { + + #[cfg(test)] + { check_constraints( stark, trace_commitment, @@ -378,6 +391,7 @@ where num_lookup_columns, ); } + let quotient_polys = timed!( timing, "compute quotient polys", @@ -606,6 +620,7 @@ where .collect() } +#[cfg(test)] /// Check that all constraints evaluate to zero on `H`. /// Can also be used to check the degree of the constraints by evaluating on a larger subgroup. fn check_constraints<'a, F, C, S, const D: usize>( @@ -705,7 +720,7 @@ fn check_constraints<'a, F, C, S, const D: usize>( assert!( v.iter().all(|x| x.is_zero()), "Constraint failed in {}", - type_name::() + std::any::type_name::() ); } } diff --git a/evm/src/verifier.rs b/evm/src/verifier.rs index d3f06c01..04450b46 100644 --- a/evm/src/verifier.rs +++ b/evm/src/verifier.rs @@ -455,6 +455,152 @@ fn eval_l_0_and_l_last(log_n: usize, x: F) -> (F, F) { (z_x * invs[0], z_x * invs[1]) } +#[cfg(test)] +pub(crate) mod testutils { + use super::*; + + /// Output all the extra memory rows that don't appear in the CPU trace but are + /// necessary to correctly check the MemoryStark CTL. + pub(crate) fn get_memory_extra_looking_values( + public_values: &PublicValues, + ) -> Vec> + where + F: RichField + Extendable, + { + // Add metadata and tries writes. + let fields = [ + ( + GlobalMetadata::BlockBeneficiary, + U256::from_big_endian(&public_values.block_metadata.block_beneficiary.0), + ), + ( + GlobalMetadata::BlockTimestamp, + public_values.block_metadata.block_timestamp, + ), + ( + GlobalMetadata::BlockNumber, + public_values.block_metadata.block_number, + ), + ( + GlobalMetadata::BlockRandom, + public_values.block_metadata.block_random.into_uint(), + ), + ( + GlobalMetadata::BlockDifficulty, + public_values.block_metadata.block_difficulty, + ), + ( + GlobalMetadata::BlockGasLimit, + public_values.block_metadata.block_gaslimit, + ), + ( + GlobalMetadata::BlockChainId, + public_values.block_metadata.block_chain_id, + ), + ( + GlobalMetadata::BlockBaseFee, + public_values.block_metadata.block_base_fee, + ), + ( + GlobalMetadata::BlockCurrentHash, + h2u(public_values.block_hashes.cur_hash), + ), + ( + GlobalMetadata::BlockGasUsed, + public_values.block_metadata.block_gas_used, + ), + ( + GlobalMetadata::TxnNumberBefore, + public_values.extra_block_data.txn_number_before, + ), + ( + GlobalMetadata::TxnNumberAfter, + public_values.extra_block_data.txn_number_after, + ), + ( + GlobalMetadata::BlockGasUsedBefore, + public_values.extra_block_data.gas_used_before, + ), + ( + GlobalMetadata::BlockGasUsedAfter, + public_values.extra_block_data.gas_used_after, + ), + ( + GlobalMetadata::StateTrieRootDigestBefore, + h2u(public_values.trie_roots_before.state_root), + ), + ( + GlobalMetadata::TransactionTrieRootDigestBefore, + h2u(public_values.trie_roots_before.transactions_root), + ), + ( + GlobalMetadata::ReceiptTrieRootDigestBefore, + h2u(public_values.trie_roots_before.receipts_root), + ), + ( + GlobalMetadata::StateTrieRootDigestAfter, + h2u(public_values.trie_roots_after.state_root), + ), + ( + GlobalMetadata::TransactionTrieRootDigestAfter, + h2u(public_values.trie_roots_after.transactions_root), + ), + ( + GlobalMetadata::ReceiptTrieRootDigestAfter, + h2u(public_values.trie_roots_after.receipts_root), + ), + ]; + + let segment = F::from_canonical_u32(Segment::GlobalMetadata as u32); + let mut extra_looking_rows = Vec::new(); + + fields.map(|(field, val)| { + extra_looking_rows.push(add_extra_looking_row(segment, field as usize, val)) + }); + + // Add block bloom writes. + let bloom_segment = F::from_canonical_u32(Segment::GlobalBlockBloom as u32); + for index in 0..8 { + let val = public_values.block_metadata.block_bloom[index]; + extra_looking_rows.push(add_extra_looking_row(bloom_segment, index, val)); + } + + for index in 0..8 { + let val = public_values.extra_block_data.block_bloom_before[index]; + extra_looking_rows.push(add_extra_looking_row(bloom_segment, index + 8, val)); + } + for index in 0..8 { + let val = public_values.extra_block_data.block_bloom_after[index]; + extra_looking_rows.push(add_extra_looking_row(bloom_segment, index + 16, val)); + } + + // Add Blockhashes writes. + let block_hashes_segment = F::from_canonical_u32(Segment::BlockHashes as u32); + for index in 0..256 { + let val = h2u(public_values.block_hashes.prev_hashes[index]); + extra_looking_rows.push(add_extra_looking_row(block_hashes_segment, index, val)); + } + + extra_looking_rows + } + + fn add_extra_looking_row(segment: F, index: usize, val: U256) -> Vec + where + F: RichField + Extendable, + { + let mut row = vec![F::ZERO; 13]; + row[0] = F::ZERO; // is_read + row[1] = F::ZERO; // context + row[2] = segment; + row[3] = F::from_canonical_usize(index); + + for j in 0..VALUE_LIMBS { + row[j + 4] = F::from_canonical_u32((val >> (j * 32)).low_u32()); + } + row[12] = F::ONE; // timestamp + row + } +} #[cfg(test)] mod tests { use plonky2::field::goldilocks_field::GoldilocksField;