From 06037f814fda3bd8992c5a05a3605e549b19b349 Mon Sep 17 00:00:00 2001 From: Linda Guiga Date: Fri, 14 Apr 2023 21:55:44 +0800 Subject: [PATCH 1/6] Fix the memory CTL and implement the verifier memory bus Co-authored-by: Hamy Ratoanina --- evm/src/all_stark.rs | 16 +- evm/src/cpu/bootstrap_kernel.rs | 3 + evm/src/cpu/kernel/assembler.rs | 3 +- evm/src/cross_table_lookup.rs | 45 +++- evm/src/fixed_recursive_verifier.rs | 263 ++++++++++++++++++- evm/src/generation/mod.rs | 53 +++- evm/src/keccak_sponge/columns.rs | 3 + evm/src/keccak_sponge/keccak_sponge_stark.rs | 62 ++++- evm/src/permutation.rs | 15 +- evm/src/proof.rs | 3 +- evm/src/recursive_verifier.rs | 11 + evm/src/verifier.rs | 154 ++++++++++- evm/src/witness/operation.rs | 7 +- evm/tests/empty_txn_list.rs | 5 +- 14 files changed, 595 insertions(+), 48 deletions(-) diff --git a/evm/src/all_stark.rs b/evm/src/all_stark.rs index b7cb52e5..ae02233c 100644 --- a/evm/src/all_stark.rs +++ b/evm/src/all_stark.rs @@ -10,7 +10,7 @@ use crate::config::StarkConfig; use crate::cpu::cpu_stark; use crate::cpu::cpu_stark::CpuStark; use crate::cpu::membus::NUM_GP_CHANNELS; -use crate::cross_table_lookup::{Column, CrossTableLookup, TableWithColumns}; +use crate::cross_table_lookup::{CrossTableLookup, TableWithColumns}; use crate::keccak::keccak_stark; use crate::keccak::keccak_stark::KeccakStark; use crate::keccak_sponge::columns::KECCAK_RATE_BYTES; @@ -97,23 +97,13 @@ impl Table { } pub(crate) fn all_cross_table_lookups() -> Vec> { - let mut ctls = vec![ + vec![ ctl_arithmetic(), ctl_keccak_sponge(), ctl_keccak(), ctl_logic(), ctl_memory(), - ]; - // TODO: Some CTLs temporarily disabled while we get them working. - disable_ctl(&mut ctls[4]); - ctls -} - -fn disable_ctl(ctl: &mut CrossTableLookup) { - for table in &mut ctl.looking_tables { - table.filter_column = Some(Column::zero()); - } - ctl.looked_table.filter_column = Some(Column::zero()); + ] } fn ctl_arithmetic() -> CrossTableLookup { diff --git a/evm/src/cpu/bootstrap_kernel.rs b/evm/src/cpu/bootstrap_kernel.rs index f29c3c1f..4aee617c 100644 --- a/evm/src/cpu/bootstrap_kernel.rs +++ b/evm/src/cpu/bootstrap_kernel.rs @@ -48,6 +48,7 @@ pub(crate) fn generate_bootstrap_kernel(state: &mut GenerationState final_cpu_row.mem_channels[2].value[0] = F::ZERO; // virt final_cpu_row.mem_channels[3].value[0] = F::from_canonical_usize(KERNEL.code.len()); // len final_cpu_row.mem_channels[4].value = KERNEL.code_hash.map(F::from_canonical_u32); + final_cpu_row.mem_channels[4].value.reverse(); keccak_sponge_log( state, MemoryAddress::new(0, Segment::Code, 0), @@ -93,6 +94,7 @@ pub(crate) fn eval_bootstrap_kernel>( for (&expected, actual) in KERNEL .code_hash .iter() + .rev() .zip(local_values.mem_channels.last().unwrap().value) { let expected = P::from(F::from_canonical_u32(expected)); @@ -153,6 +155,7 @@ pub(crate) fn eval_bootstrap_kernel_circuit, const for (&expected, actual) in KERNEL .code_hash .iter() + .rev() .zip(local_values.mem_channels.last().unwrap().value) { let expected = builder.constant_extension(F::Extension::from_canonical_u32(expected)); diff --git a/evm/src/cpu/kernel/assembler.rs b/evm/src/cpu/kernel/assembler.rs index ffa3caca..d708d228 100644 --- a/evm/src/cpu/kernel/assembler.rs +++ b/evm/src/cpu/kernel/assembler.rs @@ -44,9 +44,10 @@ impl Kernel { prover_inputs: HashMap, ) -> Self { let code_hash_bytes = keccak(&code).0; - let code_hash = core::array::from_fn(|i| { + let code_hash_be = core::array::from_fn(|i| { u32::from_le_bytes(core::array::from_fn(|j| code_hash_bytes[i * 4 + j])) }); + let code_hash = code_hash_be.map(u32::from_be); let ordered_labels = global_labels .keys() .cloned() diff --git a/evm/src/cross_table_lookup.rs b/evm/src/cross_table_lookup.rs index 15e186aa..f8e8e53c 100644 --- a/evm/src/cross_table_lookup.rs +++ b/evm/src/cross_table_lookup.rs @@ -542,21 +542,29 @@ pub(crate) fn eval_cross_table_lookup_checks_circuit< pub(crate) fn verify_cross_table_lookups, const D: usize>( cross_table_lookups: &[CrossTableLookup], ctl_zs_lasts: [Vec; NUM_TABLES], + ctl_extra_looking_products: Vec>, config: &StarkConfig, ) -> Result<()> { let mut ctl_zs_openings = ctl_zs_lasts.iter().map(|v| v.iter()).collect::>(); - for CrossTableLookup { - looking_tables, - looked_table, - } in cross_table_lookups.iter() + for ( + CrossTableLookup { + looking_tables, + looked_table, + }, + extra_product_vec, + ) in cross_table_lookups + .iter() + .zip(ctl_extra_looking_products.iter()) { - for _ in 0..config.num_challenges { - let looking_zs_prod = looking_tables + for c in 0..config.num_challenges { + let mut looking_zs_prod = looking_tables .iter() .map(|table| *ctl_zs_openings[table.table as usize].next().unwrap()) .product::(); - let looked_z = *ctl_zs_openings[looked_table.table as usize].next().unwrap(); + looking_zs_prod *= extra_product_vec[c]; + + let looked_z = *ctl_zs_openings[looked_table.table as usize].next().unwrap(); ensure!( looking_zs_prod == looked_z, "Cross-table lookup verification failed." @@ -572,22 +580,31 @@ pub(crate) fn verify_cross_table_lookups_circuit, c builder: &mut CircuitBuilder, cross_table_lookups: Vec>, ctl_zs_lasts: [Vec; NUM_TABLES], + ctl_extra_looking_products: Vec>, inner_config: &StarkConfig, ) { let mut ctl_zs_openings = ctl_zs_lasts.iter().map(|v| v.iter()).collect::>(); - for CrossTableLookup { - looking_tables, - looked_table, - } in cross_table_lookups.into_iter() + for ( + CrossTableLookup { + looking_tables, + looked_table, + }, + extra_product_vec, + ) in cross_table_lookups + .into_iter() + .zip(ctl_extra_looking_products.iter()) { - for _ in 0..inner_config.num_challenges { - let looking_zs_prod = builder.mul_many( + for c in 0..inner_config.num_challenges { + let mut looking_zs_prod = builder.mul_many( looking_tables .iter() .map(|table| *ctl_zs_openings[table.table as usize].next().unwrap()), ); + + looking_zs_prod = builder.mul(looking_zs_prod, extra_product_vec[c]); + let looked_z = *ctl_zs_openings[looked_table.table as usize].next().unwrap(); - builder.connect(looking_zs_prod, looked_z); + builder.connect(looked_z, looking_zs_prod); } } debug_assert!(ctl_zs_openings.iter_mut().all(|iter| iter.next().is_none())); diff --git a/evm/src/fixed_recursive_verifier.rs b/evm/src/fixed_recursive_verifier.rs index 19a026d7..df56f4d0 100644 --- a/evm/src/fixed_recursive_verifier.rs +++ b/evm/src/fixed_recursive_verifier.rs @@ -2,6 +2,7 @@ use core::mem::{self, MaybeUninit}; use std::collections::BTreeMap; use std::ops::Range; +use ethereum_types::BigEndianHash; use hashbrown::HashMap; use itertools::{zip_eq, Itertools}; use plonky2::field::extension::Extendable; @@ -30,20 +31,26 @@ use crate::all_stark::{all_cross_table_lookups, AllStark, Table, NUM_TABLES}; use crate::arithmetic::arithmetic_stark::ArithmeticStark; use crate::config::StarkConfig; use crate::cpu::cpu_stark::CpuStark; +use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; use crate::cross_table_lookup::{verify_cross_table_lookups_circuit, CrossTableLookup}; use crate::generation::GenerationInputs; use crate::keccak::keccak_stark::KeccakStark; use crate::keccak_sponge::keccak_sponge_stark::KeccakSpongeStark; use crate::logic::LogicStark; use crate::memory::memory_stark::MemoryStark; -use crate::permutation::{get_grand_product_challenge_set_target, GrandProductChallengeSet}; -use crate::proof::StarkProofWithMetadata; +use crate::memory::segments::Segment; +use crate::memory::{NUM_CHANNELS, VALUE_LIMBS}; +use crate::permutation::{ + get_grand_product_challenge_set_target, GrandProductChallenge, GrandProductChallengeSet, +}; +use crate::proof::{BlockMetadataTarget, PublicValues, StarkProofWithMetadata}; use crate::prover::prove; use crate::recursive_verifier::{ - add_common_recursion_gates, recursive_stark_circuit, PlonkWrapperCircuit, PublicInputs, - StarkWrapperCircuit, + add_common_recursion_gates, add_virtual_trie_roots, recursive_stark_circuit, + PlonkWrapperCircuit, PublicInputs, StarkWrapperCircuit, }; use crate::stark::Stark; +use crate::util::h160_limbs; /// The recursion threshold. We end a chain of recursive proofs once we reach this size. const THRESHOLD_DEGREE_BITS: usize = 13; @@ -339,6 +346,8 @@ where pub fn new( all_stark: &AllStark, degree_bits_ranges: &[Range; NUM_TABLES], + public_values: &PublicValues, + cpu_trace_len: usize, stark_config: &StarkConfig, ) -> Self { let arithmetic = RecursiveCircuitsForTable::new( @@ -385,7 +394,7 @@ where ); let by_table = [arithmetic, cpu, keccak, keccak_sponge, logic, memory]; - let root = Self::create_root_circuit(&by_table, stark_config); + let root = Self::create_root_circuit(public_values, cpu_trace_len, &by_table, stark_config); let aggregation = Self::create_aggregation_circuit(&root); let block = Self::create_block_circuit(&aggregation); Self { @@ -397,6 +406,8 @@ where } fn create_root_circuit( + public_values: &PublicValues, + cpu_trace_len: usize, by_table: &[RecursiveCircuitsForTable; NUM_TABLES], stark_config: &StarkConfig, ) -> RootCircuitData { @@ -404,6 +415,7 @@ where core::array::from_fn(|i| &by_table[i].final_circuits()[0].common); let mut builder = CircuitBuilder::new(CircuitConfig::standard_recursion_config()); + let recursive_proofs = core::array::from_fn(|i| builder.add_virtual_proof_with_pis(inner_common_data[i])); let pis: [_; NUM_TABLES] = core::array::from_fn(|i| { @@ -453,11 +465,50 @@ where } } + // Extra products to add to the looked last value + let mut extra_looking_products = Vec::new(); + + // Arithmetic + extra_looking_products.push(Vec::new()); + for _ in 0..stark_config.num_challenges { + extra_looking_products[0].push(builder.constant(F::ONE)); + } + + // KeccakSponge + extra_looking_products.push(Vec::new()); + for _ in 0..stark_config.num_challenges { + extra_looking_products[1].push(builder.constant(F::ONE)); + } + + // Keccak + extra_looking_products.push(Vec::new()); + for _ in 0..stark_config.num_challenges { + extra_looking_products[2].push(builder.constant(F::ONE)); + } + + // Logic + extra_looking_products.push(Vec::new()); + for _ in 0..stark_config.num_challenges { + extra_looking_products[3].push(builder.constant(F::ONE)); + } + + // Memory + extra_looking_products.push(Vec::new()); + for c in 0..stark_config.num_challenges { + extra_looking_products[4].push(Self::get_memory_extra_looking_products_circuit( + &mut builder, + public_values, + cpu_trace_len, + ctl_challenges.challenges[c], + )); + } + // Verify the CTL checks. verify_cross_table_lookups_circuit::( &mut builder, all_cross_table_lookups(), pis.map(|p| p.ctl_zs_last), + extra_looking_products, stark_config, ); @@ -505,6 +556,208 @@ where } } + /// Recursive version of `get_memory_extra_looking_products`. + pub(crate) fn get_memory_extra_looking_products_circuit( + builder: &mut CircuitBuilder, + public_values: &PublicValues, + cpu_trace_len: usize, + challenge: GrandProductChallenge, + ) -> Target { + let mut prod = builder.constant(F::ONE); + + // Add metadata writes. + let metadata_target = BlockMetadataTarget { + block_beneficiary: builder.add_virtual_target_arr::<5>(), + block_timestamp: builder.constant(F::from_canonical_u64( + public_values.block_metadata.block_timestamp.as_u64(), + )), + block_difficulty: builder.constant(F::from_canonical_u64( + public_values.block_metadata.block_difficulty.as_u64(), + )), + block_number: builder.constant(F::from_canonical_u64( + public_values.block_metadata.block_number.as_u64(), + )), + block_gaslimit: builder.constant(F::from_canonical_u64( + public_values.block_metadata.block_gaslimit.as_u64(), + )), + block_chain_id: builder.constant(F::from_canonical_u64( + public_values.block_metadata.block_chain_id.as_u64(), + )), + block_base_fee: builder.constant(F::from_canonical_u64( + public_values.block_metadata.block_base_fee.as_u64(), + )), + }; + + let beneficiary_limbs = h160_limbs(public_values.block_metadata.block_beneficiary); + for i in 0..5 { + let limb_target = builder.constant(beneficiary_limbs[i]); + builder.connect(metadata_target.block_beneficiary[i], limb_target); + } + + let block_fields_without_beneficiary = [ + ( + GlobalMetadata::BlockTimestamp, + metadata_target.block_timestamp, + ), + (GlobalMetadata::BlockNumber, metadata_target.block_number), + ( + GlobalMetadata::BlockDifficulty, + metadata_target.block_difficulty, + ), + ( + GlobalMetadata::BlockGasLimit, + metadata_target.block_gaslimit, + ), + (GlobalMetadata::BlockChainId, metadata_target.block_chain_id), + (GlobalMetadata::BlockBaseFee, metadata_target.block_base_fee), + ]; + + let zero = builder.constant(F::ZERO); + let one = builder.constant(F::ONE); + let segment = builder.constant(F::from_canonical_u32(Segment::GlobalMetadata as u32)); + + let row = builder.add_virtual_targets(13); + // is_read + builder.connect(row[0], zero); + // context + builder.connect(row[1], zero); + // segment + builder.connect(row[2], segment); + // virtual + let field_target = builder.constant(F::from_canonical_usize( + GlobalMetadata::BlockBeneficiary as usize, + )); + builder.connect(row[3], field_target); + // values + for j in 0..5 { + builder.connect(row[4 + j], metadata_target.block_beneficiary[j]); + } + for j in 5..VALUE_LIMBS { + builder.connect(row[4 + j], zero); + } + // timestamp + builder.connect(row[12], one); + + let combined = challenge.combine_base_circuit(builder, &row); + prod = builder.mul(prod, combined); + + block_fields_without_beneficiary.map(|(field, target)| { + let row = builder.add_virtual_targets(13); + // is_read + builder.connect(row[0], zero); + // context + builder.connect(row[1], zero); + // segment + builder.connect(row[2], segment); + // virtual + let field_target = builder.constant(F::from_canonical_usize(field as usize)); + builder.connect(row[3], field_target); + // These values only have one cell. + builder.connect(row[4], target); + for j in 1..VALUE_LIMBS { + builder.connect(row[4 + j], zero); + } + // timestamp + builder.connect(row[12], one); + let combined = challenge.combine_base_circuit(builder, &row); + prod = builder.mul(prod, combined); + }); + + // Add public values reads. + let tries_before_target = add_virtual_trie_roots(builder); + for (targets, hash) in [ + tries_before_target.state_root, + tries_before_target.transactions_root, + tries_before_target.receipts_root, + ] + .iter() + .zip([ + public_values.trie_roots_before.state_root, + public_values.trie_roots_before.transactions_root, + public_values.trie_roots_before.receipts_root, + ]) { + for (i, limb) in hash.into_uint().0.into_iter().enumerate() { + let low_limb_target = builder.constant(F::from_canonical_u32(limb as u32)); + let high_limb_target = builder.constant(F::from_canonical_u32((limb >> 32) as u32)); + builder.connect(targets[2 * i], low_limb_target); + builder.connect(targets[2 * i + 1], high_limb_target); + } + } + + let tries_after_target = add_virtual_trie_roots(builder); + for (targets, hash) in [ + tries_after_target.state_root, + tries_after_target.transactions_root, + tries_after_target.receipts_root, + ] + .iter() + .zip([ + public_values.trie_roots_after.state_root, + public_values.trie_roots_after.transactions_root, + public_values.trie_roots_after.receipts_root, + ]) { + for (i, limb) in hash.into_uint().0.into_iter().enumerate() { + let low_limb_target = builder.constant(F::from_canonical_u32(limb as u32)); + let high_limb_target = builder.constant(F::from_canonical_u32((limb >> 32) as u32)); + builder.connect(targets[2 * i], low_limb_target); + builder.connect(targets[2 * i + 1], high_limb_target); + } + } + + let trie_fields = [ + ( + GlobalMetadata::StateTrieRootDigestBefore, + tries_before_target.state_root, + ), + ( + GlobalMetadata::TransactionTrieRootDigestBefore, + tries_before_target.transactions_root, + ), + ( + GlobalMetadata::ReceiptTrieRootDigestBefore, + tries_before_target.receipts_root, + ), + ( + GlobalMetadata::StateTrieRootDigestAfter, + tries_after_target.state_root, + ), + ( + GlobalMetadata::TransactionTrieRootDigestAfter, + tries_after_target.transactions_root, + ), + ( + GlobalMetadata::ReceiptTrieRootDigestAfter, + tries_after_target.receipts_root, + ), + ]; + + let timestamp_target = + builder.constant(F::from_canonical_usize(cpu_trace_len * NUM_CHANNELS + 1)); + trie_fields.map(|(field, targets)| { + let row = builder.add_virtual_targets(13); + // is_read + builder.connect(row[0], one); + // context + builder.connect(row[1], zero); + // segment + builder.connect(row[2], segment); + // virtual + let field_target = builder.constant(F::from_canonical_usize(field as usize)); + builder.connect(row[3], field_target); + // values + for j in 0..VALUE_LIMBS { + builder.connect(row[j + 4], targets[j]); + } + // timestamp + builder.connect(row[12], timestamp_target); + + let combined = challenge.combine_base_circuit(builder, &row); + prod = builder.mul(prod, combined); + }); + + prod + } + fn create_aggregation_circuit( root: &RootCircuitData, ) -> AggregationCircuitData { diff --git a/evm/src/generation/mod.rs b/evm/src/generation/mod.rs index 8bc46cd5..3e31c145 100644 --- a/evm/src/generation/mod.rs +++ b/evm/src/generation/mod.rs @@ -22,7 +22,7 @@ use crate::generation::outputs::{get_outputs, GenerationOutputs}; use crate::generation::state::GenerationState; use crate::memory::segments::Segment; use crate::proof::{BlockMetadata, PublicValues, TrieRoots}; -use crate::witness::memory::{MemoryAddress, MemoryChannel}; +use crate::witness::memory::{MemoryAddress, MemoryChannel, MemoryOp, MemoryOpKind}; use crate::witness::transition::transition; pub mod mpt; @@ -105,6 +105,54 @@ fn apply_metadata_memops, const D: usize>( state.traces.memory_ops.extend(ops); } +fn apply_trie_memops, const D: usize>( + state: &mut GenerationState, + trie_roots_before: &TrieRoots, + trie_roots_after: &TrieRoots, +) { + let fields = [ + ( + GlobalMetadata::StateTrieRootDigestBefore, + trie_roots_before.state_root, + ), + ( + GlobalMetadata::TransactionTrieRootDigestBefore, + trie_roots_before.transactions_root, + ), + ( + GlobalMetadata::ReceiptTrieRootDigestBefore, + trie_roots_before.receipts_root, + ), + ( + GlobalMetadata::StateTrieRootDigestAfter, + trie_roots_after.state_root, + ), + ( + GlobalMetadata::TransactionTrieRootDigestAfter, + trie_roots_after.transactions_root, + ), + ( + GlobalMetadata::ReceiptTrieRootDigestAfter, + trie_roots_after.receipts_root, + ), + ]; + + let channel = MemoryChannel::GeneralPurpose(0); + + let ops = fields.map(|(field, hash)| { + let val = hash.into_uint(); + MemoryOp::new( + channel, + state.traces.cpu.len(), + MemoryAddress::new(0, Segment::GlobalMetadata, field as usize), + MemoryOpKind::Read, + val, + ) + }); + + state.traces.memory_ops.extend(ops); +} + pub(crate) fn generate_traces, const D: usize>( all_stark: &AllStark, inputs: GenerationInputs, @@ -116,7 +164,6 @@ pub(crate) fn generate_traces, const D: usize>( GenerationOutputs, )> { let mut state = GenerationState::::new(inputs.clone(), &KERNEL.code); - apply_metadata_memops(&mut state, &inputs.block_metadata); generate_bootstrap_kernel::(&mut state); @@ -147,6 +194,8 @@ pub(crate) fn generate_traces, const D: usize>( receipts_root: H256::from_uint(&read_metadata(ReceiptTrieRootDigestAfter)), }; + apply_trie_memops(&mut state, &trie_roots_before, &trie_roots_after); + let public_values = PublicValues { trie_roots_before, trie_roots_after, diff --git a/evm/src/keccak_sponge/columns.rs b/evm/src/keccak_sponge/columns.rs index d15e0e4b..44f66a5d 100644 --- a/evm/src/keccak_sponge/columns.rs +++ b/evm/src/keccak_sponge/columns.rs @@ -9,6 +9,7 @@ pub(crate) const KECCAK_RATE_BYTES: usize = 136; pub(crate) const KECCAK_RATE_U32S: usize = KECCAK_RATE_BYTES / 4; pub(crate) const KECCAK_CAPACITY_BYTES: usize = 64; pub(crate) const KECCAK_CAPACITY_U32S: usize = KECCAK_CAPACITY_BYTES / 4; +pub(crate) const KECCAK_DIGEST_BYTES: usize = 32; #[repr(C)] #[derive(Eq, PartialEq, Debug)] @@ -53,6 +54,8 @@ pub(crate) struct KeccakSpongeColumnsView { /// The entire state (rate + capacity) of the sponge, encoded as 32-bit chunks, after the /// permutation is applied. pub updated_state_u32s: [T; KECCAK_WIDTH_U32S], + + pub updated_state_bytes: [T; KECCAK_DIGEST_BYTES], } // `u8` is guaranteed to have a `size_of` of 1. diff --git a/evm/src/keccak_sponge/keccak_sponge_stark.rs b/evm/src/keccak_sponge/keccak_sponge_stark.rs index 4c91d6fd..b40a2488 100644 --- a/evm/src/keccak_sponge/keccak_sponge_stark.rs +++ b/evm/src/keccak_sponge/keccak_sponge_stark.rs @@ -25,7 +25,17 @@ use crate::witness::memory::MemoryAddress; pub(crate) fn ctl_looked_data() -> Vec> { let cols = KECCAK_SPONGE_COL_MAP; - let outputs = Column::singles(&cols.updated_state_u32s[..8]); + let mut outputs = vec![]; + for i in (0..8).rev() { + let cur_col = Column::linear_combination( + cols.updated_state_bytes[i * 4..(i + 1) * 4] + .iter() + .enumerate() + .map(|(j, &c)| (c, F::from_canonical_u64(1 << (24 - 8 * j)))), + ); + outputs.push(cur_col); + } + Column::singles([ cols.context, cols.segment, @@ -137,7 +147,11 @@ pub(crate) fn ctl_looking_memory_filter(i: usize) -> Column { // - this is a full input block, or // - this is a final block of length `i` or greater let cols = KECCAK_SPONGE_COL_MAP; - Column::sum(once(&cols.is_full_input_block).chain(&cols.is_final_input_len[i..])) + if i == KECCAK_RATE_BYTES - 1 { + Column::single(cols.is_full_input_block) + } else { + Column::sum(once(&cols.is_full_input_block).chain(&cols.is_final_input_len[i + 1..])) + } } /// CTL filter for looking at XORs in the logic table. @@ -342,6 +356,25 @@ impl, const D: usize> KeccakSpongeStark { keccakf_u32s(&mut sponge_state); row.updated_state_u32s = sponge_state.map(F::from_canonical_u32); + let is_final_block = row.is_final_input_len.iter().copied().sum::() == F::ONE; + if is_final_block { + for (l, &elt) in row.updated_state_u32s[..8].iter().enumerate() { + let mut cur_bytes = vec![F::ZERO; 4]; + let mut cur_elt = elt; + for i in 0..4 { + cur_bytes[i] = + F::from_canonical_u32((cur_elt.to_canonical_u64() & 0xFF) as u32); + cur_elt = F::from_canonical_u64(cur_elt.to_canonical_u64() >> 8); + row.updated_state_bytes[l * 4 + i] = cur_bytes[i]; + } + + let mut s = row.updated_state_bytes[l * 4].to_canonical_u64(); + for i in 1..4 { + s += row.updated_state_bytes[l * 4 + i].to_canonical_u64() << (8 * i); + } + assert_eq!(elt, F::from_canonical_u64(s), "not equal"); + } + } } fn generate_padding_row(&self) -> [F; NUM_KECCAK_SPONGE_COLUMNS] { @@ -448,6 +481,16 @@ impl, const D: usize> Stark for KeccakSpongeS let entry_match = offset - P::from(FE::from_canonical_usize(i)); yield_constr.constraint(is_final_len * entry_match); } + + // Adding constraints for byte columns. + for (l, &elt) in local_values.updated_state_u32s[..8].iter().enumerate() { + let mut s = local_values.updated_state_bytes[l * 4]; + for i in 1..4 { + s += local_values.updated_state_bytes[l * 4 + i] + * P::from(FE::from_canonical_usize(1 << (8 * i))); + } + yield_constr.constraint(is_final_block * (s - elt)); + } } fn eval_ext_circuit( @@ -572,6 +615,21 @@ impl, const D: usize> Stark for KeccakSpongeS let constraint = builder.mul_extension(is_final_len, entry_match); yield_constr.constraint(builder, constraint); } + + // Adding constraints for byte columns. + for (l, &elt) in local_values.updated_state_u32s[..8].iter().enumerate() { + let mut s = local_values.updated_state_bytes[l * 4]; + for i in 1..4 { + s = builder.mul_const_add_extension( + F::from_canonical_usize(1 << (8 * i)), + local_values.updated_state_bytes[l * 4 + i], + s, + ); + } + let constraint = builder.sub_extension(s, elt); + let constraint = builder.mul_extension(is_final_block, constraint); + yield_constr.constraint(builder, constraint); + } } fn constraint_degree(&self) -> usize { diff --git a/evm/src/permutation.rs b/evm/src/permutation.rs index 35d6ee61..6ce9c943 100644 --- a/evm/src/permutation.rs +++ b/evm/src/permutation.rs @@ -14,7 +14,9 @@ use plonky2::iop::ext_target::ExtensionTarget; use plonky2::iop::target::Target; use plonky2::plonk::circuit_builder::CircuitBuilder; use plonky2::plonk::config::{AlgebraicHasher, Hasher}; -use plonky2::plonk::plonk_common::{reduce_with_powers, reduce_with_powers_ext_circuit}; +use plonky2::plonk::plonk_common::{ + reduce_with_powers, reduce_with_powers_circuit, reduce_with_powers_ext_circuit, +}; use plonky2::util::reducing::{ReducingFactor, ReducingFactorTarget}; use plonky2::util::serialization::{Buffer, IoResult, Read, Write}; use plonky2_maybe_rayon::*; @@ -83,6 +85,17 @@ impl GrandProductChallenge { } } +impl GrandProductChallenge { + pub(crate) fn combine_base_circuit, const D: usize>( + &self, + builder: &mut CircuitBuilder, + terms: &[Target], + ) -> Target { + let reduced = reduce_with_powers_circuit(builder, terms, self.beta); + builder.add(reduced, self.gamma) + } +} + /// Like `PermutationChallenge`, but with `num_challenges` copies to boost soundness. #[derive(Clone, Eq, PartialEq, Debug)] pub(crate) struct GrandProductChallengeSet { diff --git a/evm/src/proof.rs b/evm/src/proof.rs index 33133efc..3767714f 100644 --- a/evm/src/proof.rs +++ b/evm/src/proof.rs @@ -119,7 +119,8 @@ where C: GenericConfig, { pub(crate) init_challenger_state: >::Permutation, - pub(crate) proof: StarkProof, + // TODO: set it back to pub(crate) when cpu trace len is a public input + pub proof: StarkProof, } impl, C: GenericConfig, const D: usize> StarkProof { diff --git a/evm/src/recursive_verifier.rs b/evm/src/recursive_verifier.rs index 477d36c2..00c5e8c2 100644 --- a/evm/src/recursive_verifier.rs +++ b/evm/src/recursive_verifier.rs @@ -129,10 +129,21 @@ impl, C: GenericConfig, const D: usize> ensure!(pis[i].challenger_state_before == pis[i - 1].challenger_state_after); } + // Dummy values which will make the check fail. + // TODO: Fix this if the code isn't deprecated. + let mut extra_looking_products = Vec::new(); + for i in 0..NUM_TABLES { + extra_looking_products.push(Vec::new()); + for _ in 0..inner_config.num_challenges { + extra_looking_products[i].push(F::ONE); + } + } + // Verify the CTL checks. verify_cross_table_lookups::( &cross_table_lookups, pis.map(|p| p.ctl_zs_last), + extra_looking_products, inner_config, )?; diff --git a/evm/src/verifier.rs b/evm/src/verifier.rs index 5e68350b..59e60172 100644 --- a/evm/src/verifier.rs +++ b/evm/src/verifier.rs @@ -1,6 +1,7 @@ use std::any::type_name; use anyhow::{ensure, Result}; +use ethereum_types::{BigEndianHash, U256}; use plonky2::field::extension::{Extendable, FieldExtension}; use plonky2::field::types::Field; use plonky2::fri::verifier::verify_fri_proof; @@ -13,14 +14,17 @@ use crate::arithmetic::arithmetic_stark::ArithmeticStark; use crate::config::StarkConfig; use crate::constraint_consumer::ConstraintConsumer; use crate::cpu::cpu_stark::CpuStark; +use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; use crate::cross_table_lookup::{verify_cross_table_lookups, CtlCheckVars}; use crate::keccak::keccak_stark::KeccakStark; use crate::keccak_sponge::keccak_sponge_stark::KeccakSpongeStark; use crate::logic::LogicStark; use crate::memory::memory_stark::MemoryStark; -use crate::permutation::PermutationCheckVars; +use crate::memory::segments::Segment; +use crate::memory::{NUM_CHANNELS, VALUE_LIMBS}; +use crate::permutation::{GrandProductChallenge, PermutationCheckVars}; use crate::proof::{ - AllProof, AllProofChallenges, StarkOpeningSet, StarkProof, StarkProofChallenges, + AllProof, AllProofChallenges, PublicValues, StarkOpeningSet, StarkProof, StarkProofChallenges, }; use crate::stark::Stark; use crate::vanishing_poly::eval_vanishing_poly; @@ -106,13 +110,159 @@ where config, )?; + let public_values = all_proof.public_values; + + // Extra products to add to the looked last value. + let mut extra_looking_products = Vec::new(); + + // KeccakSponge + extra_looking_products.push(Vec::new()); + for _ in 0..config.num_challenges { + extra_looking_products[0].push(F::ONE); + } + + // Keccak + extra_looking_products.push(Vec::new()); + for _ in 0..config.num_challenges { + extra_looking_products[1].push(F::ONE); + } + + // Logic + extra_looking_products.push(Vec::new()); + for _ in 0..config.num_challenges { + extra_looking_products[2].push(F::ONE); + } + + // Memory + extra_looking_products.push(Vec::new()); + let cpu_trace_len = 1 << all_proof.stark_proofs[0].proof.recover_degree_bits(config); + for c in 0..config.num_challenges { + extra_looking_products[3].push(get_memory_extra_looking_products( + &public_values, + cpu_trace_len, + ctl_challenges.challenges[c], + )); + } + verify_cross_table_lookups::( cross_table_lookups, all_proof.stark_proofs.map(|p| p.proof.openings.ctl_zs_last), + extra_looking_products, config, ) } +/// Computes the extra product to multiply to the looked value. It contains memory operations not in the CPU trace: +/// - block metadata writes before kernel bootstrapping, +/// - public values reads at the end of the execution. +pub(crate) fn get_memory_extra_looking_products( + public_values: &PublicValues, + cpu_trace_len: usize, + challenge: GrandProductChallenge, +) -> F +where + F: RichField + Extendable, +{ + let mut prod = F::ONE; + + // Add metadata writes. + let block_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::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, + ), + ]; + let is_read = F::ZERO; + let context = F::ZERO; + let segment = F::from_canonical_u32(Segment::GlobalMetadata as u32); + let timestamp = F::ONE; + + block_fields.map(|(field, val)| { + let mut row = vec![F::ZERO; 13]; + row[0] = is_read; + row[1] = context; + row[2] = segment; + row[3] = F::from_canonical_usize(field as usize); + + for j in 0..VALUE_LIMBS { + row[j + 4] = F::from_canonical_u32((val >> (j * 32)).low_u32()); + } + row[12] = timestamp; + prod *= challenge.combine(row.iter()); + }); + + // Add public values reads. + let trie_fields = [ + ( + GlobalMetadata::StateTrieRootDigestBefore, + public_values.trie_roots_before.state_root, + ), + ( + GlobalMetadata::TransactionTrieRootDigestBefore, + public_values.trie_roots_before.transactions_root, + ), + ( + GlobalMetadata::ReceiptTrieRootDigestBefore, + public_values.trie_roots_before.receipts_root, + ), + ( + GlobalMetadata::StateTrieRootDigestAfter, + public_values.trie_roots_after.state_root, + ), + ( + GlobalMetadata::TransactionTrieRootDigestAfter, + public_values.trie_roots_after.transactions_root, + ), + ( + GlobalMetadata::ReceiptTrieRootDigestAfter, + public_values.trie_roots_after.receipts_root, + ), + ]; + let is_read = F::ONE; + let timestamp = F::from_canonical_usize(cpu_trace_len * NUM_CHANNELS + 1); + + trie_fields.map(|(field, hash)| { + let mut row = vec![F::ZERO; 13]; + row[0] = is_read; + row[1] = context; + row[2] = segment; + row[3] = F::from_canonical_usize(field as usize); + + let val = hash.into_uint(); + + for j in 0..VALUE_LIMBS { + row[j + 4] = F::from_canonical_u32((val >> (j * 32)).low_u32()); + } + row[12] = timestamp; + prod *= challenge.combine(row.iter()); + }); + prod +} + pub(crate) fn verify_stark_proof_with_challenges< F: RichField + Extendable, C: GenericConfig, diff --git a/evm/src/witness/operation.rs b/evm/src/witness/operation.rs index 2136f582..1f59beb7 100644 --- a/evm/src/witness/operation.rs +++ b/evm/src/witness/operation.rs @@ -141,12 +141,7 @@ pub(crate) fn generate_keccak_general( log::debug!("Hashing {:?}", input); let hash = keccak(&input); - let val_u64s: [u64; 4] = - core::array::from_fn(|i| u64::from_le_bytes(core::array::from_fn(|j| hash.0[i * 8 + j]))); - let hash_int = U256(val_u64s); - - let mut log_push = stack_push_log_and_fill(state, &mut row, hash_int)?; - log_push.value = hash.into_uint(); + let log_push = stack_push_log_and_fill(state, &mut row, hash.into_uint())?; keccak_sponge_log(state, base_address, input); diff --git a/evm/tests/empty_txn_list.rs b/evm/tests/empty_txn_list.rs index b745deb1..4ead2e4a 100644 --- a/evm/tests/empty_txn_list.rs +++ b/evm/tests/empty_txn_list.rs @@ -93,11 +93,14 @@ fn test_empty_txn_list() -> anyhow::Result<()> { receipts_trie_root ); - verify_proof(&all_stark, proof, &config)?; + verify_proof(&all_stark, proof.clone(), &config)?; + let cpu_trace_len = 1 << proof.stark_proofs[0].proof.recover_degree_bits(&config); let all_circuits = AllRecursiveCircuits::::new( &all_stark, &[16..17, 14..15, 14..15, 9..10, 12..13, 18..19], // Minimal ranges to prove an empty list + &proof.public_values, + cpu_trace_len, &config, ); From f97deab8a7614ec628b59cdebb725f5e228d9555 Mon Sep 17 00:00:00 2001 From: Hamy Ratoanina Date: Mon, 8 May 2023 18:58:42 -0400 Subject: [PATCH 2/6] Remove non-passing debug assert --- evm/src/cross_table_lookup.rs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/evm/src/cross_table_lookup.rs b/evm/src/cross_table_lookup.rs index f8e8e53c..f1876049 100644 --- a/evm/src/cross_table_lookup.rs +++ b/evm/src/cross_table_lookup.rs @@ -244,15 +244,6 @@ pub(crate) fn cross_table_lookup_data( &looked_table.filter_column, challenge, ); - - debug_assert_eq!( - zs_looking - .clone() - .map(|z| *z.values.last().unwrap()) - .product::(), - *z_looked.values.last().unwrap() - ); - for (table, z) in looking_tables.iter().zip(zs_looking) { ctl_data_per_table[table.table as usize] .zs_columns From 1590c1d0beb910fb296b55bed5a0188771f08cca Mon Sep 17 00:00:00 2001 From: Hamy Ratoanina Date: Wed, 10 May 2023 15:37:05 -0400 Subject: [PATCH 3/6] Fix indices in CTL functions --- evm/src/cross_table_lookup.rs | 28 ++++++++++----------------- evm/src/fixed_recursive_verifier.rs | 30 ++++++++++++++--------------- evm/src/verifier.rs | 24 +++++++++++++---------- evm/tests/empty_txn_list.rs | 2 +- 4 files changed, 40 insertions(+), 44 deletions(-) diff --git a/evm/src/cross_table_lookup.rs b/evm/src/cross_table_lookup.rs index f1876049..86e370d3 100644 --- a/evm/src/cross_table_lookup.rs +++ b/evm/src/cross_table_lookup.rs @@ -537,16 +537,12 @@ pub(crate) fn verify_cross_table_lookups, const D: config: &StarkConfig, ) -> Result<()> { let mut ctl_zs_openings = ctl_zs_lasts.iter().map(|v| v.iter()).collect::>(); - for ( - CrossTableLookup { - looking_tables, - looked_table, - }, - extra_product_vec, - ) in cross_table_lookups - .iter() - .zip(ctl_extra_looking_products.iter()) + for CrossTableLookup { + looking_tables, + looked_table, + } in cross_table_lookups.iter() { + let extra_product_vec = &ctl_extra_looking_products[looked_table.table as usize]; for c in 0..config.num_challenges { let mut looking_zs_prod = looking_tables .iter() @@ -575,16 +571,12 @@ pub(crate) fn verify_cross_table_lookups_circuit, c inner_config: &StarkConfig, ) { let mut ctl_zs_openings = ctl_zs_lasts.iter().map(|v| v.iter()).collect::>(); - for ( - CrossTableLookup { - looking_tables, - looked_table, - }, - extra_product_vec, - ) in cross_table_lookups - .into_iter() - .zip(ctl_extra_looking_products.iter()) + for CrossTableLookup { + looking_tables, + looked_table, + } in cross_table_lookups.into_iter() { + let extra_product_vec = &ctl_extra_looking_products[looked_table.table as usize]; for c in 0..inner_config.num_challenges { let mut looking_zs_prod = builder.mul_many( looking_tables diff --git a/evm/src/fixed_recursive_verifier.rs b/evm/src/fixed_recursive_verifier.rs index df56f4d0..1e5ab459 100644 --- a/evm/src/fixed_recursive_verifier.rs +++ b/evm/src/fixed_recursive_verifier.rs @@ -467,40 +467,40 @@ where // Extra products to add to the looked last value let mut extra_looking_products = Vec::new(); + for _ in 0..NUM_TABLES { + extra_looking_products.push(Vec::new()); + } // Arithmetic - extra_looking_products.push(Vec::new()); for _ in 0..stark_config.num_challenges { - extra_looking_products[0].push(builder.constant(F::ONE)); + extra_looking_products[Table::Arithmetic as usize].push(builder.constant(F::ONE)); } // KeccakSponge - extra_looking_products.push(Vec::new()); for _ in 0..stark_config.num_challenges { - extra_looking_products[1].push(builder.constant(F::ONE)); + extra_looking_products[Table::KeccakSponge as usize].push(builder.constant(F::ONE)); } // Keccak - extra_looking_products.push(Vec::new()); for _ in 0..stark_config.num_challenges { - extra_looking_products[2].push(builder.constant(F::ONE)); + extra_looking_products[Table::Keccak as usize].push(builder.constant(F::ONE)); } // Logic - extra_looking_products.push(Vec::new()); for _ in 0..stark_config.num_challenges { - extra_looking_products[3].push(builder.constant(F::ONE)); + extra_looking_products[Table::Logic as usize].push(builder.constant(F::ONE)); } // Memory - extra_looking_products.push(Vec::new()); for c in 0..stark_config.num_challenges { - extra_looking_products[4].push(Self::get_memory_extra_looking_products_circuit( - &mut builder, - public_values, - cpu_trace_len, - ctl_challenges.challenges[c], - )); + extra_looking_products[Table::Memory as usize].push( + Self::get_memory_extra_looking_products_circuit( + &mut builder, + public_values, + cpu_trace_len, + ctl_challenges.challenges[c], + ), + ); } // Verify the CTL checks. diff --git a/evm/src/verifier.rs b/evm/src/verifier.rs index 59e60172..ea504760 100644 --- a/evm/src/verifier.rs +++ b/evm/src/verifier.rs @@ -9,7 +9,7 @@ use plonky2::hash::hash_types::RichField; use plonky2::plonk::config::GenericConfig; use plonky2::plonk::plonk_common::reduce_with_powers; -use crate::all_stark::{AllStark, Table}; +use crate::all_stark::{AllStark, Table, NUM_TABLES}; use crate::arithmetic::arithmetic_stark::ArithmeticStark; use crate::config::StarkConfig; use crate::constraint_consumer::ConstraintConsumer; @@ -114,30 +114,34 @@ where // Extra products to add to the looked last value. let mut extra_looking_products = Vec::new(); + for _ in 0..NUM_TABLES { + extra_looking_products.push(Vec::new()); + } + + // Arithmetic + for _ in 0..config.num_challenges { + extra_looking_products[Table::Arithmetic as usize].push(F::ONE); + } // KeccakSponge - extra_looking_products.push(Vec::new()); for _ in 0..config.num_challenges { - extra_looking_products[0].push(F::ONE); + extra_looking_products[Table::KeccakSponge as usize].push(F::ONE); } // Keccak - extra_looking_products.push(Vec::new()); for _ in 0..config.num_challenges { - extra_looking_products[1].push(F::ONE); + extra_looking_products[Table::Keccak as usize].push(F::ONE); } // Logic - extra_looking_products.push(Vec::new()); for _ in 0..config.num_challenges { - extra_looking_products[2].push(F::ONE); + extra_looking_products[Table::Logic as usize].push(F::ONE); } // Memory - extra_looking_products.push(Vec::new()); - let cpu_trace_len = 1 << all_proof.stark_proofs[0].proof.recover_degree_bits(config); + let cpu_trace_len = 1 << all_proof.stark_proofs[1].proof.recover_degree_bits(config); for c in 0..config.num_challenges { - extra_looking_products[3].push(get_memory_extra_looking_products( + extra_looking_products[Table::Memory as usize].push(get_memory_extra_looking_products( &public_values, cpu_trace_len, ctl_challenges.challenges[c], diff --git a/evm/tests/empty_txn_list.rs b/evm/tests/empty_txn_list.rs index 4ead2e4a..59913787 100644 --- a/evm/tests/empty_txn_list.rs +++ b/evm/tests/empty_txn_list.rs @@ -95,7 +95,7 @@ fn test_empty_txn_list() -> anyhow::Result<()> { verify_proof(&all_stark, proof.clone(), &config)?; - let cpu_trace_len = 1 << proof.stark_proofs[0].proof.recover_degree_bits(&config); + let cpu_trace_len = 1 << proof.stark_proofs[1].proof.recover_degree_bits(&config); let all_circuits = AllRecursiveCircuits::::new( &all_stark, &[16..17, 14..15, 14..15, 9..10, 12..13, 18..19], // Minimal ranges to prove an empty list From 59b73c84ee633780e56fd938b38e56d8c9b8cbbd Mon Sep 17 00:00:00 2001 From: Hamy Ratoanina Date: Wed, 10 May 2023 17:35:28 -0400 Subject: [PATCH 4/6] Apply comments --- evm/src/cross_table_lookup.rs | 7 +++---- evm/src/fixed_recursive_verifier.rs | 30 +++++------------------------ evm/src/generation/mod.rs | 1 + evm/src/verifier.rs | 29 ++++------------------------ 4 files changed, 13 insertions(+), 54 deletions(-) diff --git a/evm/src/cross_table_lookup.rs b/evm/src/cross_table_lookup.rs index 86e370d3..61480d05 100644 --- a/evm/src/cross_table_lookup.rs +++ b/evm/src/cross_table_lookup.rs @@ -544,12 +544,11 @@ pub(crate) fn verify_cross_table_lookups, const D: { let extra_product_vec = &ctl_extra_looking_products[looked_table.table as usize]; for c in 0..config.num_challenges { - let mut looking_zs_prod = looking_tables + let looking_zs_prod = looking_tables .iter() .map(|table| *ctl_zs_openings[table.table as usize].next().unwrap()) - .product::(); - - looking_zs_prod *= extra_product_vec[c]; + .product::() + * extra_product_vec[c]; let looked_z = *ctl_zs_openings[looked_table.table as usize].next().unwrap(); ensure!( diff --git a/evm/src/fixed_recursive_verifier.rs b/evm/src/fixed_recursive_verifier.rs index 1e5ab459..1f8f0615 100644 --- a/evm/src/fixed_recursive_verifier.rs +++ b/evm/src/fixed_recursive_verifier.rs @@ -466,32 +466,12 @@ where } // Extra products to add to the looked last value - let mut extra_looking_products = Vec::new(); - for _ in 0..NUM_TABLES { - extra_looking_products.push(Vec::new()); - } - - // Arithmetic - for _ in 0..stark_config.num_challenges { - extra_looking_products[Table::Arithmetic as usize].push(builder.constant(F::ONE)); - } - - // KeccakSponge - for _ in 0..stark_config.num_challenges { - extra_looking_products[Table::KeccakSponge as usize].push(builder.constant(F::ONE)); - } - - // Keccak - for _ in 0..stark_config.num_challenges { - extra_looking_products[Table::Keccak as usize].push(builder.constant(F::ONE)); - } - - // Logic - for _ in 0..stark_config.num_challenges { - extra_looking_products[Table::Logic as usize].push(builder.constant(F::ONE)); - } + // Arithmetic, KeccakSponge, Keccak, Logic + let mut extra_looking_products = + vec![vec![builder.constant(F::ONE); stark_config.num_challenges]; NUM_TABLES - 1]; // Memory + extra_looking_products.push(Vec::new()); for c in 0..stark_config.num_challenges { extra_looking_products[Table::Memory as usize].push( Self::get_memory_extra_looking_products_circuit( @@ -746,7 +726,7 @@ where builder.connect(row[3], field_target); // values for j in 0..VALUE_LIMBS { - builder.connect(row[j + 4], targets[j]); + builder.connect(row[4 + j], targets[j]); } // timestamp builder.connect(row[12], timestamp_target); diff --git a/evm/src/generation/mod.rs b/evm/src/generation/mod.rs index 3e31c145..867d6749 100644 --- a/evm/src/generation/mod.rs +++ b/evm/src/generation/mod.rs @@ -164,6 +164,7 @@ pub(crate) fn generate_traces, const D: usize>( GenerationOutputs, )> { let mut state = GenerationState::::new(inputs.clone(), &KERNEL.code); + apply_metadata_memops(&mut state, &inputs.block_metadata); generate_bootstrap_kernel::(&mut state); diff --git a/evm/src/verifier.rs b/evm/src/verifier.rs index ea504760..1f026dc5 100644 --- a/evm/src/verifier.rs +++ b/evm/src/verifier.rs @@ -112,33 +112,12 @@ where let public_values = all_proof.public_values; - // Extra products to add to the looked last value. - let mut extra_looking_products = Vec::new(); - for _ in 0..NUM_TABLES { - extra_looking_products.push(Vec::new()); - } - - // Arithmetic - for _ in 0..config.num_challenges { - extra_looking_products[Table::Arithmetic as usize].push(F::ONE); - } - - // KeccakSponge - for _ in 0..config.num_challenges { - extra_looking_products[Table::KeccakSponge as usize].push(F::ONE); - } - - // Keccak - for _ in 0..config.num_challenges { - extra_looking_products[Table::Keccak as usize].push(F::ONE); - } - - // Logic - for _ in 0..config.num_challenges { - extra_looking_products[Table::Logic as usize].push(F::ONE); - } + // Extra products to add to the looked last value + // Arithmetic, KeccakSponge, Keccak, Logic + let mut extra_looking_products = vec![vec![F::ONE; config.num_challenges]; NUM_TABLES - 1]; // Memory + extra_looking_products.push(Vec::new()); let cpu_trace_len = 1 << all_proof.stark_proofs[1].proof.recover_degree_bits(config); for c in 0..config.num_challenges { extra_looking_products[Table::Memory as usize].push(get_memory_extra_looking_products( From 6253a68ea56eecb9428636557494eb036feb15b1 Mon Sep 17 00:00:00 2001 From: Linda Guiga Date: Wed, 21 Jun 2023 20:05:39 +0200 Subject: [PATCH 5/6] Change public values into public inputs --- evm/src/fixed_recursive_verifier.rs | 239 +++++++++++++++------------ evm/src/generation/mod.rs | 4 +- evm/src/proof.rs | 86 +++++++++- evm/src/recursive_verifier.rs | 80 ++++++--- evm/tests/empty_txn_list.rs | 59 ++----- plonky2/src/plonk/circuit_builder.rs | 6 + 6 files changed, 298 insertions(+), 176 deletions(-) diff --git a/evm/src/fixed_recursive_verifier.rs b/evm/src/fixed_recursive_verifier.rs index 1f8f0615..65e6ac01 100644 --- a/evm/src/fixed_recursive_verifier.rs +++ b/evm/src/fixed_recursive_verifier.rs @@ -2,7 +2,6 @@ use core::mem::{self, MaybeUninit}; use std::collections::BTreeMap; use std::ops::Range; -use ethereum_types::BigEndianHash; use hashbrown::HashMap; use itertools::{zip_eq, Itertools}; use plonky2::field::extension::Extendable; @@ -33,7 +32,7 @@ use crate::config::StarkConfig; use crate::cpu::cpu_stark::CpuStark; use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; use crate::cross_table_lookup::{verify_cross_table_lookups_circuit, CrossTableLookup}; -use crate::generation::GenerationInputs; +use crate::generation::{self, GenerationInputs}; use crate::keccak::keccak_stark::KeccakStark; use crate::keccak_sponge::keccak_sponge_stark::KeccakSpongeStark; use crate::logic::LogicStark; @@ -43,11 +42,15 @@ use crate::memory::{NUM_CHANNELS, VALUE_LIMBS}; use crate::permutation::{ get_grand_product_challenge_set_target, GrandProductChallenge, GrandProductChallengeSet, }; -use crate::proof::{BlockMetadataTarget, PublicValues, StarkProofWithMetadata}; +use crate::proof::{ + BlockMetadata, BlockMetadataTarget, PublicValues, PublicValuesTarget, StarkProofWithMetadata, + TrieRootsTarget, +}; use crate::prover::prove; use crate::recursive_verifier::{ - add_common_recursion_gates, add_virtual_trie_roots, recursive_stark_circuit, - PlonkWrapperCircuit, PublicInputs, StarkWrapperCircuit, + add_common_recursion_gates, add_virtual_public_values, add_virtual_trie_roots, + recursive_stark_circuit, set_block_metadata_target, set_trie_roots_target, PlonkWrapperCircuit, + PublicInputs, StarkWrapperCircuit, }; use crate::stark::Stark; use crate::util::h160_limbs; @@ -88,6 +91,8 @@ where /// For each table, various inner circuits may be used depending on the initial table size. /// This target holds the index of the circuit (within `final_circuits()`) that was used. index_verifier_data: [Target; NUM_TABLES], + /// Public inputs containing public values. + public_values: PublicValuesTarget, /// Public inputs used for cyclic verification. These aren't actually used for EVM root /// proofs; the circuit has them just to match the structure of aggregation proofs. cyclic_vk: VerifierCircuitTarget, @@ -111,6 +116,7 @@ where for index in self.index_verifier_data { buffer.write_target(index)?; } + self.public_values.to_buffer(buffer)?; buffer.write_target_verifier_circuit(&self.cyclic_vk)?; Ok(()) } @@ -129,12 +135,14 @@ where for _ in 0..NUM_TABLES { index_verifier_data.push(buffer.read_target()?); } + let public_values = PublicValuesTarget::from_buffer(buffer)?; let cyclic_vk = buffer.read_target_verifier_circuit()?; Ok(Self { circuit, proof_with_pis: proof_with_pis.try_into().unwrap(), index_verifier_data: index_verifier_data.try_into().unwrap(), + public_values, cyclic_vk, }) } @@ -151,6 +159,7 @@ where pub circuit: CircuitData, lhs: AggregationChildTarget, rhs: AggregationChildTarget, + public_values: PublicValuesTarget, cyclic_vk: VerifierCircuitTarget, } @@ -167,6 +176,7 @@ where ) -> IoResult<()> { buffer.write_circuit_data(&self.circuit, gate_serializer, generator_serializer)?; buffer.write_target_verifier_circuit(&self.cyclic_vk)?; + self.public_values.to_buffer(buffer)?; self.lhs.to_buffer(buffer)?; self.rhs.to_buffer(buffer)?; Ok(()) @@ -179,12 +189,14 @@ where ) -> IoResult { let circuit = buffer.read_circuit_data(gate_serializer, generator_serializer)?; let cyclic_vk = buffer.read_target_verifier_circuit()?; + let public_values = PublicValuesTarget::from_buffer(buffer)?; let lhs = AggregationChildTarget::from_buffer(buffer)?; let rhs = AggregationChildTarget::from_buffer(buffer)?; Ok(Self { circuit, lhs, rhs, + public_values, cyclic_vk, }) } @@ -227,6 +239,7 @@ where has_parent_block: BoolTarget, parent_block_proof: ProofWithPublicInputsTarget, agg_root_proof: ProofWithPublicInputsTarget, + public_values: PublicValuesTarget, cyclic_vk: VerifierCircuitTarget, } @@ -245,6 +258,7 @@ where buffer.write_target_bool(self.has_parent_block)?; buffer.write_target_proof_with_public_inputs(&self.parent_block_proof)?; buffer.write_target_proof_with_public_inputs(&self.agg_root_proof)?; + self.public_values.to_buffer(buffer)?; buffer.write_target_verifier_circuit(&self.cyclic_vk)?; Ok(()) } @@ -258,12 +272,14 @@ where let has_parent_block = buffer.read_target_bool()?; let parent_block_proof = buffer.read_target_proof_with_public_inputs()?; let agg_root_proof = buffer.read_target_proof_with_public_inputs()?; + let public_values = PublicValuesTarget::from_buffer(buffer)?; let cyclic_vk = buffer.read_target_verifier_circuit()?; Ok(Self { circuit, has_parent_block, parent_block_proof, agg_root_proof, + public_values, cyclic_vk, }) } @@ -346,8 +362,6 @@ where pub fn new( all_stark: &AllStark, degree_bits_ranges: &[Range; NUM_TABLES], - public_values: &PublicValues, - cpu_trace_len: usize, stark_config: &StarkConfig, ) -> Self { let arithmetic = RecursiveCircuitsForTable::new( @@ -394,7 +408,7 @@ where ); let by_table = [arithmetic, cpu, keccak, keccak_sponge, logic, memory]; - let root = Self::create_root_circuit(public_values, cpu_trace_len, &by_table, stark_config); + let root = Self::create_root_circuit(&by_table, stark_config); let aggregation = Self::create_aggregation_circuit(&root); let block = Self::create_block_circuit(&aggregation); Self { @@ -406,8 +420,6 @@ where } fn create_root_circuit( - public_values: &PublicValues, - cpu_trace_len: usize, by_table: &[RecursiveCircuitsForTable; NUM_TABLES], stark_config: &StarkConfig, ) -> RootCircuitData { @@ -416,6 +428,8 @@ where let mut builder = CircuitBuilder::new(CircuitConfig::standard_recursion_config()); + let public_values = add_virtual_public_values(&mut builder); + let recursive_proofs = core::array::from_fn(|i| builder.add_virtual_proof_with_pis(inner_common_data[i])); let pis: [_; NUM_TABLES] = core::array::from_fn(|i| { @@ -476,8 +490,7 @@ where extra_looking_products[Table::Memory as usize].push( Self::get_memory_extra_looking_products_circuit( &mut builder, - public_values, - cpu_trace_len, + &public_values, ctl_challenges.challenges[c], ), ); @@ -532,6 +545,7 @@ where circuit: builder.build::(), proof_with_pis: recursive_proofs, index_verifier_data, + public_values, cyclic_vk, } } @@ -539,57 +553,37 @@ where /// Recursive version of `get_memory_extra_looking_products`. pub(crate) fn get_memory_extra_looking_products_circuit( builder: &mut CircuitBuilder, - public_values: &PublicValues, - cpu_trace_len: usize, + public_values: &PublicValuesTarget, challenge: GrandProductChallenge, ) -> Target { let mut prod = builder.constant(F::ONE); // Add metadata writes. - let metadata_target = BlockMetadataTarget { - block_beneficiary: builder.add_virtual_target_arr::<5>(), - block_timestamp: builder.constant(F::from_canonical_u64( - public_values.block_metadata.block_timestamp.as_u64(), - )), - block_difficulty: builder.constant(F::from_canonical_u64( - public_values.block_metadata.block_difficulty.as_u64(), - )), - block_number: builder.constant(F::from_canonical_u64( - public_values.block_metadata.block_number.as_u64(), - )), - block_gaslimit: builder.constant(F::from_canonical_u64( - public_values.block_metadata.block_gaslimit.as_u64(), - )), - block_chain_id: builder.constant(F::from_canonical_u64( - public_values.block_metadata.block_chain_id.as_u64(), - )), - block_base_fee: builder.constant(F::from_canonical_u64( - public_values.block_metadata.block_base_fee.as_u64(), - )), - }; - - let beneficiary_limbs = h160_limbs(public_values.block_metadata.block_beneficiary); - for i in 0..5 { - let limb_target = builder.constant(beneficiary_limbs[i]); - builder.connect(metadata_target.block_beneficiary[i], limb_target); - } - let block_fields_without_beneficiary = [ ( GlobalMetadata::BlockTimestamp, - metadata_target.block_timestamp, + public_values.block_metadata.block_timestamp, + ), + ( + GlobalMetadata::BlockNumber, + public_values.block_metadata.block_number, ), - (GlobalMetadata::BlockNumber, metadata_target.block_number), ( GlobalMetadata::BlockDifficulty, - metadata_target.block_difficulty, + public_values.block_metadata.block_difficulty, ), ( GlobalMetadata::BlockGasLimit, - metadata_target.block_gaslimit, + 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::BlockChainId, metadata_target.block_chain_id), - (GlobalMetadata::BlockBaseFee, metadata_target.block_base_fee), ]; let zero = builder.constant(F::ZERO); @@ -610,7 +604,10 @@ where builder.connect(row[3], field_target); // values for j in 0..5 { - builder.connect(row[4 + j], metadata_target.block_beneficiary[j]); + builder.connect( + row[4 + j], + public_values.block_metadata.block_beneficiary[j], + ); } for j in 5..VALUE_LIMBS { builder.connect(row[4 + j], zero); @@ -644,75 +641,38 @@ where }); // Add public values reads. - let tries_before_target = add_virtual_trie_roots(builder); - for (targets, hash) in [ - tries_before_target.state_root, - tries_before_target.transactions_root, - tries_before_target.receipts_root, - ] - .iter() - .zip([ - public_values.trie_roots_before.state_root, - public_values.trie_roots_before.transactions_root, - public_values.trie_roots_before.receipts_root, - ]) { - for (i, limb) in hash.into_uint().0.into_iter().enumerate() { - let low_limb_target = builder.constant(F::from_canonical_u32(limb as u32)); - let high_limb_target = builder.constant(F::from_canonical_u32((limb >> 32) as u32)); - builder.connect(targets[2 * i], low_limb_target); - builder.connect(targets[2 * i + 1], high_limb_target); - } - } - - let tries_after_target = add_virtual_trie_roots(builder); - for (targets, hash) in [ - tries_after_target.state_root, - tries_after_target.transactions_root, - tries_after_target.receipts_root, - ] - .iter() - .zip([ - public_values.trie_roots_after.state_root, - public_values.trie_roots_after.transactions_root, - public_values.trie_roots_after.receipts_root, - ]) { - for (i, limb) in hash.into_uint().0.into_iter().enumerate() { - let low_limb_target = builder.constant(F::from_canonical_u32(limb as u32)); - let high_limb_target = builder.constant(F::from_canonical_u32((limb >> 32) as u32)); - builder.connect(targets[2 * i], low_limb_target); - builder.connect(targets[2 * i + 1], high_limb_target); - } - } - let trie_fields = [ ( GlobalMetadata::StateTrieRootDigestBefore, - tries_before_target.state_root, + public_values.trie_roots_before.state_root, ), ( GlobalMetadata::TransactionTrieRootDigestBefore, - tries_before_target.transactions_root, + public_values.trie_roots_before.transactions_root, ), ( GlobalMetadata::ReceiptTrieRootDigestBefore, - tries_before_target.receipts_root, + public_values.trie_roots_before.receipts_root, ), ( GlobalMetadata::StateTrieRootDigestAfter, - tries_after_target.state_root, + public_values.trie_roots_after.state_root, ), ( GlobalMetadata::TransactionTrieRootDigestAfter, - tries_after_target.transactions_root, + public_values.trie_roots_after.transactions_root, ), ( GlobalMetadata::ReceiptTrieRootDigestAfter, - tries_after_target.receipts_root, + public_values.trie_roots_after.receipts_root, ), ]; - let timestamp_target = - builder.constant(F::from_canonical_usize(cpu_trace_len * NUM_CHANNELS + 1)); + let mut timestamp_target = builder.mul_const( + F::from_canonical_usize(NUM_CHANNELS), + public_values.cpu_trace_len, + ); + timestamp_target = builder.add_const(timestamp_target, F::ONE); trie_fields.map(|(field, targets)| { let row = builder.add_virtual_targets(13); // is_read @@ -742,6 +702,7 @@ where root: &RootCircuitData, ) -> AggregationCircuitData { let mut builder = CircuitBuilder::::new(root.circuit.common.config.clone()); + let public_values = add_virtual_public_values(&mut builder); let cyclic_vk = builder.add_verifier_data_public_inputs(); let lhs = Self::add_agg_child(&mut builder, root); let rhs = Self::add_agg_child(&mut builder, root); @@ -756,6 +717,7 @@ where circuit, lhs, rhs, + public_values, cyclic_vk, } } @@ -793,6 +755,7 @@ where }; let mut builder = CircuitBuilder::::new(CircuitConfig::standard_recursion_config()); + let public_values = add_virtual_public_values(&mut builder); let has_parent_block = builder.add_virtual_bool_target_safe(); let parent_block_proof = builder.add_virtual_proof_with_pis(&expected_common_data); let agg_root_proof = builder.add_virtual_proof_with_pis(&agg.circuit.common); @@ -815,6 +778,7 @@ where has_parent_block, parent_block_proof, agg_root_proof, + public_values, cyclic_vk, } } @@ -826,7 +790,7 @@ where config: &StarkConfig, generation_inputs: GenerationInputs, timing: &mut TimingTree, - ) -> anyhow::Result> { + ) -> anyhow::Result<(ProofWithPublicInputs, PublicValues)> { let all_proof = prove::(all_stark, config, generation_inputs, timing)?; let mut root_inputs = PartialWitness::new(); @@ -853,7 +817,30 @@ where &self.aggregation.circuit.verifier_only, ); - self.root.circuit.prove(root_inputs) + set_block_metadata_target( + &mut root_inputs, + &self.root.public_values.block_metadata, + &all_proof.public_values.block_metadata, + ); + + root_inputs.set_target( + self.root.public_values.cpu_trace_len, + F::from_canonical_usize(all_proof.public_values.cpu_trace_len), + ); + set_trie_roots_target( + &mut root_inputs, + &self.root.public_values.trie_roots_before, + &all_proof.public_values.trie_roots_before, + ); + set_trie_roots_target( + &mut root_inputs, + &self.root.public_values.trie_roots_after, + &all_proof.public_values.trie_roots_after, + ); + + let root_proof = self.root.circuit.prove(root_inputs)?; + + Ok((root_proof, all_proof.public_values)) } pub fn verify_root(&self, agg_proof: ProofWithPublicInputs) -> anyhow::Result<()> { @@ -866,7 +853,8 @@ where lhs_proof: &ProofWithPublicInputs, rhs_is_agg: bool, rhs_proof: &ProofWithPublicInputs, - ) -> anyhow::Result> { + public_values: PublicValues, + ) -> anyhow::Result<(ProofWithPublicInputs, PublicValues)> { let mut agg_inputs = PartialWitness::new(); agg_inputs.set_bool_target(self.aggregation.lhs.is_agg, lhs_is_agg); @@ -882,7 +870,29 @@ where &self.aggregation.circuit.verifier_only, ); - self.aggregation.circuit.prove(agg_inputs) + set_block_metadata_target( + &mut agg_inputs, + &self.aggregation.public_values.block_metadata, + &public_values.block_metadata, + ); + + agg_inputs.set_target( + self.aggregation.public_values.cpu_trace_len, + F::from_canonical_usize(public_values.cpu_trace_len), + ); + set_trie_roots_target( + &mut agg_inputs, + &self.aggregation.public_values.trie_roots_before, + &public_values.trie_roots_before, + ); + set_trie_roots_target( + &mut agg_inputs, + &self.aggregation.public_values.trie_roots_after, + &public_values.trie_roots_after, + ); + + let aggregation_proof = self.aggregation.circuit.prove(agg_inputs)?; + Ok((aggregation_proof, public_values)) } pub fn verify_aggregation( @@ -901,7 +911,8 @@ where &self, opt_parent_block_proof: Option<&ProofWithPublicInputs>, agg_root_proof: &ProofWithPublicInputs, - ) -> anyhow::Result> { + public_values: PublicValues, + ) -> anyhow::Result<(ProofWithPublicInputs, PublicValues)> { let mut block_inputs = PartialWitness::new(); block_inputs.set_bool_target( @@ -927,7 +938,29 @@ where block_inputs .set_verifier_data_target(&self.block.cyclic_vk, &self.block.circuit.verifier_only); - self.block.circuit.prove(block_inputs) + set_block_metadata_target( + &mut block_inputs, + &self.block.public_values.block_metadata, + &public_values.block_metadata, + ); + + block_inputs.set_target( + self.block.public_values.cpu_trace_len, + F::from_canonical_usize(public_values.cpu_trace_len), + ); + set_trie_roots_target( + &mut block_inputs, + &self.block.public_values.trie_roots_before, + &public_values.trie_roots_before, + ); + set_trie_roots_target( + &mut block_inputs, + &self.block.public_values.trie_roots_after, + &public_values.trie_roots_after, + ); + + let block_proof = self.block.circuit.prove(block_inputs)?; + Ok((block_proof, public_values)) } pub fn verify_block(&self, block_proof: &ProofWithPublicInputs) -> anyhow::Result<()> { diff --git a/evm/src/generation/mod.rs b/evm/src/generation/mod.rs index 867d6749..d011ec76 100644 --- a/evm/src/generation/mod.rs +++ b/evm/src/generation/mod.rs @@ -38,7 +38,6 @@ use crate::witness::util::mem_write_log; #[derive(Clone, Debug, Deserialize, Serialize, Default)] pub struct GenerationInputs { pub signed_txns: Vec>, - pub tries: TrieInputs, /// Mapping between smart contract code hashes and the contract byte code. @@ -153,7 +152,7 @@ fn apply_trie_memops, const D: usize>( state.traces.memory_ops.extend(ops); } -pub(crate) fn generate_traces, const D: usize>( +pub fn generate_traces, const D: usize>( all_stark: &AllStark, inputs: GenerationInputs, config: &StarkConfig, @@ -201,6 +200,7 @@ pub(crate) fn generate_traces, const D: usize>( trie_roots_before, trie_roots_after, block_metadata: inputs.block_metadata, + cpu_trace_len: state.traces.clock(), }; let tables = timed!( diff --git a/evm/src/proof.rs b/evm/src/proof.rs index 3767714f..0356473a 100644 --- a/evm/src/proof.rs +++ b/evm/src/proof.rs @@ -52,9 +52,10 @@ pub struct PublicValues { pub trie_roots_before: TrieRoots, pub trie_roots_after: TrieRoots, pub block_metadata: BlockMetadata, + pub cpu_trace_len: usize, } -#[derive(Debug, Clone, Default)] +#[derive(Debug, Clone, Default, Serialize, Deserialize)] pub struct TrieRoots { pub state_root: H256, pub transactions_root: H256, @@ -74,18 +75,101 @@ pub struct BlockMetadata { /// Memory values which are public. /// Note: All the larger integers are encoded with 32-bit limbs in little-endian order. +#[derive(Eq, PartialEq, Debug)] pub struct PublicValuesTarget { pub trie_roots_before: TrieRootsTarget, pub trie_roots_after: TrieRootsTarget, pub block_metadata: BlockMetadataTarget, + pub cpu_trace_len: Target, } +impl PublicValuesTarget { + pub fn to_buffer(&self, buffer: &mut Vec) -> IoResult<()> { + let TrieRootsTarget { + state_root: state_root_before, + transactions_root: transactions_root_before, + receipts_root: receipts_root_before, + } = self.trie_roots_before; + + buffer.write_target_vec(&state_root_before)?; + buffer.write_target_vec(&transactions_root_before)?; + buffer.write_target_vec(&receipts_root_before)?; + + let TrieRootsTarget { + state_root: state_root_after, + transactions_root: transactions_root_after, + receipts_root: receipts_root_after, + } = self.trie_roots_after; + + buffer.write_target_vec(&state_root_after)?; + buffer.write_target_vec(&transactions_root_after)?; + buffer.write_target_vec(&receipts_root_after)?; + + let BlockMetadataTarget { + block_beneficiary, + block_timestamp, + block_number, + block_difficulty, + block_gaslimit, + block_chain_id, + block_base_fee, + } = self.block_metadata; + + buffer.write_target_vec(&block_beneficiary)?; + buffer.write_target(block_timestamp)?; + buffer.write_target(block_number)?; + buffer.write_target(block_difficulty)?; + buffer.write_target(block_gaslimit)?; + buffer.write_target(block_chain_id)?; + buffer.write_target(block_base_fee)?; + + buffer.write_target(self.cpu_trace_len)?; + + Ok(()) + } + + pub fn from_buffer(buffer: &mut Buffer) -> IoResult { + let trie_roots_before = TrieRootsTarget { + state_root: buffer.read_target_vec()?.try_into().unwrap(), + transactions_root: buffer.read_target_vec()?.try_into().unwrap(), + receipts_root: buffer.read_target_vec()?.try_into().unwrap(), + }; + + let trie_roots_after = TrieRootsTarget { + state_root: buffer.read_target_vec()?.try_into().unwrap(), + transactions_root: buffer.read_target_vec()?.try_into().unwrap(), + receipts_root: buffer.read_target_vec()?.try_into().unwrap(), + }; + + let block_metadata = BlockMetadataTarget { + block_beneficiary: buffer.read_target_vec()?.try_into().unwrap(), + block_timestamp: buffer.read_target()?, + block_number: buffer.read_target()?, + block_difficulty: buffer.read_target()?, + block_gaslimit: buffer.read_target()?, + block_chain_id: buffer.read_target()?, + block_base_fee: buffer.read_target()?, + }; + + let cpu_trace_len = buffer.read_target()?; + + Ok(Self { + trie_roots_before, + trie_roots_after, + block_metadata, + cpu_trace_len, + }) + } +} + +#[derive(Eq, PartialEq, Debug)] pub struct TrieRootsTarget { pub state_root: [Target; 8], pub transactions_root: [Target; 8], pub receipts_root: [Target; 8], } +#[derive(Eq, PartialEq, Debug)] pub struct BlockMetadataTarget { pub block_beneficiary: [Target; 5], pub block_timestamp: Target, diff --git a/evm/src/recursive_verifier.rs b/evm/src/recursive_verifier.rs index 00c5e8c2..b8b87e71 100644 --- a/evm/src/recursive_verifier.rs +++ b/evm/src/recursive_verifier.rs @@ -1,6 +1,7 @@ use std::fmt::Debug; use anyhow::{ensure, Result}; +use ethereum_types::BigEndianHash; use plonky2::field::extension::Extendable; use plonky2::field::types::Field; use plonky2::fri::witness_util::set_fri_proof_target; @@ -509,26 +510,27 @@ fn eval_l_0_and_l_last_circuit, const D: usize>( ) } -#[allow(unused)] // TODO: used later? pub(crate) fn add_virtual_public_values, const D: usize>( builder: &mut CircuitBuilder, ) -> PublicValuesTarget { let trie_roots_before = add_virtual_trie_roots(builder); let trie_roots_after = add_virtual_trie_roots(builder); let block_metadata = add_virtual_block_metadata(builder); + let cpu_trace_len = builder.add_virtual_public_input(); PublicValuesTarget { trie_roots_before, trie_roots_after, block_metadata, + cpu_trace_len, } } pub(crate) fn add_virtual_trie_roots, const D: usize>( builder: &mut CircuitBuilder, ) -> TrieRootsTarget { - let state_root = builder.add_virtual_target_arr(); - let transactions_root = builder.add_virtual_target_arr(); - let receipts_root = builder.add_virtual_target_arr(); + let state_root = builder.add_virtual_public_input_arr(); + let transactions_root = builder.add_virtual_public_input_arr(); + let receipts_root = builder.add_virtual_public_input_arr(); TrieRootsTarget { state_root, transactions_root, @@ -539,13 +541,13 @@ pub(crate) fn add_virtual_trie_roots, const D: usiz pub(crate) fn add_virtual_block_metadata, const D: usize>( builder: &mut CircuitBuilder, ) -> BlockMetadataTarget { - let block_beneficiary = builder.add_virtual_target_arr(); - let block_timestamp = builder.add_virtual_target(); - let block_number = builder.add_virtual_target(); - let block_difficulty = builder.add_virtual_target(); - let block_gaslimit = builder.add_virtual_target(); - let block_chain_id = builder.add_virtual_target(); - let block_base_fee = builder.add_virtual_target(); + let block_beneficiary = builder.add_virtual_public_input_arr(); + let block_timestamp = builder.add_virtual_public_input(); + let block_number = builder.add_virtual_public_input(); + let block_difficulty = builder.add_virtual_public_input(); + let block_gaslimit = builder.add_virtual_public_input(); + let block_chain_id = builder.add_virtual_public_input(); + let block_base_fee = builder.add_virtual_public_input(); BlockMetadataTarget { block_beneficiary, block_timestamp, @@ -668,18 +670,50 @@ pub(crate) fn set_trie_roots_target( F: RichField + Extendable, W: Witness, { - witness.set_target_arr( - &trie_roots_target.state_root, - &h256_limbs(trie_roots.state_root), - ); - witness.set_target_arr( - &trie_roots_target.transactions_root, - &h256_limbs(trie_roots.transactions_root), - ); - witness.set_target_arr( - &trie_roots_target.receipts_root, - &h256_limbs(trie_roots.receipts_root), - ); + for (i, limb) in trie_roots.state_root.into_uint().0.into_iter().enumerate() { + witness.set_target( + trie_roots_target.state_root[2 * i], + F::from_canonical_u32(limb as u32), + ); + witness.set_target( + trie_roots_target.state_root[2 * i + 1], + F::from_canonical_u32((limb >> 32) as u32), + ); + } + + for (i, limb) in trie_roots + .transactions_root + .into_uint() + .0 + .into_iter() + .enumerate() + { + witness.set_target( + trie_roots_target.transactions_root[2 * i], + F::from_canonical_u32(limb as u32), + ); + witness.set_target( + trie_roots_target.transactions_root[2 * i + 1], + F::from_canonical_u32((limb >> 32) as u32), + ); + } + + for (i, limb) in trie_roots + .receipts_root + .into_uint() + .0 + .into_iter() + .enumerate() + { + witness.set_target( + trie_roots_target.receipts_root[2 * i], + F::from_canonical_u32(limb as u32), + ); + witness.set_target( + trie_roots_target.receipts_root[2 * i + 1], + F::from_canonical_u32((limb >> 32) as u32), + ); + } } pub(crate) fn set_block_metadata_target( diff --git a/evm/tests/empty_txn_list.rs b/evm/tests/empty_txn_list.rs index 59913787..fae020c4 100644 --- a/evm/tests/empty_txn_list.rs +++ b/evm/tests/empty_txn_list.rs @@ -15,8 +15,8 @@ use plonky2::util::timing::TimingTree; use plonky2_evm::all_stark::AllStark; use plonky2_evm::config::StarkConfig; use plonky2_evm::fixed_recursive_verifier::AllRecursiveCircuits; -use plonky2_evm::generation::{GenerationInputs, TrieInputs}; -use plonky2_evm::proof::BlockMetadata; +use plonky2_evm::generation::{generate_traces, GenerationInputs, TrieInputs}; +use plonky2_evm::proof::{BlockMetadata, TrieRoots}; use plonky2_evm::prover::prove; use plonky2_evm::verifier::verify_proof; use plonky2_evm::Node; @@ -27,7 +27,7 @@ type C = PoseidonGoldilocksConfig; /// Execute the empty list of transactions, i.e. a no-op. #[test] -#[ignore] // Too slow to run on CI. +//#[ignore] // Too slow to run on CI. fn test_empty_txn_list() -> anyhow::Result<()> { init_logger(); @@ -41,10 +41,6 @@ fn test_empty_txn_list() -> anyhow::Result<()> { let receipts_trie = HashedPartialTrie::from(Node::Empty); let storage_tries = vec![]; - let state_trie_root = state_trie.hash(); - let txns_trie_root = transactions_trie.hash(); - let receipts_trie_root = receipts_trie.hash(); - let mut contract_code = HashMap::new(); contract_code.insert(keccak(vec![]), vec![]); @@ -61,46 +57,9 @@ fn test_empty_txn_list() -> anyhow::Result<()> { addresses: vec![], }; - let mut timing = TimingTree::new("prove", log::Level::Debug); - // TODO: This is redundant; prove_root below calls this prove method internally. - // Just keeping it for now because the root proof returned by prove_root doesn't contain public - // values yet, and we want those for the assertions below. - let proof = prove::(&all_stark, &config, inputs.clone(), &mut timing)?; - timing.filter(Duration::from_millis(100)).print(); - - assert_eq!( - proof.public_values.trie_roots_before.state_root, - state_trie_root - ); - assert_eq!( - proof.public_values.trie_roots_after.state_root, - state_trie_root - ); - assert_eq!( - proof.public_values.trie_roots_before.transactions_root, - txns_trie_root - ); - assert_eq!( - proof.public_values.trie_roots_after.transactions_root, - txns_trie_root - ); - assert_eq!( - proof.public_values.trie_roots_before.receipts_root, - receipts_trie_root - ); - assert_eq!( - proof.public_values.trie_roots_after.receipts_root, - receipts_trie_root - ); - - verify_proof(&all_stark, proof.clone(), &config)?; - - let cpu_trace_len = 1 << proof.stark_proofs[1].proof.recover_degree_bits(&config); let all_circuits = AllRecursiveCircuits::::new( &all_stark, &[16..17, 14..15, 14..15, 9..10, 12..13, 18..19], // Minimal ranges to prove an empty list - &proof.public_values, - cpu_trace_len, &config, ); @@ -133,12 +92,18 @@ fn test_empty_txn_list() -> anyhow::Result<()> { } let mut timing = TimingTree::new("prove", log::Level::Info); - let root_proof = all_circuits.prove_root(&all_stark, &config, inputs, &mut timing)?; + let (root_proof, public_values) = + all_circuits.prove_root(&all_stark, &config, inputs, &mut timing)?; timing.filter(Duration::from_millis(100)).print(); all_circuits.verify_root(root_proof.clone())?; - let agg_proof = all_circuits.prove_aggregation(false, &root_proof, false, &root_proof)?; - all_circuits.verify_aggregation(&agg_proof) + // We can duplicate the proofs here because the state hasn't mutated. + let (agg_proof, public_values) = + all_circuits.prove_aggregation(false, &root_proof, false, &root_proof, public_values)?; + all_circuits.verify_aggregation(&agg_proof)?; + + let (block_proof, _) = all_circuits.prove_block(None, &agg_proof, public_values)?; + all_circuits.verify_block(&block_proof) } fn init_logger() { diff --git a/plonky2/src/plonk/circuit_builder.rs b/plonky2/src/plonk/circuit_builder.rs index 3cf48de0..82d4ee89 100644 --- a/plonky2/src/plonk/circuit_builder.rs +++ b/plonky2/src/plonk/circuit_builder.rs @@ -323,6 +323,12 @@ impl, const D: usize> CircuitBuilder { t } + pub fn add_virtual_public_input_arr(&mut self) -> [Target; N] { + let ts = [0; N].map(|_| self.add_virtual_target()); + self.register_public_inputs(&ts); + ts + } + pub fn add_virtual_verifier_data(&mut self, cap_height: usize) -> VerifierCircuitTarget { VerifierCircuitTarget { constants_sigmas_cap: self.add_virtual_cap(cap_height), From 7a882d0a64c211f9ce77820877db3b23aa2f89ef Mon Sep 17 00:00:00 2001 From: Hamy Ratoanina Date: Fri, 23 Jun 2023 11:06:51 -0400 Subject: [PATCH 6/6] Clippy --- evm/src/fixed_recursive_verifier.rs | 14 +++++--------- evm/src/recursive_verifier.rs | 2 +- evm/src/util.rs | 1 + evm/tests/empty_txn_list.rs | 10 ++++------ 4 files changed, 11 insertions(+), 16 deletions(-) diff --git a/evm/src/fixed_recursive_verifier.rs b/evm/src/fixed_recursive_verifier.rs index 65e6ac01..06333b27 100644 --- a/evm/src/fixed_recursive_verifier.rs +++ b/evm/src/fixed_recursive_verifier.rs @@ -32,7 +32,7 @@ use crate::config::StarkConfig; use crate::cpu::cpu_stark::CpuStark; use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; use crate::cross_table_lookup::{verify_cross_table_lookups_circuit, CrossTableLookup}; -use crate::generation::{self, GenerationInputs}; +use crate::generation::GenerationInputs; use crate::keccak::keccak_stark::KeccakStark; use crate::keccak_sponge::keccak_sponge_stark::KeccakSpongeStark; use crate::logic::LogicStark; @@ -42,18 +42,14 @@ use crate::memory::{NUM_CHANNELS, VALUE_LIMBS}; use crate::permutation::{ get_grand_product_challenge_set_target, GrandProductChallenge, GrandProductChallengeSet, }; -use crate::proof::{ - BlockMetadata, BlockMetadataTarget, PublicValues, PublicValuesTarget, StarkProofWithMetadata, - TrieRootsTarget, -}; +use crate::proof::{PublicValues, PublicValuesTarget, StarkProofWithMetadata}; use crate::prover::prove; use crate::recursive_verifier::{ - add_common_recursion_gates, add_virtual_public_values, add_virtual_trie_roots, - recursive_stark_circuit, set_block_metadata_target, set_trie_roots_target, PlonkWrapperCircuit, - PublicInputs, StarkWrapperCircuit, + add_common_recursion_gates, add_virtual_public_values, recursive_stark_circuit, + set_block_metadata_target, set_trie_roots_target, PlonkWrapperCircuit, PublicInputs, + StarkWrapperCircuit, }; use crate::stark::Stark; -use crate::util::h160_limbs; /// The recursion threshold. We end a chain of recursive proofs once we reach this size. const THRESHOLD_DEGREE_BITS: usize = 13; diff --git a/evm/src/recursive_verifier.rs b/evm/src/recursive_verifier.rs index b8b87e71..b9b36dcd 100644 --- a/evm/src/recursive_verifier.rs +++ b/evm/src/recursive_verifier.rs @@ -39,7 +39,7 @@ use crate::proof::{ TrieRootsTarget, }; use crate::stark::Stark; -use crate::util::{h160_limbs, h256_limbs}; +use crate::util::h160_limbs; use crate::vanishing_poly::eval_vanishing_poly_circuit; use crate::vars::StarkEvaluationTargets; diff --git a/evm/src/util.rs b/evm/src/util.rs index 3ad88778..d39ea83d 100644 --- a/evm/src/util.rs +++ b/evm/src/util.rs @@ -61,6 +61,7 @@ pub(crate) fn u256_limbs(u256: U256) -> [F; 8] { .unwrap() } +#[allow(unused)] /// Returns the 32-bit little-endian limbs of a `H256`. pub(crate) fn h256_limbs(h256: H256) -> [F; 8] { h256.0 diff --git a/evm/tests/empty_txn_list.rs b/evm/tests/empty_txn_list.rs index fae020c4..50f677cb 100644 --- a/evm/tests/empty_txn_list.rs +++ b/evm/tests/empty_txn_list.rs @@ -5,7 +5,7 @@ use std::marker::PhantomData; use std::time::Duration; use env_logger::{try_init_from_env, Env, DEFAULT_FILTER_ENV}; -use eth_trie_utils::partial_trie::{HashedPartialTrie, PartialTrie}; +use eth_trie_utils::partial_trie::HashedPartialTrie; use keccak_hash::keccak; use log::info; use plonky2::field::goldilocks_field::GoldilocksField; @@ -15,10 +15,8 @@ use plonky2::util::timing::TimingTree; use plonky2_evm::all_stark::AllStark; use plonky2_evm::config::StarkConfig; use plonky2_evm::fixed_recursive_verifier::AllRecursiveCircuits; -use plonky2_evm::generation::{generate_traces, GenerationInputs, TrieInputs}; -use plonky2_evm::proof::{BlockMetadata, TrieRoots}; -use plonky2_evm::prover::prove; -use plonky2_evm::verifier::verify_proof; +use plonky2_evm::generation::{GenerationInputs, TrieInputs}; +use plonky2_evm::proof::BlockMetadata; use plonky2_evm::Node; type F = GoldilocksField; @@ -27,7 +25,7 @@ type C = PoseidonGoldilocksConfig; /// Execute the empty list of transactions, i.e. a no-op. #[test] -//#[ignore] // Too slow to run on CI. +#[ignore] // Too slow to run on CI. fn test_empty_txn_list() -> anyhow::Result<()> { init_logger();