diff --git a/evm/src/cpu/kernel/interpreter.rs b/evm/src/cpu/kernel/interpreter.rs index 6039c515..c4deba99 100644 --- a/evm/src/cpu/kernel/interpreter.rs +++ b/evm/src/cpu/kernel/interpreter.rs @@ -200,7 +200,7 @@ impl<'a> Interpreter<'a> { self.generation_state.memory.contexts[0].segments[segment as usize] .content .iter() - .map(|x| x.as_u32() as u8) + .map(|x| x.low_u32() as u8) .collect() } @@ -1045,7 +1045,7 @@ impl<'a> Interpreter<'a> { self.generation_state .memory .mload_general(context, segment, offset + i) - .as_u32() as u8 + .low_u32() as u8 }) .collect(); let value = U256::from_big_endian(&bytes); diff --git a/evm/src/cpu/kernel/tests/blake2_f.rs b/evm/src/cpu/kernel/tests/blake2_f.rs index 1b465bed..b12c9f32 100644 --- a/evm/src/cpu/kernel/tests/blake2_f.rs +++ b/evm/src/cpu/kernel/tests/blake2_f.rs @@ -75,7 +75,7 @@ fn run_blake2_f( Ok(hash .iter() - .map(|&x| x.as_u64()) + .map(|&x| x.low_u64()) .collect::>() .try_into() .unwrap()) diff --git a/evm/src/cross_table_lookup.rs b/evm/src/cross_table_lookup.rs index dfac49f7..213858a8 100644 --- a/evm/src/cross_table_lookup.rs +++ b/evm/src/cross_table_lookup.rs @@ -30,6 +30,7 @@ use crate::vars::{StarkEvaluationTargets, StarkEvaluationVars}; #[derive(Clone, Debug)] pub struct Column { linear_combination: Vec<(usize, F)>, + next_row_linear_combination: Vec<(usize, F)>, constant: F, } @@ -37,6 +38,7 @@ impl Column { pub fn single(c: usize) -> Self { Self { linear_combination: vec![(c, F::ONE)], + next_row_linear_combination: vec![], constant: F::ZERO, } } @@ -47,9 +49,24 @@ impl Column { cs.into_iter().map(|c| Self::single(*c.borrow())) } + pub fn single_next_row(c: usize) -> Self { + Self { + linear_combination: vec![], + next_row_linear_combination: vec![(c, F::ONE)], + constant: F::ZERO, + } + } + + pub fn singles_next_row>>( + cs: I, + ) -> impl Iterator { + cs.into_iter().map(|c| Self::single_next_row(*c.borrow())) + } + pub fn constant(constant: F) -> Self { Self { linear_combination: vec![], + next_row_linear_combination: vec![], constant, } } @@ -75,6 +92,34 @@ impl Column { ); Self { linear_combination: v, + next_row_linear_combination: vec![], + constant, + } + } + + pub fn linear_combination_and_next_row_with_constant>( + iter: I, + next_row_iter: I, + constant: F, + ) -> Self { + let v = iter.into_iter().collect::>(); + let next_row_v = next_row_iter.into_iter().collect::>(); + + assert!(!v.is_empty() || !next_row_v.is_empty()); + debug_assert_eq!( + v.iter().map(|(c, _)| c).unique().count(), + v.len(), + "Duplicate columns." + ); + debug_assert_eq!( + next_row_v.iter().map(|(c, _)| c).unique().count(), + next_row_v.len(), + "Duplicate columns." + ); + + Self { + linear_combination: v, + next_row_linear_combination: next_row_v, constant, } } @@ -111,13 +156,43 @@ impl Column { + FE::from_basefield(self.constant) } + pub fn eval_with_next(&self, v: &[P], next_v: &[P]) -> P + where + FE: FieldExtension, + P: PackedField, + { + self.linear_combination + .iter() + .map(|&(c, f)| v[c] * FE::from_basefield(f)) + .sum::

() + + self + .next_row_linear_combination + .iter() + .map(|&(c, f)| next_v[c] * FE::from_basefield(f)) + .sum::

() + + FE::from_basefield(self.constant) + } + /// Evaluate on an row of a table given in column-major form. pub fn eval_table(&self, table: &[PolynomialValues], row: usize) -> F { - self.linear_combination + let mut res = self + .linear_combination .iter() .map(|&(c, f)| table[c].values[row] * f) .sum::() - + self.constant + + self.constant; + + // If we access the next row at the last row, for sanity, we consider the next row's values to be 0. + // If CTLs are correctly written, the filter should be 0 in that case anyway. + if !self.next_row_linear_combination.is_empty() && row < table.len() - 1 { + res += self + .next_row_linear_combination + .iter() + .map(|&(c, f)| table[c].values[row + 1] * f) + .sum::(); + } + + res } pub fn eval_circuit( @@ -141,6 +216,36 @@ impl Column { let constant = builder.constant_extension(F::Extension::from_basefield(self.constant)); builder.inner_product_extension(F::ONE, constant, pairs) } + + pub fn eval_with_next_circuit( + &self, + builder: &mut CircuitBuilder, + v: &[ExtensionTarget], + next_v: &[ExtensionTarget], + ) -> ExtensionTarget + where + F: RichField + Extendable, + { + let mut pairs = self + .linear_combination + .iter() + .map(|&(c, f)| { + ( + v[c], + builder.constant_extension(F::Extension::from_basefield(f)), + ) + }) + .collect::>(); + let next_row_pairs = self.next_row_linear_combination.iter().map(|&(c, f)| { + ( + next_v[c], + builder.constant_extension(F::Extension::from_basefield(f)), + ) + }); + pairs.extend(next_row_pairs); + let constant = builder.constant_extension(F::Extension::from_basefield(self.constant)); + builder.inner_product_extension(F::ONE, constant, pairs) + } } #[derive(Clone, Debug)] @@ -403,7 +508,7 @@ fn partial_products( let mut partial_prod = F::ONE; let degree = trace[0].len(); let mut res = Vec::with_capacity(degree); - for i in 0..degree { + for i in (0..degree).rev() { let filter = if let Some(column) = filter_column { column.eval_table(trace, i) } else { @@ -420,6 +525,7 @@ fn partial_products( }; res.push(partial_prod); } + res.reverse(); res.into() } @@ -489,6 +595,10 @@ impl<'a, F: RichField + Extendable, const D: usize> } } +/// CTL Z partial products are upside down: the complete product is on the first row, and +/// the first term is on the last row. This allows the transition constraint to be: +/// Z(w) = Z(gw) * combine(w) where combine is called on the local row +/// and not the next. This enables CTLs across two rows. pub(crate) fn eval_cross_table_lookup_checks( vars: StarkEvaluationVars, ctl_vars: &[CtlCheckVars], @@ -507,27 +617,23 @@ pub(crate) fn eval_cross_table_lookup_checks P { - let evals = columns.iter().map(|c| c.eval(v)).collect::>(); - challenges.combine(evals.iter()) - }; - let filter = |v: &[P]| -> P { - if let Some(column) = filter_column { - column.eval(v) - } else { - P::ONES - } - }; - let local_filter = filter(vars.local_values); - let next_filter = filter(vars.next_values); - let select = |filter, x| filter * x + P::ONES - filter; - // Check value of `Z(1)` - consumer.constraint_first_row(*local_z - select(local_filter, combine(vars.local_values))); - // Check `Z(gw) = combination * Z(w)` - consumer.constraint_transition( - *local_z * select(next_filter, combine(vars.next_values)) - *next_z, - ); + let evals = columns + .iter() + .map(|c| c.eval_with_next(vars.local_values, vars.next_values)) + .collect::>(); + let combined = challenges.combine(evals.iter()); + let local_filter = if let Some(column) = filter_column { + column.eval(vars.local_values) + } else { + P::ONES + }; + let select = local_filter * combined + P::ONES - local_filter; + + // Check value of `Z(g^(n-1))` + consumer.constraint_last_row(*local_z - select); + // Check `Z(w) = combination * Z(gw)` + consumer.constraint_transition(*next_z * select - *local_z); } } @@ -620,11 +726,6 @@ pub(crate) fn eval_cross_table_lookup_checks_circuit< } else { one }; - let next_filter = if let Some(column) = filter_column { - column.eval_circuit(builder, vars.next_values) - } else { - one - }; fn select, const D: usize>( builder: &mut CircuitBuilder, filter: ExtensionTarget, @@ -635,34 +736,30 @@ pub(crate) fn eval_cross_table_lookup_checks_circuit< builder.mul_add_extension(filter, x, tmp) // filter * x + 1 - filter } - // Check value of `Z(1)` - let local_columns_eval = columns + let evals = columns .iter() - .map(|c| c.eval_circuit(builder, vars.local_values)) + .map(|c| c.eval_with_next_circuit(builder, vars.local_values, vars.next_values)) .collect::>(); - let combined_local = challenges.combine_circuit(builder, &local_columns_eval); - let selected_local = select(builder, local_filter, combined_local); - let first_row = builder.sub_extension(*local_z, selected_local); - consumer.constraint_first_row(builder, first_row); - // Check `Z(gw) = combination * Z(w)` - let next_columns_eval = columns - .iter() - .map(|c| c.eval_circuit(builder, vars.next_values)) - .collect::>(); - let combined_next = challenges.combine_circuit(builder, &next_columns_eval); - let selected_next = select(builder, next_filter, combined_next); - let transition = builder.mul_sub_extension(*local_z, selected_next, *next_z); + + let combined = challenges.combine_circuit(builder, &evals); + let select = select(builder, local_filter, combined); + + // Check value of `Z(g^(n-1))` + let last_row = builder.sub_extension(*local_z, select); + consumer.constraint_last_row(builder, last_row); + // Check `Z(w) = combination * Z(gw)` + let transition = builder.mul_sub_extension(*next_z, select, *local_z); consumer.constraint_transition(builder, transition); } } pub(crate) fn verify_cross_table_lookups, const D: usize>( cross_table_lookups: &[CrossTableLookup], - ctl_zs_lasts: [Vec; NUM_TABLES], + ctl_zs_first: [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::>(); + let mut ctl_zs_openings = ctl_zs_first.iter().map(|v| v.iter()).collect::>(); for ( index, CrossTableLookup { @@ -695,11 +792,11 @@ pub(crate) fn verify_cross_table_lookups, const D: pub(crate) fn verify_cross_table_lookups_circuit, const D: usize>( builder: &mut CircuitBuilder, cross_table_lookups: Vec>, - ctl_zs_lasts: [Vec; NUM_TABLES], + ctl_zs_first: [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::>(); + let mut ctl_zs_openings = ctl_zs_first.iter().map(|v| v.iter()).collect::>(); for CrossTableLookup { looking_tables, looked_table, diff --git a/evm/src/fixed_recursive_verifier.rs b/evm/src/fixed_recursive_verifier.rs index 33021c39..8c389c2a 100644 --- a/evm/src/fixed_recursive_verifier.rs +++ b/evm/src/fixed_recursive_verifier.rs @@ -49,9 +49,8 @@ use crate::proof::{ use crate::prover::prove; use crate::recursive_verifier::{ add_common_recursion_gates, add_virtual_public_values, - get_memory_extra_looking_products_circuit, recursive_stark_circuit, set_block_hashes_target, - set_block_metadata_target, set_extra_public_values_target, set_public_value_targets, - set_trie_roots_target, PlonkWrapperCircuit, PublicInputs, StarkWrapperCircuit, + get_memory_extra_looking_products_circuit, recursive_stark_circuit, set_public_value_targets, + PlonkWrapperCircuit, PublicInputs, StarkWrapperCircuit, }; use crate::stark::Stark; use crate::util::h256_limbs; @@ -528,7 +527,7 @@ where verify_cross_table_lookups_circuit::( &mut builder, all_cross_table_lookups(), - pis.map(|p| p.ctl_zs_last), + pis.map(|p| p.ctl_zs_first), extra_looking_products, stark_config, ); @@ -907,7 +906,10 @@ where &mut root_inputs, &self.root.public_values, &all_proof.public_values, - ); + ) + .map_err(|_| { + anyhow::Error::msg("Invalid conversion when setting public values targets.") + })?; let root_proof = self.root.circuit.prove(root_inputs)?; @@ -941,32 +943,15 @@ where &self.aggregation.circuit.verifier_only, ); - set_block_hashes_target( + set_public_value_targets( &mut agg_inputs, - &self.aggregation.public_values.block_hashes, - &public_values.block_hashes, - ); - set_block_metadata_target( - &mut agg_inputs, - &self.aggregation.public_values.block_metadata, - &public_values.block_metadata, - ); + &self.aggregation.public_values, + &public_values, + ) + .map_err(|_| { + anyhow::Error::msg("Invalid conversion when setting public values targets.") + })?; - 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, - ); - set_extra_public_values_target( - &mut agg_inputs, - &self.aggregation.public_values.extra_block_data, - &public_values.extra_block_data, - ); let aggregation_proof = self.aggregation.circuit.prove(agg_inputs)?; Ok((aggregation_proof, public_values)) } @@ -1024,32 +1009,10 @@ where block_inputs .set_verifier_data_target(&self.block.cyclic_vk, &self.block.circuit.verifier_only); - set_block_hashes_target( - &mut block_inputs, - &self.block.public_values.block_hashes, - &public_values.block_hashes, - ); - set_extra_public_values_target( - &mut block_inputs, - &self.block.public_values.extra_block_data, - &public_values.extra_block_data, - ); - set_block_metadata_target( - &mut block_inputs, - &self.block.public_values.block_metadata, - &public_values.block_metadata, - ); - - 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, - ); + set_public_value_targets(&mut block_inputs, &self.block.public_values, &public_values) + .map_err(|_| { + anyhow::Error::msg("Invalid conversion when setting public values targets.") + })?; let block_proof = self.block.circuit.prove(block_inputs)?; Ok((block_proof, public_values)) diff --git a/evm/src/generation/state.rs b/evm/src/generation/state.rs index c0c03e28..2b85821f 100644 --- a/evm/src/generation/state.rs +++ b/evm/src/generation/state.rs @@ -116,7 +116,7 @@ impl GenerationState { let code = self.memory.contexts[ctx].segments[Segment::Returndata as usize].content [..returndata_size] .iter() - .map(|x| x.as_u32() as u8) + .map(|x| x.low_u32() as u8) .collect::>(); debug_assert_eq!(keccak(&code), codehash); diff --git a/evm/src/generation/trie_extractor.rs b/evm/src/generation/trie_extractor.rs index 8311f692..a508a720 100644 --- a/evm/src/generation/trie_extractor.rs +++ b/evm/src/generation/trie_extractor.rs @@ -20,7 +20,7 @@ pub(crate) struct AccountTrieRecord { pub(crate) fn read_state_trie_value(slice: &[U256]) -> AccountTrieRecord { AccountTrieRecord { - nonce: slice[0].as_u64(), + nonce: slice[0].low_u64(), balance: slice[1], storage_ptr: slice[2].as_usize(), code_hash: H256::from_uint(&slice[3]), diff --git a/evm/src/get_challenges.rs b/evm/src/get_challenges.rs index e1f2eddb..7fffced4 100644 --- a/evm/src/get_challenges.rs +++ b/evm/src/get_challenges.rs @@ -10,7 +10,8 @@ use crate::all_stark::{AllStark, NUM_TABLES}; use crate::config::StarkConfig; use crate::cross_table_lookup::get_grand_product_challenge_set; use crate::proof::*; -use crate::util::{h256_limbs, u256_limbs}; +use crate::util::{h256_limbs, u256_limbs, u256_to_u32, u256_to_u64}; +use crate::witness::errors::ProgramError; fn observe_root, C: GenericConfig, const D: usize>( challenger: &mut Challenger, @@ -53,35 +54,24 @@ fn observe_block_metadata< >( challenger: &mut Challenger, block_metadata: &BlockMetadata, -) { +) -> Result<(), ProgramError> { challenger.observe_elements( &u256_limbs::(U256::from_big_endian(&block_metadata.block_beneficiary.0))[..5], ); - challenger.observe_element(F::from_canonical_u32( - block_metadata.block_timestamp.as_u32(), - )); - challenger.observe_element(F::from_canonical_u32(block_metadata.block_number.as_u32())); - challenger.observe_element(F::from_canonical_u32( - block_metadata.block_difficulty.as_u32(), - )); - challenger.observe_element(F::from_canonical_u32( - block_metadata.block_gaslimit.as_u32(), - )); - challenger.observe_element(F::from_canonical_u32( - block_metadata.block_chain_id.as_u32(), - )); - challenger.observe_element(F::from_canonical_u32( - block_metadata.block_base_fee.as_u64() as u32, - )); - challenger.observe_element(F::from_canonical_u32( - (block_metadata.block_base_fee.as_u64() >> 32) as u32, - )); - challenger.observe_element(F::from_canonical_u32( - block_metadata.block_gas_used.as_u32(), - )); + challenger.observe_element(u256_to_u32(block_metadata.block_timestamp)?); + challenger.observe_element(u256_to_u32(block_metadata.block_number)?); + challenger.observe_element(u256_to_u32(block_metadata.block_difficulty)?); + challenger.observe_element(u256_to_u32(block_metadata.block_gaslimit)?); + challenger.observe_element(u256_to_u32(block_metadata.block_chain_id)?); + let basefee = u256_to_u64(block_metadata.block_base_fee)?; + challenger.observe_element(basefee.0); + challenger.observe_element(basefee.1); + challenger.observe_element(u256_to_u32(block_metadata.block_gas_used)?); for i in 0..8 { challenger.observe_elements(&u256_limbs(block_metadata.block_bloom[i])); } + + Ok(()) } fn observe_block_metadata_target< @@ -112,17 +102,19 @@ fn observe_extra_block_data< >( challenger: &mut Challenger, extra_data: &ExtraBlockData, -) { - challenger.observe_element(F::from_canonical_u32(extra_data.txn_number_before.as_u32())); - challenger.observe_element(F::from_canonical_u32(extra_data.txn_number_after.as_u32())); - challenger.observe_element(F::from_canonical_u32(extra_data.gas_used_before.as_u32())); - challenger.observe_element(F::from_canonical_u32(extra_data.gas_used_after.as_u32())); +) -> Result<(), ProgramError> { + challenger.observe_element(u256_to_u32(extra_data.txn_number_before)?); + challenger.observe_element(u256_to_u32(extra_data.txn_number_after)?); + challenger.observe_element(u256_to_u32(extra_data.gas_used_before)?); + challenger.observe_element(u256_to_u32(extra_data.gas_used_after)?); for i in 0..8 { challenger.observe_elements(&u256_limbs(extra_data.block_bloom_before[i])); } for i in 0..8 { challenger.observe_elements(&u256_limbs(extra_data.block_bloom_after[i])); } + + Ok(()) } fn observe_extra_block_data_target< @@ -178,12 +170,12 @@ pub(crate) fn observe_public_values< >( challenger: &mut Challenger, public_values: &PublicValues, -) { +) -> Result<(), ProgramError> { observe_trie_roots::(challenger, &public_values.trie_roots_before); observe_trie_roots::(challenger, &public_values.trie_roots_after); - observe_block_metadata::(challenger, &public_values.block_metadata); + observe_block_metadata::(challenger, &public_values.block_metadata)?; observe_block_hashes::(challenger, &public_values.block_hashes); - observe_extra_block_data::(challenger, &public_values.extra_block_data); + observe_extra_block_data::(challenger, &public_values.extra_block_data) } pub(crate) fn observe_public_values_target< @@ -205,19 +197,22 @@ pub(crate) fn observe_public_values_target< impl, C: GenericConfig, const D: usize> AllProof { /// Computes all Fiat-Shamir challenges used in the STARK proof. - pub(crate) fn get_challenges(&self, config: &StarkConfig) -> AllProofChallenges { + pub(crate) fn get_challenges( + &self, + config: &StarkConfig, + ) -> Result, ProgramError> { let mut challenger = Challenger::::new(); for proof in &self.stark_proofs { challenger.observe_cap(&proof.proof.trace_cap); } - observe_public_values::(&mut challenger, &self.public_values); + observe_public_values::(&mut challenger, &self.public_values)?; let ctl_challenges = get_grand_product_challenge_set(&mut challenger, config.num_challenges); - AllProofChallenges { + Ok(AllProofChallenges { stark_challenges: core::array::from_fn(|i| { challenger.compact(); self.stark_proofs[i] @@ -225,7 +220,7 @@ impl, C: GenericConfig, const D: usize> A .get_challenges(&mut challenger, config) }), ctl_challenges, - } + }) } #[allow(unused)] // TODO: should be used soon diff --git a/evm/src/proof.rs b/evm/src/proof.rs index 72515f62..0eccc4a6 100644 --- a/evm/src/proof.rs +++ b/evm/src/proof.rs @@ -623,7 +623,7 @@ impl, C: GenericConfig, const D: usize> S } pub fn num_ctl_zs(&self) -> usize { - self.openings.ctl_zs_last.len() + self.openings.ctl_zs_first.len() } } @@ -700,8 +700,8 @@ pub struct StarkOpeningSet, const D: usize> { pub auxiliary_polys: Vec, /// Openings of lookups and cross-table lookups `Z` polynomials at `g * zeta`. pub auxiliary_polys_next: Vec, - /// Openings of cross-table lookups `Z` polynomials at `g^-1`. - pub ctl_zs_last: Vec, + /// Openings of cross-table lookups `Z` polynomials at `1`. + pub ctl_zs_first: Vec, /// Openings of quotient polynomials at `zeta`. pub quotient_polys: Vec, } @@ -713,7 +713,6 @@ impl, const D: usize> StarkOpeningSet { trace_commitment: &PolynomialBatch, auxiliary_polys_commitment: &PolynomialBatch, quotient_commitment: &PolynomialBatch, - degree_bits: usize, num_lookup_columns: usize, ) -> Self { let eval_commitment = |z: F::Extension, c: &PolynomialBatch| { @@ -734,10 +733,8 @@ impl, const D: usize> StarkOpeningSet { next_values: eval_commitment(zeta_next, trace_commitment), auxiliary_polys: eval_commitment(zeta, auxiliary_polys_commitment), auxiliary_polys_next: eval_commitment(zeta_next, auxiliary_polys_commitment), - ctl_zs_last: eval_commitment_base( - F::primitive_root_of_unity(degree_bits).inverse(), - auxiliary_polys_commitment, - )[num_lookup_columns..] + ctl_zs_first: eval_commitment_base(F::ONE, auxiliary_polys_commitment) + [num_lookup_columns..] .to_vec(), quotient_polys: eval_commitment(zeta, quotient_commitment), } @@ -761,10 +758,10 @@ impl, const D: usize> StarkOpeningSet { .copied() .collect_vec(), }; - debug_assert!(!self.ctl_zs_last.is_empty()); - let ctl_last_batch = FriOpeningBatch { + debug_assert!(!self.ctl_zs_first.is_empty()); + let ctl_first_batch = FriOpeningBatch { values: self - .ctl_zs_last + .ctl_zs_first .iter() .copied() .map(F::Extension::from_basefield) @@ -772,7 +769,7 @@ impl, const D: usize> StarkOpeningSet { }; FriOpenings { - batches: vec![zeta_batch, zeta_next_batch, ctl_last_batch], + batches: vec![zeta_batch, zeta_next_batch, ctl_first_batch], } } } @@ -783,7 +780,7 @@ pub struct StarkOpeningSetTarget { pub next_values: Vec>, pub auxiliary_polys: Vec>, pub auxiliary_polys_next: Vec>, - pub ctl_zs_last: Vec, + pub ctl_zs_first: Vec, pub quotient_polys: Vec>, } @@ -793,7 +790,7 @@ impl StarkOpeningSetTarget { buffer.write_target_ext_vec(&self.next_values)?; buffer.write_target_ext_vec(&self.auxiliary_polys)?; buffer.write_target_ext_vec(&self.auxiliary_polys_next)?; - buffer.write_target_vec(&self.ctl_zs_last)?; + buffer.write_target_vec(&self.ctl_zs_first)?; buffer.write_target_ext_vec(&self.quotient_polys)?; Ok(()) } @@ -803,7 +800,7 @@ impl StarkOpeningSetTarget { let next_values = buffer.read_target_ext_vec::()?; let auxiliary_polys = buffer.read_target_ext_vec::()?; let auxiliary_polys_next = buffer.read_target_ext_vec::()?; - let ctl_zs_last = buffer.read_target_vec()?; + let ctl_zs_first = buffer.read_target_vec()?; let quotient_polys = buffer.read_target_ext_vec::()?; Ok(Self { @@ -811,7 +808,7 @@ impl StarkOpeningSetTarget { next_values, auxiliary_polys, auxiliary_polys_next, - ctl_zs_last, + ctl_zs_first, quotient_polys, }) } @@ -834,10 +831,10 @@ impl StarkOpeningSetTarget { .copied() .collect_vec(), }; - debug_assert!(!self.ctl_zs_last.is_empty()); - let ctl_last_batch = FriOpeningBatchTarget { + debug_assert!(!self.ctl_zs_first.is_empty()); + let ctl_first_batch = FriOpeningBatchTarget { values: self - .ctl_zs_last + .ctl_zs_first .iter() .copied() .map(|t| t.to_ext_target(zero)) @@ -845,7 +842,7 @@ impl StarkOpeningSetTarget { }; FriOpeningsTarget { - batches: vec![zeta_batch, zeta_next_batch, ctl_last_batch], + batches: vec![zeta_batch, zeta_next_batch, ctl_first_batch], } } } diff --git a/evm/src/prover.rs b/evm/src/prover.rs index 10172005..9b95ea37 100644 --- a/evm/src/prover.rs +++ b/evm/src/prover.rs @@ -150,7 +150,8 @@ where challenger.observe_cap(cap); } - observe_public_values::(&mut challenger, &public_values); + observe_public_values::(&mut challenger, &public_values) + .map_err(|_| anyhow::Error::msg("Invalid conversion of public values."))?; let ctl_challenges = get_grand_product_challenge_set(&mut challenger, config.num_challenges); let ctl_data_per_table = timed!( @@ -476,7 +477,6 @@ where trace_commitment, &auxiliary_polys_commitment, "ient_commitment, - degree_bits, stark.num_lookup_helper_columns(config), ); challenger.observe_openings(&openings.to_fri_openings()); @@ -491,7 +491,7 @@ where timing, "compute openings proof", PolynomialBatch::prove_openings( - &stark.fri_instance(zeta, g, degree_bits, ctl_data.len(), config), + &stark.fri_instance(zeta, g, ctl_data.len(), config), &initial_merkle_trees, challenger, &fri_params, diff --git a/evm/src/recursive_verifier.rs b/evm/src/recursive_verifier.rs index 3539f35b..bf9abdf1 100644 --- a/evm/src/recursive_verifier.rs +++ b/evm/src/recursive_verifier.rs @@ -43,9 +43,10 @@ use crate::proof::{ TrieRootsTarget, }; use crate::stark::Stark; -use crate::util::{h256_limbs, u256_limbs}; +use crate::util::{h256_limbs, u256_limbs, u256_to_u32, u256_to_u64}; use crate::vanishing_poly::eval_vanishing_poly_circuit; use crate::vars::StarkEvaluationTargets; +use crate::witness::errors::ProgramError; /// Table-wise recursive proofs of an `AllProof`. pub struct RecursiveAllProof< @@ -59,7 +60,7 @@ pub struct RecursiveAllProof< pub(crate) struct PublicInputs> { pub(crate) trace_cap: Vec>, - pub(crate) ctl_zs_last: Vec, + pub(crate) ctl_zs_first: Vec, pub(crate) ctl_challenges: GrandProductChallengeSet, pub(crate) challenger_state_before: P, pub(crate) challenger_state_after: P, @@ -85,11 +86,11 @@ impl> Public }; let challenger_state_before = P::new(&mut iter); let challenger_state_after = P::new(&mut iter); - let ctl_zs_last: Vec<_> = iter.collect(); + let ctl_zs_first: Vec<_> = iter.collect(); Self { trace_cap, - ctl_zs_last, + ctl_zs_first, ctl_challenges, challenger_state_before, challenger_state_after, @@ -150,7 +151,7 @@ impl, C: GenericConfig, const D: usize> // Verify the CTL checks. verify_cross_table_lookups::( &cross_table_lookups, - pis.map(|p| p.ctl_zs_last), + pis.map(|p| p.ctl_zs_first), extra_looking_products, inner_config, )?; @@ -344,7 +345,7 @@ where let challenger_state = challenger.compact(&mut builder); builder.register_public_inputs(challenger_state.as_ref()); - builder.register_public_inputs(&proof_target.openings.ctl_zs_last); + builder.register_public_inputs(&proof_target.openings.ctl_zs_first); verify_stark_proof_with_challenges_circuit::( &mut builder, @@ -410,7 +411,7 @@ fn verify_stark_proof_with_challenges_circuit< next_values, auxiliary_polys, auxiliary_polys_next, - ctl_zs_last, + ctl_zs_first, quotient_polys, } = &proof.openings; let vars = StarkEvaluationTargets { @@ -485,8 +486,7 @@ fn verify_stark_proof_with_challenges_circuit< builder, challenges.stark_zeta, F::primitive_root_of_unity(degree_bits), - degree_bits, - ctl_zs_last.len(), + ctl_zs_first.len(), inner_config, ); builder.verify_fri_proof::( @@ -870,7 +870,7 @@ fn add_virtual_stark_opening_set, S: Stark, c .add_virtual_extension_targets(stark.num_lookup_helper_columns(config) + num_ctl_zs), auxiliary_polys_next: builder .add_virtual_extension_targets(stark.num_lookup_helper_columns(config) + num_ctl_zs), - ctl_zs_last: builder.add_virtual_targets(num_ctl_zs), + ctl_zs_first: builder.add_virtual_targets(num_ctl_zs), quotient_polys: builder .add_virtual_extension_targets(stark.quotient_degree_factor() * num_challenges), } @@ -906,7 +906,8 @@ pub(crate) fn set_public_value_targets( witness: &mut W, public_values_target: &PublicValuesTarget, public_values: &PublicValues, -) where +) -> Result<(), ProgramError> +where F: RichField + Extendable, W: Witness, { @@ -924,7 +925,7 @@ pub(crate) fn set_public_value_targets( witness, &public_values_target.block_metadata, &public_values.block_metadata, - ); + )?; set_block_hashes_target( witness, &public_values_target.block_hashes, @@ -935,6 +936,8 @@ pub(crate) fn set_public_value_targets( &public_values_target.extra_block_data, &public_values.extra_block_data, ); + + Ok(()) } pub(crate) fn set_trie_roots_target( @@ -995,7 +998,8 @@ pub(crate) fn set_block_metadata_target( witness: &mut W, block_metadata_target: &BlockMetadataTarget, block_metadata: &BlockMetadata, -) where +) -> Result<(), ProgramError> +where F: RichField + Extendable, W: Witness, { @@ -1006,42 +1010,39 @@ pub(crate) fn set_block_metadata_target( witness.set_target_arr(&block_metadata_target.block_beneficiary, &beneficiary_limbs); witness.set_target( block_metadata_target.block_timestamp, - F::from_canonical_u32(block_metadata.block_timestamp.as_u32()), + u256_to_u32(block_metadata.block_timestamp)?, ); witness.set_target( block_metadata_target.block_number, - F::from_canonical_u32(block_metadata.block_number.as_u32()), + u256_to_u32(block_metadata.block_number)?, ); witness.set_target( block_metadata_target.block_difficulty, - F::from_canonical_u32(block_metadata.block_difficulty.as_u32()), + u256_to_u32(block_metadata.block_difficulty)?, ); witness.set_target( block_metadata_target.block_gaslimit, - F::from_canonical_u32(block_metadata.block_gaslimit.as_u32()), + u256_to_u32(block_metadata.block_gaslimit)?, ); witness.set_target( block_metadata_target.block_chain_id, - F::from_canonical_u32(block_metadata.block_chain_id.as_u32()), + u256_to_u32(block_metadata.block_chain_id)?, ); // Basefee fits in 2 limbs - witness.set_target( - block_metadata_target.block_base_fee[0], - F::from_canonical_u32(block_metadata.block_base_fee.as_u64() as u32), - ); - witness.set_target( - block_metadata_target.block_base_fee[1], - F::from_canonical_u32((block_metadata.block_base_fee.as_u64() >> 32) as u32), - ); + let basefee = u256_to_u64(block_metadata.block_base_fee)?; + witness.set_target(block_metadata_target.block_base_fee[0], basefee.0); + witness.set_target(block_metadata_target.block_base_fee[1], basefee.1); witness.set_target( block_metadata_target.block_gas_used, - F::from_canonical_u64(block_metadata.block_gas_used.as_u64()), + u256_to_u32(block_metadata.block_gas_used)?, ); let mut block_bloom_limbs = [F::ZERO; 64]; for (i, limbs) in block_bloom_limbs.chunks_exact_mut(8).enumerate() { limbs.copy_from_slice(&u256_limbs(block_metadata.block_bloom[i])); } witness.set_target_arr(&block_metadata_target.block_bloom, &block_bloom_limbs); + + Ok(()) } pub(crate) fn set_block_hashes_target( diff --git a/evm/src/stark.rs b/evm/src/stark.rs index 4e76017d..ead647b3 100644 --- a/evm/src/stark.rs +++ b/evm/src/stark.rs @@ -83,7 +83,6 @@ pub trait Stark, const D: usize>: Sync { &self, zeta: F::Extension, g: F, - degree_bits: usize, num_ctl_zs: usize, config: &StarkConfig, ) -> FriInstanceInfo { @@ -128,13 +127,13 @@ pub trait Stark, const D: usize>: Sync { point: zeta.scalar_mul(g), polynomials: [trace_info, auxiliary_polys_info].concat(), }; - let ctl_last_batch = FriBatchInfo { - point: F::Extension::primitive_root_of_unity(degree_bits).inverse(), + let ctl_first_batch = FriBatchInfo { + point: F::Extension::ONE, polynomials: ctl_zs_info, }; FriInstanceInfo { oracles: vec![trace_oracle, auxiliary_oracle, quotient_oracle], - batches: vec![zeta_batch, zeta_next_batch, ctl_last_batch], + batches: vec![zeta_batch, zeta_next_batch, ctl_first_batch], } } @@ -144,7 +143,6 @@ pub trait Stark, const D: usize>: Sync { builder: &mut CircuitBuilder, zeta: ExtensionTarget, g: F, - degree_bits: usize, num_ctl_zs: usize, inner_config: &StarkConfig, ) -> FriInstanceInfoTarget { @@ -190,14 +188,13 @@ pub trait Stark, const D: usize>: Sync { point: zeta_next, polynomials: [trace_info, auxiliary_polys_info].concat(), }; - let ctl_last_batch = FriBatchInfoTarget { - point: builder - .constant_extension(F::Extension::primitive_root_of_unity(degree_bits).inverse()), + let ctl_first_batch = FriBatchInfoTarget { + point: builder.one_extension(), polynomials: ctl_zs_info, }; FriInstanceInfoTarget { oracles: vec![trace_oracle, auxiliary_oracle, quotient_oracle], - batches: vec![zeta_batch, zeta_next_batch, ctl_last_batch], + batches: vec![zeta_batch, zeta_next_batch, ctl_first_batch], } } diff --git a/evm/src/util.rs b/evm/src/util.rs index 5fa085dc..a3f6d050 100644 --- a/evm/src/util.rs +++ b/evm/src/util.rs @@ -11,6 +11,8 @@ use plonky2::hash::hash_types::RichField; use plonky2::iop::ext_target::ExtensionTarget; use plonky2::util::transpose; +use crate::witness::errors::ProgramError; + /// Construct an integer from its constituent bits (in little-endian order) pub fn limb_from_bits_le(iter: impl IntoIterator) -> P { // TODO: This is technically wrong, as 1 << i won't be canonical for all fields... @@ -45,6 +47,29 @@ pub fn trace_rows_to_poly_values( .collect() } +/// Returns the lowest LE 32-bit limb of a `U256` as a field element, +/// and errors if the integer is actually greater. +pub(crate) fn u256_to_u32(u256: U256) -> Result { + if TryInto::::try_into(u256).is_err() { + return Err(ProgramError::IntegerTooLarge); + } + + Ok(F::from_canonical_u32(u256.low_u32())) +} + +/// Returns the lowest LE 64-bit word of a `U256` as two field elements +/// each storing a 32-bit limb, and errors if the integer is actually greater. +pub(crate) fn u256_to_u64(u256: U256) -> Result<(F, F), ProgramError> { + if TryInto::::try_into(u256).is_err() { + return Err(ProgramError::IntegerTooLarge); + } + + Ok(( + F::from_canonical_u32(u256.low_u64() as u32), + F::from_canonical_u32((u256.low_u64() >> 32) as u32), + )) +} + #[allow(unused)] // TODO: Remove? /// Returns the 32-bit little-endian limbs of a `U256`. pub(crate) fn u256_limbs(u256: U256) -> [F; 8] { diff --git a/evm/src/verifier.rs b/evm/src/verifier.rs index e4c277fd..1fc339bd 100644 --- a/evm/src/verifier.rs +++ b/evm/src/verifier.rs @@ -52,7 +52,9 @@ where let AllProofChallenges { stark_challenges, ctl_challenges, - } = all_proof.get_challenges(config); + } = all_proof + .get_challenges(config) + .map_err(|_| anyhow::Error::msg("Invalid sampling of proof challenges."))?; let num_lookup_columns = all_stark.num_lookups_helper_columns(config); @@ -144,7 +146,9 @@ where verify_cross_table_lookups::( cross_table_lookups, - all_proof.stark_proofs.map(|p| p.proof.openings.ctl_zs_last), + all_proof + .stark_proofs + .map(|p| p.proof.openings.ctl_zs_first), extra_looking_products, config, ) @@ -318,7 +322,7 @@ where next_values, auxiliary_polys, auxiliary_polys_next, - ctl_zs_last, + ctl_zs_first, quotient_polys, } = &proof.openings; let vars = StarkEvaluationVars { @@ -393,8 +397,7 @@ where &stark.fri_instance( challenges.stark_zeta, F::primitive_root_of_unity(degree_bits), - degree_bits, - ctl_zs_last.len(), + ctl_zs_first.len(), config, ), &proof.openings.to_fri_openings(), @@ -434,7 +437,7 @@ where next_values, auxiliary_polys, auxiliary_polys_next, - ctl_zs_last, + ctl_zs_first, quotient_polys, } = openings; @@ -451,7 +454,7 @@ where ensure!(next_values.len() == S::COLUMNS); ensure!(auxiliary_polys.len() == num_auxiliary); ensure!(auxiliary_polys_next.len() == num_auxiliary); - ensure!(ctl_zs_last.len() == num_ctl_zs); + ensure!(ctl_zs_first.len() == num_ctl_zs); ensure!(quotient_polys.len() == stark.num_quotient_polys(config)); Ok(()) diff --git a/evm/src/witness/errors.rs b/evm/src/witness/errors.rs index 44693a33..1ab99eae 100644 --- a/evm/src/witness/errors.rs +++ b/evm/src/witness/errors.rs @@ -13,6 +13,7 @@ pub enum ProgramError { MemoryError(MemoryError), GasLimitError, InterpreterError, + IntegerTooLarge, } #[allow(clippy::enum_variant_names)] diff --git a/evm/src/witness/mod.rs b/evm/src/witness/mod.rs index 7d491e4e..fbb88a71 100644 --- a/evm/src/witness/mod.rs +++ b/evm/src/witness/mod.rs @@ -1,4 +1,4 @@ -mod errors; +pub(crate) mod errors; mod gas; pub(crate) mod memory; mod operation; diff --git a/evm/src/witness/operation.rs b/evm/src/witness/operation.rs index 23d64be4..8349d56d 100644 --- a/evm/src/witness/operation.rs +++ b/evm/src/witness/operation.rs @@ -137,7 +137,7 @@ pub(crate) fn generate_keccak_general( ..base_address }; let val = state.memory.get(address); - val.as_u32() as u8 + val.low_u32() as u8 }) .collect_vec(); log::debug!("Hashing {:?}", input); @@ -375,7 +375,7 @@ pub(crate) fn generate_push( Segment::Code, initial_offset + i, )) - .as_u32() as u8 + .low_u32() as u8 }) .collect_vec(); @@ -709,7 +709,7 @@ pub(crate) fn generate_mload_32bytes( ..base_address }; let val = state.memory.get(address); - val.as_u32() as u8 + val.low_u32() as u8 }) .collect_vec(); diff --git a/plonky2/src/lib.rs b/plonky2/src/lib.rs index b4a4b6af..c2913023 100644 --- a/plonky2/src/lib.rs +++ b/plonky2/src/lib.rs @@ -12,7 +12,9 @@ pub mod gadgets; pub mod gates; pub mod hash; pub mod iop; -pub mod lookup_test; pub mod plonk; pub mod recursion; pub mod util; + +#[cfg(test)] +mod lookup_test; diff --git a/plonky2/src/lookup_test.rs b/plonky2/src/lookup_test.rs index 165d5dfe..bca90d59 100644 --- a/plonky2/src/lookup_test.rs +++ b/plonky2/src/lookup_test.rs @@ -1,523 +1,477 @@ -#[cfg(test)] -mod tests { - static LOGGER_INITIALIZED: Once = Once::new(); - - use alloc::sync::Arc; - use std::sync::Once; - - use itertools::Itertools; - use log::{Level, LevelFilter}; - - use crate::gadgets::lookup::{OTHER_TABLE, SMALLER_TABLE, TIP5_TABLE}; - use crate::gates::lookup_table::LookupTable; - use crate::gates::noop::NoopGate; - use crate::plonk::prover::prove; - use crate::util::timing::TimingTree; - - #[test] - fn test_no_lookup() -> anyhow::Result<()> { - LOGGER_INITIALIZED.call_once(|| init_logger().unwrap()); - use crate::iop::witness::PartialWitness; - use crate::plonk::circuit_builder::CircuitBuilder; - use crate::plonk::circuit_data::CircuitConfig; - use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; - - const D: usize = 2; - type C = PoseidonGoldilocksConfig; - type F = >::F; - - let config = CircuitConfig::standard_recursion_config(); - let mut builder = CircuitBuilder::::new(config); - builder.add_gate(NoopGate, vec![]); - let pw = PartialWitness::new(); - - let data = builder.build::(); - let mut timing = TimingTree::new("prove first", Level::Debug); - let proof = prove(&data.prover_only, &data.common, pw, &mut timing)?; - timing.print(); - data.verify(proof)?; - - Ok(()) - } - - #[should_panic] - #[test] - fn test_lookup_table_not_used() { - LOGGER_INITIALIZED.call_once(|| init_logger().unwrap()); - use crate::plonk::circuit_builder::CircuitBuilder; - use crate::plonk::circuit_data::CircuitConfig; - use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; - - const D: usize = 2; - type C = PoseidonGoldilocksConfig; - type F = >::F; - - let config = CircuitConfig::standard_recursion_config(); - let mut builder = CircuitBuilder::::new(config); - - let tip5_table = TIP5_TABLE.to_vec(); - let table: LookupTable = Arc::new((0..256).zip_eq(tip5_table).collect()); - builder.add_lookup_table_from_pairs(table); - - builder.build::(); - } - - #[should_panic] - #[test] - fn test_lookup_without_table() { - LOGGER_INITIALIZED.call_once(|| init_logger().unwrap()); - use crate::plonk::circuit_builder::CircuitBuilder; - use crate::plonk::circuit_data::CircuitConfig; - use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; - - const D: usize = 2; - type C = PoseidonGoldilocksConfig; - type F = >::F; - - let config = CircuitConfig::standard_recursion_config(); - let mut builder = CircuitBuilder::::new(config); - - let dummy = builder.add_virtual_target(); - builder.add_lookup_from_index(dummy, 0); - - builder.build::(); - } - - // Tests two lookups in one lookup table. - #[test] - fn test_one_lookup() -> anyhow::Result<()> { - use crate::field::types::Field; - use crate::iop::witness::{PartialWitness, WitnessWrite}; - use crate::plonk::circuit_builder::CircuitBuilder; - use crate::plonk::circuit_data::CircuitConfig; - use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; - - const D: usize = 2; - type C = PoseidonGoldilocksConfig; - type F = >::F; - - LOGGER_INITIALIZED.call_once(|| init_logger().unwrap()); - let tip5_table = TIP5_TABLE.to_vec(); - let table: LookupTable = Arc::new((0..256).zip_eq(tip5_table).collect()); - let config = CircuitConfig::standard_recursion_config(); - let mut builder = CircuitBuilder::::new(config); - - let initial_a = builder.add_virtual_target(); - let initial_b = builder.add_virtual_target(); - - let look_val_a = 1; - let look_val_b = 2; - - let out_a = table[look_val_a].1; - let out_b = table[look_val_b].1; - let table_index = builder.add_lookup_table_from_pairs(table); - let output_a = builder.add_lookup_from_index(initial_a, table_index); - - let output_b = builder.add_lookup_from_index(initial_b, table_index); - - builder.register_public_input(initial_a); - builder.register_public_input(initial_b); - builder.register_public_input(output_a); - builder.register_public_input(output_b); - - let mut pw = PartialWitness::new(); - - pw.set_target(initial_a, F::from_canonical_usize(look_val_a)); - pw.set_target(initial_b, F::from_canonical_usize(look_val_b)); - - let data = builder.build::(); - let mut timing = TimingTree::new("prove one lookup", Level::Debug); - let proof = prove(&data.prover_only, &data.common, pw, &mut timing)?; - timing.print(); - data.verify(proof.clone())?; - - assert!( - proof.public_inputs[2] == F::from_canonical_u16(out_a), - "First lookup, at index {} in the Tip5 table gives an incorrect output.", - proof.public_inputs[0] - ); - assert!( - proof.public_inputs[3] == F::from_canonical_u16(out_b), - "Second lookup, at index {} in the Tip5 table gives an incorrect output.", - proof.public_inputs[1] - ); - - Ok(()) - } - - // Tests one lookup in two different lookup tables. - #[test] - pub fn test_two_luts() -> anyhow::Result<()> { - use crate::field::types::Field; - use crate::iop::witness::{PartialWitness, WitnessWrite}; - use crate::plonk::circuit_builder::CircuitBuilder; - use crate::plonk::circuit_data::CircuitConfig; - use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; - - const D: usize = 2; - type C = PoseidonGoldilocksConfig; - type F = >::F; - - LOGGER_INITIALIZED.call_once(|| init_logger().unwrap()); - let config = CircuitConfig::standard_recursion_config(); - let mut builder = CircuitBuilder::::new(config); - - let initial_a = builder.add_virtual_target(); - let initial_b = builder.add_virtual_target(); - - let look_val_a = 1; - let look_val_b = 2; - - let tip5_table = TIP5_TABLE.to_vec(); - - let first_out = tip5_table[look_val_a]; - let second_out = tip5_table[look_val_b]; - - let table: LookupTable = Arc::new((0..256).zip_eq(tip5_table).collect()); - - let other_table = OTHER_TABLE.to_vec(); - - let table_index = builder.add_lookup_table_from_pairs(table); - let output_a = builder.add_lookup_from_index(initial_a, table_index); - - let output_b = builder.add_lookup_from_index(initial_b, table_index); - let sum = builder.add(output_a, output_b); - - let s = first_out + second_out; - let final_out = other_table[s as usize]; - - let table2: LookupTable = Arc::new((0..256).zip_eq(other_table).collect()); - let table2_index = builder.add_lookup_table_from_pairs(table2); - - let output_final = builder.add_lookup_from_index(sum, table2_index); - - builder.register_public_input(initial_a); - builder.register_public_input(initial_b); - builder.register_public_input(sum); - builder.register_public_input(output_a); - builder.register_public_input(output_b); - builder.register_public_input(output_final); - - let mut pw = PartialWitness::new(); - pw.set_target(initial_a, F::from_canonical_usize(look_val_a)); - pw.set_target(initial_b, F::from_canonical_usize(look_val_b)); - let data = builder.build::(); - let mut timing = TimingTree::new("prove two_luts", Level::Debug); - let proof = prove(&data.prover_only, &data.common, pw, &mut timing)?; - data.verify(proof.clone())?; - timing.print(); - - assert!( - proof.public_inputs[3] == F::from_canonical_u16(first_out), - "First lookup, at index {} in the Tip5 table gives an incorrect output.", - proof.public_inputs[0] - ); - assert!( - proof.public_inputs[4] == F::from_canonical_u16(second_out), - "Second lookup, at index {} in the Tip5 table gives an incorrect output.", - proof.public_inputs[1] - ); - assert!( - proof.public_inputs[2] == F::from_canonical_u16(s), - "Sum between the first two LUT outputs is incorrect." - ); - assert!( - proof.public_inputs[5] == F::from_canonical_u16(final_out), - "Output of the second LUT at index {} is incorrect.", - s - ); - - Ok(()) - } - - #[test] - pub fn test_different_inputs() -> anyhow::Result<()> { - use crate::field::types::Field; - use crate::iop::witness::{PartialWitness, WitnessWrite}; - use crate::plonk::circuit_builder::CircuitBuilder; - use crate::plonk::circuit_data::CircuitConfig; - use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; - - const D: usize = 2; - type C = PoseidonGoldilocksConfig; - type F = >::F; - LOGGER_INITIALIZED.call_once(|| init_logger().unwrap()); - let config = CircuitConfig::standard_recursion_config(); - let mut builder = CircuitBuilder::::new(config); - - let initial_a = builder.add_virtual_target(); - let initial_b = builder.add_virtual_target(); - - let init_a = 1; - let init_b = 2; - - let tab: Vec = SMALLER_TABLE.to_vec(); - let table: LookupTable = Arc::new((2..10).zip_eq(tab).collect()); - - let other_table = OTHER_TABLE.to_vec(); - - let table2: LookupTable = Arc::new((0..256).zip_eq(other_table).collect()); - let small_index = builder.add_lookup_table_from_pairs(table.clone()); - let output_a = builder.add_lookup_from_index(initial_a, small_index); - - let output_b = builder.add_lookup_from_index(initial_b, small_index); - let sum = builder.add(output_a, output_b); - - let other_index = builder.add_lookup_table_from_pairs(table2.clone()); - let output_final = builder.add_lookup_from_index(sum, other_index); - - builder.register_public_input(initial_a); - builder.register_public_input(initial_b); - builder.register_public_input(sum); - builder.register_public_input(output_a); - builder.register_public_input(output_b); - builder.register_public_input(output_final); - - let mut pw = PartialWitness::new(); - - let look_val_a = table[init_a].0; - let look_val_b = table[init_b].0; - pw.set_target(initial_a, F::from_canonical_u16(look_val_a)); - pw.set_target(initial_b, F::from_canonical_u16(look_val_b)); - - let data = builder.build::(); - let mut timing = TimingTree::new("prove different lookups", Level::Debug); - let proof = prove(&data.prover_only, &data.common, pw, &mut timing)?; - data.verify(proof.clone())?; - timing.print(); - - let out_a = table[init_a].1; - let out_b = table[init_b].1; - let s = out_a + out_b; - let out_final = table2[s as usize].1; - - assert!( - proof.public_inputs[3] == F::from_canonical_u16(out_a), - "First lookup, at index {} in the smaller LUT gives an incorrect output.", - proof.public_inputs[0] - ); - assert!( - proof.public_inputs[4] == F::from_canonical_u16(out_b), - "Second lookup, at index {} in the smaller LUT gives an incorrect output.", - proof.public_inputs[1] - ); - assert!( - proof.public_inputs[2] == F::from_canonical_u16(s), - "Sum between the first two LUT outputs is incorrect." - ); - assert!( - proof.public_inputs[5] == F::from_canonical_u16(out_final), - "Output of the second LUT at index {} is incorrect.", - s - ); - - Ok(()) - } - - // This test looks up over 514 values for one LookupTableGate, which means that several LookupGates are created. - #[test] - pub fn test_many_lookups() -> anyhow::Result<()> { - use crate::field::types::Field; - use crate::iop::witness::{PartialWitness, WitnessWrite}; - use crate::plonk::circuit_builder::CircuitBuilder; - use crate::plonk::circuit_data::CircuitConfig; - use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; - - const D: usize = 2; - type C = PoseidonGoldilocksConfig; - type F = >::F; - LOGGER_INITIALIZED.call_once(|| init_logger().unwrap()); - let config = CircuitConfig::standard_recursion_config(); - let mut builder = CircuitBuilder::::new(config); - - let initial_a = builder.add_virtual_target(); - let initial_b = builder.add_virtual_target(); - - let look_val_a = 1; - let look_val_b = 2; - - let tip5_table = TIP5_TABLE.to_vec(); - let table: LookupTable = Arc::new((0..256).zip_eq(tip5_table).collect()); - - let out_a = table[look_val_a].1; - let out_b = table[look_val_b].1; - - let tip5_index = builder.add_lookup_table_from_pairs(table); - let output_a = builder.add_lookup_from_index(initial_a, tip5_index); - - let output_b = builder.add_lookup_from_index(initial_b, tip5_index); - let sum = builder.add(output_a, output_b); - - for _ in 0..514 { - builder.add_lookup_from_index(initial_a, tip5_index); - } - - let other_table = OTHER_TABLE.to_vec(); - - let table2: LookupTable = Arc::new((0..256).zip_eq(other_table).collect()); - - let s = out_a + out_b; - let out_final = table2[s as usize].1; - - let other_index = builder.add_lookup_table_from_pairs(table2); - let output_final = builder.add_lookup_from_index(sum, other_index); - - builder.register_public_input(initial_a); - builder.register_public_input(initial_b); - builder.register_public_input(sum); - builder.register_public_input(output_a); - builder.register_public_input(output_b); - builder.register_public_input(output_final); - - let mut pw = PartialWitness::new(); - - pw.set_target(initial_a, F::from_canonical_usize(look_val_a)); - pw.set_target(initial_b, F::from_canonical_usize(look_val_b)); - - let data = builder.build::(); - let mut timing = TimingTree::new("prove different lookups", Level::Debug); - let proof = prove(&data.prover_only, &data.common, pw, &mut timing)?; - - data.verify(proof.clone())?; - timing.print(); - - assert!( - proof.public_inputs[3] == F::from_canonical_u16(out_a), - "First lookup, at index {} in the Tip5 table gives an incorrect output.", - proof.public_inputs[0] - ); - assert!( - proof.public_inputs[4] == F::from_canonical_u16(out_b), - "Second lookup, at index {} in the Tip5 table gives an incorrect output.", - proof.public_inputs[1] - ); - assert!( - proof.public_inputs[2] == F::from_canonical_u16(s), - "Sum between the first two LUT outputs is incorrect." - ); - assert!( - proof.public_inputs[5] == F::from_canonical_u16(out_final), - "Output of the second LUT at index {} is incorrect.", - s - ); - - Ok(()) - } - - // Tests whether, when adding the same LUT to the circuit, the circuit only adds one copy, with the same index. - #[test] - pub fn test_same_luts() -> anyhow::Result<()> { - use crate::field::types::Field; - use crate::iop::witness::{PartialWitness, WitnessWrite}; - use crate::plonk::circuit_builder::CircuitBuilder; - use crate::plonk::circuit_data::CircuitConfig; - use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; - - const D: usize = 2; - type C = PoseidonGoldilocksConfig; - type F = >::F; - - LOGGER_INITIALIZED.call_once(|| init_logger().unwrap()); - let config = CircuitConfig::standard_recursion_config(); - let mut builder = CircuitBuilder::::new(config); - - let initial_a = builder.add_virtual_target(); - let initial_b = builder.add_virtual_target(); - - let look_val_a = 1; - let look_val_b = 2; - - let tip5_table = TIP5_TABLE.to_vec(); - let table: LookupTable = Arc::new((0..256).zip_eq(tip5_table).collect()); - - let table_index = builder.add_lookup_table_from_pairs(table.clone()); - let output_a = builder.add_lookup_from_index(initial_a, table_index); - - let output_b = builder.add_lookup_from_index(initial_b, table_index); - let sum = builder.add(output_a, output_b); - - let table2_index = builder.add_lookup_table_from_pairs(table); - - let output_final = builder.add_lookup_from_index(sum, table2_index); - - builder.register_public_input(initial_a); - builder.register_public_input(initial_b); - builder.register_public_input(sum); - builder.register_public_input(output_a); - builder.register_public_input(output_b); - builder.register_public_input(output_final); - - let luts_length = builder.get_luts_length(); - - assert!( - luts_length == 1, - "There are {} LUTs when there should be only one", - luts_length - ); - - let mut pw = PartialWitness::new(); - - pw.set_target(initial_a, F::from_canonical_usize(look_val_a)); - pw.set_target(initial_b, F::from_canonical_usize(look_val_b)); - - let data = builder.build::(); - let mut timing = TimingTree::new("prove two_luts", Level::Debug); - let proof = prove(&data.prover_only, &data.common, pw, &mut timing)?; - data.verify(proof)?; - timing.print(); - - Ok(()) - } - - #[test] - fn test_circuit_build_mock() { - // This code is taken from examples/fibonacci.rs - use crate::field::types::Field; - use crate::iop::witness::{PartialWitness, Witness, WitnessWrite}; - use crate::plonk::circuit_builder::CircuitBuilder; - use crate::plonk::circuit_data::CircuitConfig; - use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; - - const D: usize = 2; - type C = PoseidonGoldilocksConfig; - type F = >::F; - - let config = CircuitConfig::standard_recursion_config(); - let mut builder = CircuitBuilder::::new(config); - - // The arithmetic circuit. - let initial_a = builder.add_virtual_target(); - let initial_b = builder.add_virtual_target(); - let mut prev_target = initial_a; - let mut cur_target = initial_b; - for _ in 0..99 { - let temp = builder.add(prev_target, cur_target); - prev_target = cur_target; - cur_target = temp; - } - - // Public inputs are the two initial values (provided below) and the result (which is generated). - builder.register_public_input(initial_a); - builder.register_public_input(initial_b); - builder.register_public_input(cur_target); - - // Provide initial values. - let mut pw = PartialWitness::new(); - pw.set_target(initial_a, F::ZERO); - pw.set_target(initial_b, F::ONE); - - let data = builder.mock_build::(); - let partition_witness = data.generate_witness(pw); - let result = partition_witness.try_get_target(cur_target).unwrap(); - assert_eq!(result, F::from_canonical_u64(3736710860384812976)); - } - - fn init_logger() -> anyhow::Result<()> { - let mut builder = env_logger::Builder::from_default_env(); - builder.format_timestamp(None); - builder.filter_level(LevelFilter::Debug); - - builder.try_init()?; - Ok(()) - } +static LOGGER_INITIALIZED: Once = Once::new(); + +use alloc::sync::Arc; +use std::sync::Once; + +use itertools::Itertools; +use log::{Level, LevelFilter}; + +use crate::gadgets::lookup::{OTHER_TABLE, SMALLER_TABLE, TIP5_TABLE}; +use crate::gates::lookup_table::LookupTable; +use crate::gates::noop::NoopGate; +use crate::plonk::prover::prove; +use crate::util::timing::TimingTree; + +#[test] +fn test_no_lookup() -> anyhow::Result<()> { + LOGGER_INITIALIZED.call_once(|| init_logger().unwrap()); + use crate::iop::witness::PartialWitness; + use crate::plonk::circuit_builder::CircuitBuilder; + use crate::plonk::circuit_data::CircuitConfig; + use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; + + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + type F = >::F; + + let config = CircuitConfig::standard_recursion_config(); + let mut builder = CircuitBuilder::::new(config); + builder.add_gate(NoopGate, vec![]); + let pw = PartialWitness::new(); + + let data = builder.build::(); + let mut timing = TimingTree::new("prove first", Level::Debug); + let proof = prove(&data.prover_only, &data.common, pw, &mut timing)?; + timing.print(); + data.verify(proof)?; + + Ok(()) +} + +#[should_panic] +#[test] +fn test_lookup_table_not_used() { + LOGGER_INITIALIZED.call_once(|| init_logger().unwrap()); + use crate::plonk::circuit_builder::CircuitBuilder; + use crate::plonk::circuit_data::CircuitConfig; + use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; + + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + type F = >::F; + + let config = CircuitConfig::standard_recursion_config(); + let mut builder = CircuitBuilder::::new(config); + + let tip5_table = TIP5_TABLE.to_vec(); + let table: LookupTable = Arc::new((0..256).zip_eq(tip5_table).collect()); + builder.add_lookup_table_from_pairs(table); + + builder.build::(); +} + +#[should_panic] +#[test] +fn test_lookup_without_table() { + LOGGER_INITIALIZED.call_once(|| init_logger().unwrap()); + use crate::plonk::circuit_builder::CircuitBuilder; + use crate::plonk::circuit_data::CircuitConfig; + use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; + + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + type F = >::F; + + let config = CircuitConfig::standard_recursion_config(); + let mut builder = CircuitBuilder::::new(config); + + let dummy = builder.add_virtual_target(); + builder.add_lookup_from_index(dummy, 0); + + builder.build::(); +} + +// Tests two lookups in one lookup table. +#[test] +fn test_one_lookup() -> anyhow::Result<()> { + use crate::field::types::Field; + use crate::iop::witness::{PartialWitness, WitnessWrite}; + use crate::plonk::circuit_builder::CircuitBuilder; + use crate::plonk::circuit_data::CircuitConfig; + use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; + + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + type F = >::F; + + LOGGER_INITIALIZED.call_once(|| init_logger().unwrap()); + let tip5_table = TIP5_TABLE.to_vec(); + let table: LookupTable = Arc::new((0..256).zip_eq(tip5_table).collect()); + let config = CircuitConfig::standard_recursion_config(); + let mut builder = CircuitBuilder::::new(config); + + let initial_a = builder.add_virtual_target(); + let initial_b = builder.add_virtual_target(); + + let look_val_a = 1; + let look_val_b = 2; + + let out_a = table[look_val_a].1; + let out_b = table[look_val_b].1; + let table_index = builder.add_lookup_table_from_pairs(table); + let output_a = builder.add_lookup_from_index(initial_a, table_index); + + let output_b = builder.add_lookup_from_index(initial_b, table_index); + + builder.register_public_input(initial_a); + builder.register_public_input(initial_b); + builder.register_public_input(output_a); + builder.register_public_input(output_b); + + let mut pw = PartialWitness::new(); + + pw.set_target(initial_a, F::from_canonical_usize(look_val_a)); + pw.set_target(initial_b, F::from_canonical_usize(look_val_b)); + + let data = builder.build::(); + let mut timing = TimingTree::new("prove one lookup", Level::Debug); + let proof = prove(&data.prover_only, &data.common, pw, &mut timing)?; + timing.print(); + data.verify(proof.clone())?; + + assert!( + proof.public_inputs[2] == F::from_canonical_u16(out_a), + "First lookup, at index {} in the Tip5 table gives an incorrect output.", + proof.public_inputs[0] + ); + assert!( + proof.public_inputs[3] == F::from_canonical_u16(out_b), + "Second lookup, at index {} in the Tip5 table gives an incorrect output.", + proof.public_inputs[1] + ); + + Ok(()) +} + +// Tests one lookup in two different lookup tables. +#[test] +pub fn test_two_luts() -> anyhow::Result<()> { + use crate::field::types::Field; + use crate::iop::witness::{PartialWitness, WitnessWrite}; + use crate::plonk::circuit_builder::CircuitBuilder; + use crate::plonk::circuit_data::CircuitConfig; + use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; + + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + type F = >::F; + + LOGGER_INITIALIZED.call_once(|| init_logger().unwrap()); + let config = CircuitConfig::standard_recursion_config(); + let mut builder = CircuitBuilder::::new(config); + + let initial_a = builder.add_virtual_target(); + let initial_b = builder.add_virtual_target(); + + let look_val_a = 1; + let look_val_b = 2; + + let tip5_table = TIP5_TABLE.to_vec(); + + let first_out = tip5_table[look_val_a]; + let second_out = tip5_table[look_val_b]; + + let table: LookupTable = Arc::new((0..256).zip_eq(tip5_table).collect()); + + let other_table = OTHER_TABLE.to_vec(); + + let table_index = builder.add_lookup_table_from_pairs(table); + let output_a = builder.add_lookup_from_index(initial_a, table_index); + + let output_b = builder.add_lookup_from_index(initial_b, table_index); + let sum = builder.add(output_a, output_b); + + let s = first_out + second_out; + let final_out = other_table[s as usize]; + + let table2: LookupTable = Arc::new((0..256).zip_eq(other_table).collect()); + let table2_index = builder.add_lookup_table_from_pairs(table2); + + let output_final = builder.add_lookup_from_index(sum, table2_index); + + builder.register_public_input(initial_a); + builder.register_public_input(initial_b); + builder.register_public_input(sum); + builder.register_public_input(output_a); + builder.register_public_input(output_b); + builder.register_public_input(output_final); + + let mut pw = PartialWitness::new(); + pw.set_target(initial_a, F::from_canonical_usize(look_val_a)); + pw.set_target(initial_b, F::from_canonical_usize(look_val_b)); + let data = builder.build::(); + let mut timing = TimingTree::new("prove two_luts", Level::Debug); + let proof = prove(&data.prover_only, &data.common, pw, &mut timing)?; + data.verify(proof.clone())?; + timing.print(); + + assert!( + proof.public_inputs[3] == F::from_canonical_u16(first_out), + "First lookup, at index {} in the Tip5 table gives an incorrect output.", + proof.public_inputs[0] + ); + assert!( + proof.public_inputs[4] == F::from_canonical_u16(second_out), + "Second lookup, at index {} in the Tip5 table gives an incorrect output.", + proof.public_inputs[1] + ); + assert!( + proof.public_inputs[2] == F::from_canonical_u16(s), + "Sum between the first two LUT outputs is incorrect." + ); + assert!( + proof.public_inputs[5] == F::from_canonical_u16(final_out), + "Output of the second LUT at index {} is incorrect.", + s + ); + + Ok(()) +} + +#[test] +pub fn test_different_inputs() -> anyhow::Result<()> { + use crate::field::types::Field; + use crate::iop::witness::{PartialWitness, WitnessWrite}; + use crate::plonk::circuit_builder::CircuitBuilder; + use crate::plonk::circuit_data::CircuitConfig; + use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; + + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + type F = >::F; + LOGGER_INITIALIZED.call_once(|| init_logger().unwrap()); + let config = CircuitConfig::standard_recursion_config(); + let mut builder = CircuitBuilder::::new(config); + + let initial_a = builder.add_virtual_target(); + let initial_b = builder.add_virtual_target(); + + let init_a = 1; + let init_b = 2; + + let tab: Vec = SMALLER_TABLE.to_vec(); + let table: LookupTable = Arc::new((2..10).zip_eq(tab).collect()); + + let other_table = OTHER_TABLE.to_vec(); + + let table2: LookupTable = Arc::new((0..256).zip_eq(other_table).collect()); + let small_index = builder.add_lookup_table_from_pairs(table.clone()); + let output_a = builder.add_lookup_from_index(initial_a, small_index); + + let output_b = builder.add_lookup_from_index(initial_b, small_index); + let sum = builder.add(output_a, output_b); + + let other_index = builder.add_lookup_table_from_pairs(table2.clone()); + let output_final = builder.add_lookup_from_index(sum, other_index); + + builder.register_public_input(initial_a); + builder.register_public_input(initial_b); + builder.register_public_input(sum); + builder.register_public_input(output_a); + builder.register_public_input(output_b); + builder.register_public_input(output_final); + + let mut pw = PartialWitness::new(); + + let look_val_a = table[init_a].0; + let look_val_b = table[init_b].0; + pw.set_target(initial_a, F::from_canonical_u16(look_val_a)); + pw.set_target(initial_b, F::from_canonical_u16(look_val_b)); + + let data = builder.build::(); + let mut timing = TimingTree::new("prove different lookups", Level::Debug); + let proof = prove(&data.prover_only, &data.common, pw, &mut timing)?; + data.verify(proof.clone())?; + timing.print(); + + let out_a = table[init_a].1; + let out_b = table[init_b].1; + let s = out_a + out_b; + let out_final = table2[s as usize].1; + + assert!( + proof.public_inputs[3] == F::from_canonical_u16(out_a), + "First lookup, at index {} in the smaller LUT gives an incorrect output.", + proof.public_inputs[0] + ); + assert!( + proof.public_inputs[4] == F::from_canonical_u16(out_b), + "Second lookup, at index {} in the smaller LUT gives an incorrect output.", + proof.public_inputs[1] + ); + assert!( + proof.public_inputs[2] == F::from_canonical_u16(s), + "Sum between the first two LUT outputs is incorrect." + ); + assert!( + proof.public_inputs[5] == F::from_canonical_u16(out_final), + "Output of the second LUT at index {} is incorrect.", + s + ); + + Ok(()) +} + +// This test looks up over 514 values for one LookupTableGate, which means that several LookupGates are created. +#[test] +pub fn test_many_lookups() -> anyhow::Result<()> { + use crate::field::types::Field; + use crate::iop::witness::{PartialWitness, WitnessWrite}; + use crate::plonk::circuit_builder::CircuitBuilder; + use crate::plonk::circuit_data::CircuitConfig; + use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; + + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + type F = >::F; + LOGGER_INITIALIZED.call_once(|| init_logger().unwrap()); + let config = CircuitConfig::standard_recursion_config(); + let mut builder = CircuitBuilder::::new(config); + + let initial_a = builder.add_virtual_target(); + let initial_b = builder.add_virtual_target(); + + let look_val_a = 1; + let look_val_b = 2; + + let tip5_table = TIP5_TABLE.to_vec(); + let table: LookupTable = Arc::new((0..256).zip_eq(tip5_table).collect()); + + let out_a = table[look_val_a].1; + let out_b = table[look_val_b].1; + + let tip5_index = builder.add_lookup_table_from_pairs(table); + let output_a = builder.add_lookup_from_index(initial_a, tip5_index); + + let output_b = builder.add_lookup_from_index(initial_b, tip5_index); + let sum = builder.add(output_a, output_b); + + for _ in 0..514 { + builder.add_lookup_from_index(initial_a, tip5_index); + } + + let other_table = OTHER_TABLE.to_vec(); + + let table2: LookupTable = Arc::new((0..256).zip_eq(other_table).collect()); + + let s = out_a + out_b; + let out_final = table2[s as usize].1; + + let other_index = builder.add_lookup_table_from_pairs(table2); + let output_final = builder.add_lookup_from_index(sum, other_index); + + builder.register_public_input(initial_a); + builder.register_public_input(initial_b); + builder.register_public_input(sum); + builder.register_public_input(output_a); + builder.register_public_input(output_b); + builder.register_public_input(output_final); + + let mut pw = PartialWitness::new(); + + pw.set_target(initial_a, F::from_canonical_usize(look_val_a)); + pw.set_target(initial_b, F::from_canonical_usize(look_val_b)); + + let data = builder.build::(); + let mut timing = TimingTree::new("prove different lookups", Level::Debug); + let proof = prove(&data.prover_only, &data.common, pw, &mut timing)?; + + data.verify(proof.clone())?; + timing.print(); + + assert!( + proof.public_inputs[3] == F::from_canonical_u16(out_a), + "First lookup, at index {} in the Tip5 table gives an incorrect output.", + proof.public_inputs[0] + ); + assert!( + proof.public_inputs[4] == F::from_canonical_u16(out_b), + "Second lookup, at index {} in the Tip5 table gives an incorrect output.", + proof.public_inputs[1] + ); + assert!( + proof.public_inputs[2] == F::from_canonical_u16(s), + "Sum between the first two LUT outputs is incorrect." + ); + assert!( + proof.public_inputs[5] == F::from_canonical_u16(out_final), + "Output of the second LUT at index {} is incorrect.", + s + ); + + Ok(()) +} + +// Tests whether, when adding the same LUT to the circuit, the circuit only adds one copy, with the same index. +#[test] +pub fn test_same_luts() -> anyhow::Result<()> { + use crate::field::types::Field; + use crate::iop::witness::{PartialWitness, WitnessWrite}; + use crate::plonk::circuit_builder::CircuitBuilder; + use crate::plonk::circuit_data::CircuitConfig; + use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; + + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + type F = >::F; + + LOGGER_INITIALIZED.call_once(|| init_logger().unwrap()); + let config = CircuitConfig::standard_recursion_config(); + let mut builder = CircuitBuilder::::new(config); + + let initial_a = builder.add_virtual_target(); + let initial_b = builder.add_virtual_target(); + + let look_val_a = 1; + let look_val_b = 2; + + let tip5_table = TIP5_TABLE.to_vec(); + let table: LookupTable = Arc::new((0..256).zip_eq(tip5_table).collect()); + + let table_index = builder.add_lookup_table_from_pairs(table.clone()); + let output_a = builder.add_lookup_from_index(initial_a, table_index); + + let output_b = builder.add_lookup_from_index(initial_b, table_index); + let sum = builder.add(output_a, output_b); + + let table2_index = builder.add_lookup_table_from_pairs(table); + + let output_final = builder.add_lookup_from_index(sum, table2_index); + + builder.register_public_input(initial_a); + builder.register_public_input(initial_b); + builder.register_public_input(sum); + builder.register_public_input(output_a); + builder.register_public_input(output_b); + builder.register_public_input(output_final); + + let luts_length = builder.get_luts_length(); + + assert!( + luts_length == 1, + "There are {} LUTs when there should be only one", + luts_length + ); + + let mut pw = PartialWitness::new(); + + pw.set_target(initial_a, F::from_canonical_usize(look_val_a)); + pw.set_target(initial_b, F::from_canonical_usize(look_val_b)); + + let data = builder.build::(); + let mut timing = TimingTree::new("prove two_luts", Level::Debug); + let proof = prove(&data.prover_only, &data.common, pw, &mut timing)?; + data.verify(proof)?; + timing.print(); + + Ok(()) +} + +fn init_logger() -> anyhow::Result<()> { + let mut builder = env_logger::Builder::from_default_env(); + builder.format_timestamp(None); + builder.filter_level(LevelFilter::Debug); + + builder.try_init()?; + Ok(()) }