From 3c4f938f8568db81b72855fa6a811dba96f13bee Mon Sep 17 00:00:00 2001 From: Hamy Ratoanina Date: Mon, 11 Sep 2023 14:11:13 -0400 Subject: [PATCH 01/19] Make next row available to CTLs --- evm/src/all_stark.rs | 18 ++- evm/src/arithmetic/arithmetic_stark.rs | 1 + evm/src/cpu/cpu_stark.rs | 2 + evm/src/cross_table_lookup.rs | 161 +++++++++++++++---------- evm/src/fixed_recursive_verifier.rs | 2 +- evm/src/keccak/keccak_stark.rs | 3 +- evm/src/proof.rs | 37 +++--- evm/src/prover.rs | 9 +- evm/src/recursive_verifier.rs | 17 ++- evm/src/stark.rs | 15 +-- evm/src/verifier.rs | 13 +- 11 files changed, 162 insertions(+), 116 deletions(-) diff --git a/evm/src/all_stark.rs b/evm/src/all_stark.rs index b0f3056cf6..9b1fc49b00 100644 --- a/evm/src/all_stark.rs +++ b/evm/src/all_stark.rs @@ -120,11 +120,13 @@ fn ctl_keccak() -> CrossTableLookup { let keccak_sponge_looking = TableWithColumns::new( Table::KeccakSponge, keccak_sponge_stark::ctl_looking_keccak(), + vec![], Some(keccak_sponge_stark::ctl_looking_keccak_filter()), ); let keccak_looked = TableWithColumns::new( Table::Keccak, keccak_stark::ctl_data(), + vec![], Some(keccak_stark::ctl_filter()), ); CrossTableLookup::new(vec![keccak_sponge_looking], keccak_looked) @@ -134,11 +136,13 @@ fn ctl_keccak_sponge() -> CrossTableLookup { let cpu_looking = TableWithColumns::new( Table::Cpu, cpu_stark::ctl_data_keccak_sponge(), + vec![], Some(cpu_stark::ctl_filter_keccak_sponge()), ); let keccak_sponge_looked = TableWithColumns::new( Table::KeccakSponge, keccak_sponge_stark::ctl_looked_data(), + vec![], Some(keccak_sponge_stark::ctl_looked_filter()), ); CrossTableLookup::new(vec![cpu_looking], keccak_sponge_looked) @@ -148,6 +152,7 @@ fn ctl_logic() -> CrossTableLookup { let cpu_looking = TableWithColumns::new( Table::Cpu, cpu_stark::ctl_data_logic(), + vec![], Some(cpu_stark::ctl_filter_logic()), ); let mut all_lookers = vec![cpu_looking]; @@ -155,12 +160,17 @@ fn ctl_logic() -> CrossTableLookup { let keccak_sponge_looking = TableWithColumns::new( Table::KeccakSponge, keccak_sponge_stark::ctl_looking_logic(i), + vec![], Some(keccak_sponge_stark::ctl_looking_logic_filter()), ); all_lookers.push(keccak_sponge_looking); } - let logic_looked = - TableWithColumns::new(Table::Logic, logic::ctl_data(), Some(logic::ctl_filter())); + let logic_looked = TableWithColumns::new( + Table::Logic, + logic::ctl_data(), + vec![], + Some(logic::ctl_filter()), + ); CrossTableLookup::new(all_lookers, logic_looked) } @@ -168,12 +178,14 @@ fn ctl_memory() -> CrossTableLookup { let cpu_memory_code_read = TableWithColumns::new( Table::Cpu, cpu_stark::ctl_data_code_memory(), + vec![], Some(cpu_stark::ctl_filter_code_memory()), ); let cpu_memory_gp_ops = (0..NUM_GP_CHANNELS).map(|channel| { TableWithColumns::new( Table::Cpu, cpu_stark::ctl_data_gp_memory(channel), + vec![], Some(cpu_stark::ctl_filter_gp_memory(channel)), ) }); @@ -181,6 +193,7 @@ fn ctl_memory() -> CrossTableLookup { TableWithColumns::new( Table::KeccakSponge, keccak_sponge_stark::ctl_looking_memory(i), + vec![], Some(keccak_sponge_stark::ctl_looking_memory_filter(i)), ) }); @@ -191,6 +204,7 @@ fn ctl_memory() -> CrossTableLookup { let memory_looked = TableWithColumns::new( Table::Memory, memory_stark::ctl_data(), + vec![], Some(memory_stark::ctl_filter()), ); CrossTableLookup::new(all_lookers, memory_looked) diff --git a/evm/src/arithmetic/arithmetic_stark.rs b/evm/src/arithmetic/arithmetic_stark.rs index 4695798af5..accc914efe 100644 --- a/evm/src/arithmetic/arithmetic_stark.rs +++ b/evm/src/arithmetic/arithmetic_stark.rs @@ -81,6 +81,7 @@ pub fn ctl_arithmetic_rows() -> TableWithColumns { TableWithColumns::new( Table::Arithmetic, cpu_arith_data_link(&ARITH_OPS, ®ISTER_MAP), + vec![], Some(Column::sum(ARITH_OPS)), ) } diff --git a/evm/src/cpu/cpu_stark.rs b/evm/src/cpu/cpu_stark.rs index 7fd0c76fcc..f598f24d9f 100644 --- a/evm/src/cpu/cpu_stark.rs +++ b/evm/src/cpu/cpu_stark.rs @@ -118,6 +118,7 @@ pub fn ctl_arithmetic_base_rows() -> TableWithColumns { TableWithColumns::new( Table::Cpu, ctl_data_ternops(&OPS, false), + vec![], Some(Column::sum(OPS)), ) } @@ -149,6 +150,7 @@ pub fn ctl_arithmetic_shift_rows() -> TableWithColumns { TableWithColumns::new( Table::Cpu, ctl_data_ternops(&OPS, true), + vec![], Some(Column::sum([COL_MAP.op.shl, COL_MAP.op.shr])), ) } diff --git a/evm/src/cross_table_lookup.rs b/evm/src/cross_table_lookup.rs index 8f481325c6..ac9630a065 100644 --- a/evm/src/cross_table_lookup.rs +++ b/evm/src/cross_table_lookup.rs @@ -141,15 +141,22 @@ impl Column { #[derive(Clone, Debug)] pub struct TableWithColumns { table: Table, - columns: Vec>, + local_columns: Vec>, + next_columns: Vec>, pub(crate) filter_column: Option>, } impl TableWithColumns { - pub fn new(table: Table, columns: Vec>, filter_column: Option>) -> Self { + pub fn new( + table: Table, + local_columns: Vec>, + next_columns: Vec>, + filter_column: Option>, + ) -> Self { Self { table, - columns, + local_columns, + next_columns, filter_column, } } @@ -168,7 +175,8 @@ impl CrossTableLookup { ) -> Self { assert!(looking_tables .iter() - .all(|twc| twc.columns.len() == looked_table.columns.len())); + .all(|twc| (twc.local_columns.len() + twc.next_columns.len()) + == (looked_table.local_columns.len() + looked_table.next_columns.len()))); Self { looking_tables, looked_table, @@ -196,7 +204,8 @@ pub struct CtlData { pub(crate) struct CtlZData { pub(crate) z: PolynomialValues, pub(crate) challenge: GrandProductChallenge, - pub(crate) columns: Vec>, + pub(crate) local_columns: Vec>, + pub(crate) next_columns: Vec>, pub(crate) filter_column: Option>, } @@ -233,14 +242,16 @@ pub(crate) fn cross_table_lookup_data( let zs_looking = looking_tables.iter().map(|table| { partial_products( &trace_poly_values[table.table as usize], - &table.columns, + &table.local_columns, + &table.next_columns, &table.filter_column, challenge, ) }); let z_looked = partial_products( &trace_poly_values[looked_table.table as usize], - &looked_table.columns, + &looked_table.local_columns, + &looked_table.next_columns, &looked_table.filter_column, challenge, ); @@ -250,7 +261,8 @@ pub(crate) fn cross_table_lookup_data( .push(CtlZData { z, challenge, - columns: table.columns.clone(), + local_columns: table.local_columns.clone(), + next_columns: table.next_columns.clone(), filter_column: table.filter_column.clone(), }); } @@ -259,7 +271,8 @@ pub(crate) fn cross_table_lookup_data( .push(CtlZData { z: z_looked, challenge, - columns: looked_table.columns.clone(), + local_columns: looked_table.local_columns.clone(), + next_columns: looked_table.next_columns.clone(), filter_column: looked_table.filter_column.clone(), }); } @@ -269,23 +282,31 @@ pub(crate) fn cross_table_lookup_data( fn partial_products( trace: &[PolynomialValues], - columns: &[Column], + local_columns: &[Column], + next_columns: &[Column], filter_column: &Option>, challenge: GrandProductChallenge, ) -> PolynomialValues { 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 { F::ONE }; if filter.is_one() { - let evals = columns + let evals = local_columns .iter() .map(|c| c.eval_table(trace, i)) + .chain( + next_columns + .iter() + // The modulo is there to avoid out of bounds. For any CTL using next row + // values, we expect the filter to be 0 at the last row. + .map(|c| c.eval_table(trace, (i + 1) % degree)), + ) .collect::>(); partial_prod *= challenge.combine(evals.iter()); } else { @@ -293,6 +314,7 @@ fn partial_products( }; res.push(partial_prod); } + res.reverse(); res.into() } @@ -306,7 +328,8 @@ where pub(crate) local_z: P, pub(crate) next_z: P, pub(crate) challenges: GrandProductChallenge, - pub(crate) columns: &'a [Column], + pub(crate) local_columns: &'a [Column], + pub(crate) next_columns: &'a [Column], pub(crate) filter_column: &'a Option>, } @@ -343,7 +366,8 @@ impl<'a, F: RichField + Extendable, const D: usize> local_z: *looking_z, next_z: *looking_z_next, challenges, - columns: &table.columns, + local_columns: &table.local_columns, + next_columns: &table.next_columns, filter_column: &table.filter_column, }); } @@ -353,7 +377,8 @@ impl<'a, F: RichField + Extendable, const D: usize> local_z: *looked_z, next_z: *looked_z_next, challenges, - columns: &looked_table.columns, + local_columns: &looked_table.local_columns, + next_columns: &looked_table.next_columns, filter_column: &looked_table.filter_column, }); } @@ -362,6 +387,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], @@ -377,30 +406,28 @@ 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 mut evals = local_columns + .iter() + .map(|c| c.eval(vars.local_values)) + .collect::>(); + evals.extend(next_columns.iter().map(|c| c.eval(vars.next_values))); + let combined = challenges.combine(evals.iter()); + let local_filter = if let Some(column) = filter_column { + column.eval(vars.local_values) + } 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 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); } } @@ -409,7 +436,8 @@ pub struct CtlCheckVarsTarget<'a, F: Field, const D: usize> { pub(crate) local_z: ExtensionTarget, pub(crate) next_z: ExtensionTarget, pub(crate) challenges: GrandProductChallenge, - pub(crate) columns: &'a [Column], + pub(crate) local_columns: &'a [Column], + pub(crate) next_columns: &'a [Column], pub(crate) filter_column: &'a Option>, } @@ -445,7 +473,8 @@ impl<'a, F: Field, const D: usize> CtlCheckVarsTarget<'a, F, D> { local_z: *looking_z, next_z: *looking_z_next, challenges, - columns: &looking_table.columns, + local_columns: &looking_table.local_columns, + next_columns: &looking_table.next_columns, filter_column: &looking_table.filter_column, }); } @@ -457,7 +486,8 @@ impl<'a, F: Field, const D: usize> CtlCheckVarsTarget<'a, F, D> { local_z: *looked_z, next_z: *looked_z_next, challenges, - columns: &looked_table.columns, + local_columns: &looked_table.local_columns, + next_columns: &looked_table.next_columns, filter_column: &looked_table.filter_column, }); } @@ -483,7 +513,8 @@ pub(crate) fn eval_cross_table_lookup_checks_circuit< local_z, next_z, challenges, - columns, + local_columns, + next_columns, filter_column, } = lookup_vars; @@ -493,11 +524,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, @@ -508,34 +534,35 @@ 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 mut evals = local_columns .iter() .map(|c| c.eval_circuit(builder, vars.local_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); + evals.extend( + next_columns + .iter() + .map(|c| c.eval_circuit(builder, vars.next_values)), + ); + + 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 CrossTableLookup { looking_tables, looked_table, @@ -564,11 +591,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, @@ -661,9 +688,15 @@ pub(crate) mod testutils { }; if filter.is_one() { let row = table - .columns + .local_columns .iter() .map(|c| c.eval_table(trace, i)) + .chain( + table + .next_columns + .iter() + .map(|c| c.eval_table(trace, (i + 1) % trace[0].len())), + ) .collect::>(); multiset.entry(row).or_default().push((table.table, i)); } else { diff --git a/evm/src/fixed_recursive_verifier.rs b/evm/src/fixed_recursive_verifier.rs index 72844db69c..ce1c6f5840 100644 --- a/evm/src/fixed_recursive_verifier.rs +++ b/evm/src/fixed_recursive_verifier.rs @@ -510,7 +510,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, ); diff --git a/evm/src/keccak/keccak_stark.rs b/evm/src/keccak/keccak_stark.rs index 74f92622fc..28e4e939cb 100644 --- a/evm/src/keccak/keccak_stark.rs +++ b/evm/src/keccak/keccak_stark.rs @@ -763,7 +763,8 @@ mod tests { beta: F::ZERO, gamma: F::ZERO, }, - columns: vec![], + local_columns: vec![], + next_columns: vec![], filter_column: None, }; let ctl_data = CtlData { diff --git a/evm/src/proof.rs b/evm/src/proof.rs index 14f22b6791..76f3af32f0 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() } } @@ -704,8 +704,8 @@ pub struct StarkOpeningSet, const D: usize> { pub permutation_ctl_zs: Vec, /// Openings of permutations and cross-table lookups `Z` polynomials at `g * zeta`. pub permutation_ctl_zs_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, } @@ -717,7 +717,6 @@ impl, const D: usize> StarkOpeningSet { trace_commitment: &PolynomialBatch, permutation_ctl_zs_commitment: &PolynomialBatch, quotient_commitment: &PolynomialBatch, - degree_bits: usize, num_permutation_zs: usize, ) -> Self { let eval_commitment = |z: F::Extension, c: &PolynomialBatch| { @@ -738,10 +737,8 @@ impl, const D: usize> StarkOpeningSet { next_values: eval_commitment(zeta_next, trace_commitment), permutation_ctl_zs: eval_commitment(zeta, permutation_ctl_zs_commitment), permutation_ctl_zs_next: eval_commitment(zeta_next, permutation_ctl_zs_commitment), - ctl_zs_last: eval_commitment_base( - F::primitive_root_of_unity(degree_bits).inverse(), - permutation_ctl_zs_commitment, - )[num_permutation_zs..] + ctl_zs_first: eval_commitment_base(F::ONE, permutation_ctl_zs_commitment) + [num_permutation_zs..] .to_vec(), quotient_polys: eval_commitment(zeta, quotient_commitment), } @@ -765,10 +762,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) @@ -776,7 +773,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], } } } @@ -787,7 +784,7 @@ pub struct StarkOpeningSetTarget { pub next_values: Vec>, pub permutation_ctl_zs: Vec>, pub permutation_ctl_zs_next: Vec>, - pub ctl_zs_last: Vec, + pub ctl_zs_first: Vec, pub quotient_polys: Vec>, } @@ -797,7 +794,7 @@ impl StarkOpeningSetTarget { buffer.write_target_ext_vec(&self.next_values)?; buffer.write_target_ext_vec(&self.permutation_ctl_zs)?; buffer.write_target_ext_vec(&self.permutation_ctl_zs_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(()) } @@ -807,7 +804,7 @@ impl StarkOpeningSetTarget { let next_values = buffer.read_target_ext_vec::()?; let permutation_ctl_zs = buffer.read_target_ext_vec::()?; let permutation_ctl_zs_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 { @@ -815,7 +812,7 @@ impl StarkOpeningSetTarget { next_values, permutation_ctl_zs, permutation_ctl_zs_next, - ctl_zs_last, + ctl_zs_first, quotient_polys, }) } @@ -838,10 +835,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)) @@ -849,7 +846,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 31be89e701..989b1e22ad 100644 --- a/evm/src/prover.rs +++ b/evm/src/prover.rs @@ -433,7 +433,6 @@ where trace_commitment, &permutation_ctl_zs_commitment, "ient_commitment, - degree_bits, stark.num_permutation_batches(config), ); challenger.observe_openings(&openings.to_fri_openings()); @@ -448,7 +447,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, @@ -570,7 +569,8 @@ where next_z: permutation_ctl_zs_commitment.get_lde_values_packed(i_next_start, step) [num_permutation_zs + i], challenges: zs_columns.challenge, - columns: &zs_columns.columns, + local_columns: &zs_columns.local_columns, + next_columns: &zs_columns.next_columns, filter_column: &zs_columns.filter_column, }) .collect::>(); @@ -688,7 +688,8 @@ fn check_constraints<'a, F, C, S, const D: usize>( local_z: permutation_ctl_zs_subgroup_evals[i][num_permutation_zs + iii], next_z: permutation_ctl_zs_subgroup_evals[i_next][num_permutation_zs + iii], challenges: zs_columns.challenge, - columns: &zs_columns.columns, + local_columns: &zs_columns.local_columns, + next_columns: &zs_columns.next_columns, filter_column: &zs_columns.filter_column, }) .collect::>(); diff --git a/evm/src/recursive_verifier.rs b/evm/src/recursive_verifier.rs index e669f4ab35..8a9661bf0d 100644 --- a/evm/src/recursive_verifier.rs +++ b/evm/src/recursive_verifier.rs @@ -59,7 +59,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 +85,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 +150,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, )?; @@ -350,7 +350,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, @@ -414,7 +414,7 @@ fn verify_stark_proof_with_challenges_circuit< next_values, permutation_ctl_zs, permutation_ctl_zs_next, - ctl_zs_last, + ctl_zs_first, quotient_polys, } = &proof.openings; let vars = StarkEvaluationTargets { @@ -484,8 +484,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::( @@ -869,7 +868,7 @@ fn add_virtual_stark_opening_set, S: Stark, c .add_virtual_extension_targets(stark.num_permutation_batches(config) + num_ctl_zs), permutation_ctl_zs_next: builder .add_virtual_extension_targets(stark.num_permutation_batches(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), } diff --git a/evm/src/stark.rs b/evm/src/stark.rs index 72cee0ad60..73b51ada41 100644 --- a/evm/src/stark.rs +++ b/evm/src/stark.rs @@ -84,7 +84,6 @@ pub trait Stark, const D: usize>: Sync { &self, zeta: F::Extension, g: F, - degree_bits: usize, num_ctl_zs: usize, config: &StarkConfig, ) -> FriInstanceInfo { @@ -131,13 +130,13 @@ pub trait Stark, const D: usize>: Sync { point: zeta.scalar_mul(g), polynomials: [trace_info, permutation_ctl_zs_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, permutation_ctl_oracle, quotient_oracle], - batches: vec![zeta_batch, zeta_next_batch, ctl_last_batch], + batches: vec![zeta_batch, zeta_next_batch, ctl_first_batch], } } @@ -147,7 +146,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 { @@ -195,14 +193,13 @@ pub trait Stark, const D: usize>: Sync { point: zeta_next, polynomials: [trace_info, permutation_ctl_zs_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, permutation_ctl_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/verifier.rs b/evm/src/verifier.rs index 49225f1426..c54ceefcd9 100644 --- a/evm/src/verifier.rs +++ b/evm/src/verifier.rs @@ -128,7 +128,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, ) @@ -301,7 +303,7 @@ where next_values, permutation_ctl_zs, permutation_ctl_zs_next, - ctl_zs_last, + ctl_zs_first, quotient_polys, } = &proof.openings; let vars = StarkEvaluationVars { @@ -367,8 +369,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(), @@ -408,7 +409,7 @@ where next_values, permutation_ctl_zs, permutation_ctl_zs_next, - ctl_zs_last, + ctl_zs_first, quotient_polys, } = openings; @@ -425,7 +426,7 @@ where ensure!(next_values.len() == S::COLUMNS); ensure!(permutation_ctl_zs.len() == num_zs); ensure!(permutation_ctl_zs_next.len() == num_zs); - 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(()) From 5a1b05acfbd44eeb98897f6a17e8c9057b8885c9 Mon Sep 17 00:00:00 2001 From: Robin Salen Date: Tue, 12 Sep 2023 19:23:16 -0400 Subject: [PATCH 02/19] Remove risks of panic --- evm/src/cpu/kernel/interpreter.rs | 4 +- evm/src/cpu/kernel/tests/blake2_f.rs | 2 +- evm/src/fixed_recursive_verifier.rs | 67 ++++++---------------------- evm/src/generation/state.rs | 2 +- evm/src/generation/trie_extractor.rs | 2 +- evm/src/get_challenges.rs | 64 ++++++++++++-------------- evm/src/prover.rs | 3 +- evm/src/recursive_verifier.rs | 38 ++++++++-------- evm/src/util.rs | 25 +++++++++++ evm/src/verifier.rs | 4 +- evm/src/witness/errors.rs | 1 + evm/src/witness/mod.rs | 2 +- evm/src/witness/operation.rs | 6 +-- 13 files changed, 101 insertions(+), 119 deletions(-) diff --git a/evm/src/cpu/kernel/interpreter.rs b/evm/src/cpu/kernel/interpreter.rs index 6039c51505..c4deba99c5 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 1b465bed55..b12c9f32a6 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/fixed_recursive_verifier.rs b/evm/src/fixed_recursive_verifier.rs index 5c0f1f8084..0b19c16a02 100644 --- a/evm/src/fixed_recursive_verifier.rs +++ b/evm/src/fixed_recursive_verifier.rs @@ -47,9 +47,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; @@ -905,7 +904,8 @@ where &mut root_inputs, &self.root.public_values, &all_proof.public_values, - ); + ) + .map_err(|_| anyhow::Error::msg("Invalid conversion when setting public_values target."))?; let root_proof = self.root.circuit.prove(root_inputs)?; @@ -939,32 +939,13 @@ where &self.aggregation.circuit.verifier_only, ); - set_block_hashes_target( - &mut agg_inputs, - &self.aggregation.public_values.block_hashes, - &public_values.block_hashes, - ); - set_block_metadata_target( + set_public_value_targets( &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 target."))?; - 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)) } @@ -1022,32 +1003,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 target.") + })?; 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 c0c03e28d3..2b85821fe1 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 8311f692e5..a508a720f7 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 59be8439d8..8cd6fd810f 100644 --- a/evm/src/get_challenges.rs +++ b/evm/src/get_challenges.rs @@ -13,7 +13,8 @@ use crate::permutation::{ get_n_grand_product_challenge_sets_target, }; use crate::proof::*; -use crate::util::{h256_limbs, u256_limbs}; +use crate::util::{h256_limbs, u256_limbs, u256_lowest_limb, u256_lowest_word}; +use crate::witness::errors::ProgramError; fn observe_root, C: GenericConfig, const D: usize>( challenger: &mut Challenger, @@ -56,35 +57,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_lowest_limb(block_metadata.block_timestamp)?); + challenger.observe_element(u256_lowest_limb(block_metadata.block_number)?); + challenger.observe_element(u256_lowest_limb(block_metadata.block_difficulty)?); + challenger.observe_element(u256_lowest_limb(block_metadata.block_gaslimit)?); + challenger.observe_element(u256_lowest_limb(block_metadata.block_chain_id)?); + let basefee = u256_lowest_word(block_metadata.block_base_fee)?; + challenger.observe_element(basefee.0); + challenger.observe_element(basefee.1); + challenger.observe_element(u256_lowest_limb(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< @@ -115,17 +105,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_lowest_limb(extra_data.txn_number_before)?); + challenger.observe_element(u256_lowest_limb(extra_data.txn_number_after)?); + challenger.observe_element(u256_lowest_limb(extra_data.gas_used_before)?); + challenger.observe_element(u256_lowest_limb(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< @@ -181,12 +173,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< @@ -212,14 +204,14 @@ impl, C: GenericConfig, const D: usize> A &self, all_stark: &AllStark, config: &StarkConfig, - ) -> AllProofChallenges { + ) -> 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); @@ -227,7 +219,7 @@ impl, C: GenericConfig, const D: usize> A let num_permutation_zs = all_stark.nums_permutation_zs(config); let num_permutation_batch_sizes = all_stark.permutation_batch_sizes(); - AllProofChallenges { + Ok(AllProofChallenges { stark_challenges: core::array::from_fn(|i| { challenger.compact(); self.stark_proofs[i].proof.get_challenges( @@ -238,7 +230,7 @@ impl, C: GenericConfig, const D: usize> A ) }), ctl_challenges, - } + }) } #[allow(unused)] // TODO: should be used soon diff --git a/evm/src/prover.rs b/evm/src/prover.rs index 8f5878232b..425634943e 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!( diff --git a/evm/src/recursive_verifier.rs b/evm/src/recursive_verifier.rs index e669f4ab35..531669c03e 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_lowest_limb, u256_lowest_word}; 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< @@ -905,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, { @@ -923,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, @@ -934,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( @@ -994,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, { @@ -1005,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_lowest_limb(block_metadata.block_timestamp)?, ); witness.set_target( block_metadata_target.block_number, - F::from_canonical_u32(block_metadata.block_number.as_u32()), + u256_lowest_limb(block_metadata.block_number)?, ); witness.set_target( block_metadata_target.block_difficulty, - F::from_canonical_u32(block_metadata.block_difficulty.as_u32()), + u256_lowest_limb(block_metadata.block_difficulty)?, ); witness.set_target( block_metadata_target.block_gaslimit, - F::from_canonical_u32(block_metadata.block_gaslimit.as_u32()), + u256_lowest_limb(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_lowest_limb(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_lowest_word(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_lowest_limb(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/util.rs b/evm/src/util.rs index 5fa085dcf2..9a0cd87fd5 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 in case the integer is actually greater. +pub(crate) fn u256_lowest_limb(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 in case the integer is actually greater. +pub(crate) fn u256_lowest_word(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 1aa9db9782..297d9276a6 100644 --- a/evm/src/verifier.rs +++ b/evm/src/verifier.rs @@ -50,7 +50,9 @@ where let AllProofChallenges { stark_challenges, ctl_challenges, - } = all_proof.get_challenges(all_stark, config); + } = all_proof + .get_challenges(all_stark, config) + .map_err(|_| anyhow::Error::msg("Invalid sampling of proof challenges."))?; let nums_permutation_zs = all_stark.nums_permutation_zs(config); diff --git a/evm/src/witness/errors.rs b/evm/src/witness/errors.rs index 44693a3363..1ab99eae18 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 7d491e4e54..fbb88a719c 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 7d07576d30..b1339d0cee 100644 --- a/evm/src/witness/operation.rs +++ b/evm/src/witness/operation.rs @@ -139,7 +139,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); @@ -377,7 +377,7 @@ pub(crate) fn generate_push( Segment::Code, initial_offset + i, )) - .as_u32() as u8 + .low_u32() as u8 }) .collect_vec(); @@ -710,7 +710,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(); From 61a1c246db47e41d7a5ded70ea6c4b9f7be16854 Mon Sep 17 00:00:00 2001 From: Hamy Ratoanina Date: Wed, 13 Sep 2023 10:55:27 -0400 Subject: [PATCH 03/19] Fix CTLs --- evm/src/all_stark.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/evm/src/all_stark.rs b/evm/src/all_stark.rs index 62788a5755..f374781a2b 100644 --- a/evm/src/all_stark.rs +++ b/evm/src/all_stark.rs @@ -128,16 +128,19 @@ fn ctl_byte_packing() -> CrossTableLookup { let cpu_packing_looking = TableWithColumns::new( Table::Cpu, cpu_stark::ctl_data_byte_packing(), + vec![], Some(cpu_stark::ctl_filter_byte_packing()), ); let cpu_unpacking_looking = TableWithColumns::new( Table::Cpu, cpu_stark::ctl_data_byte_unpacking(), + vec![], Some(cpu_stark::ctl_filter_byte_unpacking()), ); let byte_packing_looked = TableWithColumns::new( Table::BytePacking, byte_packing_stark::ctl_looked_data(), + vec![], Some(byte_packing_stark::ctl_looked_filter()), ); CrossTableLookup::new( @@ -231,6 +234,7 @@ fn ctl_memory() -> CrossTableLookup { TableWithColumns::new( Table::BytePacking, byte_packing_stark::ctl_looking_memory(i), + vec![], Some(byte_packing_stark::ctl_looking_memory_filter(i)), ) }); From 5db6abf026b54549f6b26133bfde90cfc4fd2bb3 Mon Sep 17 00:00:00 2001 From: Robin Salen Date: Fri, 15 Sep 2023 11:12:14 -0400 Subject: [PATCH 04/19] Update clippy in CI --- .github/workflows/continuous-integration-workflow.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/continuous-integration-workflow.yml b/.github/workflows/continuous-integration-workflow.yml index ba2ad1bd9b..a0ac3ec727 100644 --- a/.github/workflows/continuous-integration-workflow.yml +++ b/.github/workflows/continuous-integration-workflow.yml @@ -124,5 +124,5 @@ jobs: command: clippy args: --all-features --all-targets -- -D warnings -A incomplete-features env: - CARGO_INCREMENTAL: 1 - + # Seems necessary until https://github.com/rust-lang/rust/pull/115819 is merged. + CARGO_INCREMENTAL: 0 From 8903aec129715a0b83eafc005d1eb18f313d1abb Mon Sep 17 00:00:00 2001 From: Robin Salen <30937548+Nashtare@users.noreply.github.com> Date: Fri, 15 Sep 2023 17:46:58 -0400 Subject: [PATCH 05/19] Change padding rule for CPU (#1234) * Change padding rule for CPU * Disable memory channels for padding rows * Apply some of Jacqueline's comments * Update halt routine * Add clarifying comment * Redundant constraints and padding bug * Revert "Remove is_bootstrap_kernel column" This reverts commit 49d92cb8f1b0ae9de76872f76af4429699ff692f. * Make halt_state implicit * Move halting logic constraints to dedicated module * Include new module * Update some comments --- evm/src/cpu/bootstrap_kernel.rs | 12 ++-- evm/src/cpu/columns/mod.rs | 3 + evm/src/cpu/control_flow.rs | 72 ++++++++---------------- evm/src/cpu/cpu_stark.rs | 3 + evm/src/cpu/halt.rs | 98 +++++++++++++++++++++++++++++++++ evm/src/cpu/kernel/asm/halt.asm | 6 +- evm/src/cpu/mod.rs | 1 + evm/src/generation/mod.rs | 37 ++++++++----- evm/src/witness/transition.rs | 2 +- 9 files changed, 161 insertions(+), 73 deletions(-) create mode 100644 evm/src/cpu/halt.rs diff --git a/evm/src/cpu/bootstrap_kernel.rs b/evm/src/cpu/bootstrap_kernel.rs index 66f88d3ae1..4aee617c53 100644 --- a/evm/src/cpu/bootstrap_kernel.rs +++ b/evm/src/cpu/bootstrap_kernel.rs @@ -25,6 +25,7 @@ pub(crate) fn generate_bootstrap_kernel(state: &mut GenerationState for chunk in &KERNEL.code.iter().enumerate().chunks(NUM_GP_CHANNELS) { let mut cpu_row = CpuColumnsView::default(); cpu_row.clock = F::from_canonical_usize(state.traces.clock()); + cpu_row.is_bootstrap_kernel = F::ONE; // Write this chunk to memory, while simultaneously packing its bytes into a u32 word. for (channel, (addr, &byte)) in chunk.enumerate() { @@ -39,6 +40,7 @@ pub(crate) fn generate_bootstrap_kernel(state: &mut GenerationState let mut final_cpu_row = CpuColumnsView::default(); final_cpu_row.clock = F::from_canonical_usize(state.traces.clock()); + final_cpu_row.is_bootstrap_kernel = F::ONE; final_cpu_row.is_keccak_sponge = F::ONE; // The Keccak sponge CTL uses memory value columns for its inputs and outputs. final_cpu_row.mem_channels[0].value[0] = F::ZERO; // context @@ -64,8 +66,8 @@ pub(crate) fn eval_bootstrap_kernel>( let next_values: &CpuColumnsView<_> = vars.next_values.borrow(); // IS_BOOTSTRAP_KERNEL must have an init value of 1, a final value of 0, and a delta in {0, -1}. - let local_is_bootstrap = P::ONES - local_values.op.into_iter().sum::

(); - let next_is_bootstrap = P::ONES - next_values.op.into_iter().sum::

(); + let local_is_bootstrap = local_values.is_bootstrap_kernel; + let next_is_bootstrap = next_values.is_bootstrap_kernel; yield_constr.constraint_first_row(local_is_bootstrap - P::ONES); yield_constr.constraint_last_row(local_is_bootstrap); let delta_is_bootstrap = next_is_bootstrap - local_is_bootstrap; @@ -111,10 +113,8 @@ pub(crate) fn eval_bootstrap_kernel_circuit, const let one = builder.one_extension(); // IS_BOOTSTRAP_KERNEL must have an init value of 1, a final value of 0, and a delta in {0, -1}. - let local_is_bootstrap = builder.add_many_extension(local_values.op.iter()); - let local_is_bootstrap = builder.sub_extension(one, local_is_bootstrap); - let next_is_bootstrap = builder.add_many_extension(next_values.op.iter()); - let next_is_bootstrap = builder.sub_extension(one, next_is_bootstrap); + let local_is_bootstrap = local_values.is_bootstrap_kernel; + let next_is_bootstrap = next_values.is_bootstrap_kernel; let constraint = builder.sub_extension(local_is_bootstrap, one); yield_constr.constraint_first_row(builder, constraint); yield_constr.constraint_last_row(builder, local_is_bootstrap); diff --git a/evm/src/cpu/columns/mod.rs b/evm/src/cpu/columns/mod.rs index 134ab02b49..fecc8df986 100644 --- a/evm/src/cpu/columns/mod.rs +++ b/evm/src/cpu/columns/mod.rs @@ -35,6 +35,9 @@ pub struct MemoryChannelView { #[repr(C)] #[derive(Clone, Copy, Eq, PartialEq, Debug)] pub struct CpuColumnsView { + /// Filter. 1 if the row is part of bootstrapping the kernel code, 0 otherwise. + pub is_bootstrap_kernel: T, + /// If CPU cycle: Current context. // TODO: this is currently unconstrained pub context: T, diff --git a/evm/src/cpu/control_flow.rs b/evm/src/cpu/control_flow.rs index eeabf6f08e..9c17367aa2 100644 --- a/evm/src/cpu/control_flow.rs +++ b/evm/src/cpu/control_flow.rs @@ -34,14 +34,9 @@ const NATIVE_INSTRUCTIONS: [usize; 17] = [ // not exceptions (also jump) ]; -pub(crate) fn get_halt_pcs() -> (F, F) { - let halt_pc0 = KERNEL.global_labels["halt_pc0"]; - let halt_pc1 = KERNEL.global_labels["halt_pc1"]; - - ( - F::from_canonical_usize(halt_pc0), - F::from_canonical_usize(halt_pc1), - ) +pub(crate) fn get_halt_pc() -> F { + let halt_pc = KERNEL.global_labels["halt"]; + F::from_canonical_usize(halt_pc) } pub(crate) fn get_start_pc() -> F { @@ -57,8 +52,15 @@ pub fn eval_packed_generic( ) { let is_cpu_cycle: P = COL_MAP.op.iter().map(|&col_i| lv[col_i]).sum(); let is_cpu_cycle_next: P = COL_MAP.op.iter().map(|&col_i| nv[col_i]).sum(); - // Once we start executing instructions, then we continue until the end of the table. - yield_constr.constraint_transition(is_cpu_cycle * (is_cpu_cycle_next - P::ONES)); + + let next_halt_state = P::ONES - nv.is_bootstrap_kernel - is_cpu_cycle_next; + + // Once we start executing instructions, then we continue until the end of the table + // or we reach dummy padding rows. This, along with the constraints on the first row, + // enforces that operation flags and the halt flag are mutually exclusive over the entire + // CPU trace. + yield_constr + .constraint_transition(is_cpu_cycle * (is_cpu_cycle_next + next_halt_state - P::ONES)); // If a row is a CPU cycle and executing a native instruction (implemented as a table row; not // microcoded) then the program counter is incremented by 1 to obtain the next row's program @@ -79,16 +81,6 @@ pub fn eval_packed_generic( yield_constr.constraint_transition(is_last_noncpu_cycle * pc_diff); yield_constr.constraint_transition(is_last_noncpu_cycle * (nv.is_kernel_mode - P::ONES)); yield_constr.constraint_transition(is_last_noncpu_cycle * nv.stack_len); - - // The last row must be a CPU cycle row. - yield_constr.constraint_last_row(is_cpu_cycle - P::ONES); - // Also, the last row's `program_counter` must be inside the `halt` infinite loop. Note that - // that loop consists of two instructions, so we must check for `halt` and `halt_inner` labels. - let (halt_pc0, halt_pc1) = get_halt_pcs::(); - yield_constr - .constraint_last_row((lv.program_counter - halt_pc0) * (lv.program_counter - halt_pc1)); - // Finally, the last row must be in kernel mode. - yield_constr.constraint_last_row(lv.is_kernel_mode - P::ONES); } pub fn eval_ext_circuit, const D: usize>( @@ -97,11 +89,21 @@ pub fn eval_ext_circuit, const D: usize>( nv: &CpuColumnsView>, yield_constr: &mut RecursiveConstraintConsumer, ) { + let one = builder.one_extension(); + let is_cpu_cycle = builder.add_many_extension(COL_MAP.op.iter().map(|&col_i| lv[col_i])); let is_cpu_cycle_next = builder.add_many_extension(COL_MAP.op.iter().map(|&col_i| nv[col_i])); - // Once we start executing instructions, then we continue until the end of the table. + + let next_halt_state = builder.add_extension(nv.is_bootstrap_kernel, is_cpu_cycle_next); + let next_halt_state = builder.sub_extension(one, next_halt_state); + + // Once we start executing instructions, then we continue until the end of the table + // or we reach dummy padding rows. This, along with the constraints on the first row, + // enforces that operation flags and the halt flag are mutually exclusive over the entire + // CPU trace. { - let constr = builder.mul_sub_extension(is_cpu_cycle, is_cpu_cycle_next, is_cpu_cycle); + let constr = builder.add_extension(is_cpu_cycle_next, next_halt_state); + let constr = builder.mul_sub_extension(is_cpu_cycle, constr, is_cpu_cycle); yield_constr.constraint_transition(builder, constr); } @@ -144,30 +146,4 @@ pub fn eval_ext_circuit, const D: usize>( let kernel_constr = builder.mul_extension(is_last_noncpu_cycle, nv.stack_len); yield_constr.constraint_transition(builder, kernel_constr); } - - // The last row must be a CPU cycle row. - { - let one = builder.one_extension(); - let constr = builder.sub_extension(is_cpu_cycle, one); - yield_constr.constraint_last_row(builder, constr); - } - // Also, the last row's `program_counter` must be inside the `halt` infinite loop. Note that - // that loop consists of two instructions, so we must check for `halt` and `halt_inner` labels. - { - let (halt_pc0, halt_pc1) = get_halt_pcs(); - let halt_pc0_target = builder.constant_extension(halt_pc0); - let halt_pc1_target = builder.constant_extension(halt_pc1); - - let halt_pc0_offset = builder.sub_extension(lv.program_counter, halt_pc0_target); - let halt_pc1_offset = builder.sub_extension(lv.program_counter, halt_pc1_target); - let constr = builder.mul_extension(halt_pc0_offset, halt_pc1_offset); - - yield_constr.constraint_last_row(builder, constr); - } - // Finally, the last row must be in kernel mode. - { - let one = builder.one_extension(); - let constr = builder.sub_extension(lv.is_kernel_mode, one); - yield_constr.constraint_last_row(builder, constr); - } } diff --git a/evm/src/cpu/cpu_stark.rs b/evm/src/cpu/cpu_stark.rs index 2d2e9072f5..bd2fcf193b 100644 --- a/evm/src/cpu/cpu_stark.rs +++ b/evm/src/cpu/cpu_stark.rs @@ -8,6 +8,7 @@ use plonky2::field::packed::PackedField; use plonky2::field::types::Field; use plonky2::hash::hash_types::RichField; +use super::halt; use crate::all_stark::Table; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::cpu::columns::{CpuColumnsView, COL_MAP, NUM_CPU_COLUMNS}; @@ -244,6 +245,7 @@ impl, const D: usize> Stark for CpuStark, const D: usize> Stark for CpuStark( + lv: &CpuColumnsView

, + nv: &CpuColumnsView

, + yield_constr: &mut ConstraintConsumer

, +) { + let is_cpu_cycle: P = COL_MAP.op.iter().map(|&col_i| lv[col_i]).sum(); + let is_cpu_cycle_next: P = COL_MAP.op.iter().map(|&col_i| nv[col_i]).sum(); + + let halt_state = P::ONES - lv.is_bootstrap_kernel - is_cpu_cycle; + let next_halt_state = P::ONES - nv.is_bootstrap_kernel - is_cpu_cycle_next; + + // The halt flag must be boolean. + yield_constr.constraint(halt_state * (halt_state - P::ONES)); + // Once we reach a padding row, there must be only padding rows. + yield_constr.constraint_transition(halt_state * (next_halt_state - P::ONES)); + + // Padding rows should have their memory channels disabled. + for i in 0..NUM_GP_CHANNELS { + let channel = lv.mem_channels[i]; + yield_constr.constraint(halt_state * channel.used); + } + + // The last row must be a dummy padding row. + yield_constr.constraint_last_row(halt_state - P::ONES); + + // Also, a padding row's `program_counter` must be at the `halt` label. + // In particular, it ensures that the first padding row may only be added + // after we jumped to the `halt` function. Subsequent padding rows may set + // the `program_counter` to arbitrary values (there's no transition + // constraints) so we can place this requirement on them too. + let halt_pc = get_halt_pc::(); + yield_constr.constraint(halt_state * (lv.program_counter - halt_pc)); +} + +pub fn eval_ext_circuit, const D: usize>( + builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder, + lv: &CpuColumnsView>, + nv: &CpuColumnsView>, + yield_constr: &mut RecursiveConstraintConsumer, +) { + let one = builder.one_extension(); + + let is_cpu_cycle = builder.add_many_extension(COL_MAP.op.iter().map(|&col_i| lv[col_i])); + let is_cpu_cycle_next = builder.add_many_extension(COL_MAP.op.iter().map(|&col_i| nv[col_i])); + + let halt_state = builder.add_extension(lv.is_bootstrap_kernel, is_cpu_cycle); + let halt_state = builder.sub_extension(one, halt_state); + let next_halt_state = builder.add_extension(nv.is_bootstrap_kernel, is_cpu_cycle_next); + let next_halt_state = builder.sub_extension(one, next_halt_state); + + // The halt flag must be boolean. + let constr = builder.mul_sub_extension(halt_state, halt_state, halt_state); + yield_constr.constraint(builder, constr); + // Once we reach a padding row, there must be only padding rows. + let constr = builder.mul_sub_extension(halt_state, next_halt_state, halt_state); + yield_constr.constraint_transition(builder, constr); + + // Padding rows should have their memory channels disabled. + for i in 0..NUM_GP_CHANNELS { + let channel = lv.mem_channels[i]; + let constr = builder.mul_extension(halt_state, channel.used); + yield_constr.constraint(builder, constr); + } + + // The last row must be a dummy padding row. + { + let one = builder.one_extension(); + let constr = builder.sub_extension(halt_state, one); + yield_constr.constraint_last_row(builder, constr); + } + + // Also, a padding row's `program_counter` must be at the `halt` label. + // In particular, it ensures that the first padding row may only be added + // after we jumped to the `halt` function. Subsequent padding rows may set + // the `program_counter` to arbitrary values (there's no transition + // constraints) so we can place this requirement on them too. + { + let halt_pc = get_halt_pc(); + let halt_pc_target = builder.constant_extension(halt_pc); + let constr = builder.sub_extension(lv.program_counter, halt_pc_target); + let constr = builder.mul_extension(halt_state, constr); + + yield_constr.constraint(builder, constr); + } +} diff --git a/evm/src/cpu/kernel/asm/halt.asm b/evm/src/cpu/kernel/asm/halt.asm index 906ce51aaa..49561fd660 100644 --- a/evm/src/cpu/kernel/asm/halt.asm +++ b/evm/src/cpu/kernel/asm/halt.asm @@ -1,6 +1,2 @@ global halt: - PUSH halt_pc0 -global halt_pc0: - DUP1 -global halt_pc1: - JUMP + PANIC diff --git a/evm/src/cpu/mod.rs b/evm/src/cpu/mod.rs index 91b04cf487..b7312147b4 100644 --- a/evm/src/cpu/mod.rs +++ b/evm/src/cpu/mod.rs @@ -6,6 +6,7 @@ pub mod cpu_stark; pub(crate) mod decode; mod dup_swap; mod gas; +mod halt; mod jumps; pub mod kernel; pub(crate) mod membus; diff --git a/evm/src/generation/mod.rs b/evm/src/generation/mod.rs index 13c6670ba6..35078e0784 100644 --- a/evm/src/generation/mod.rs +++ b/evm/src/generation/mod.rs @@ -16,6 +16,7 @@ use GlobalMetadata::{ use crate::all_stark::{AllStark, NUM_TABLES}; use crate::config::StarkConfig; use crate::cpu::bootstrap_kernel::generate_bootstrap_kernel; +use crate::cpu::columns::CpuColumnsView; use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; use crate::generation::outputs::{get_outputs, GenerationOutputs}; @@ -278,26 +279,36 @@ pub fn generate_traces, const D: usize>( fn simulate_cpu, const D: usize>( state: &mut GenerationState, ) -> anyhow::Result<()> { - let halt_pc0 = KERNEL.global_labels["halt_pc0"]; - let halt_pc1 = KERNEL.global_labels["halt_pc1"]; + let halt_pc = KERNEL.global_labels["halt"]; - let mut already_in_halt_loop = false; loop { // If we've reached the kernel's halt routine, and our trace length is a power of 2, stop. let pc = state.registers.program_counter; - let in_halt_loop = state.registers.is_kernel && (pc == halt_pc0 || pc == halt_pc1); - if in_halt_loop && !already_in_halt_loop { + let halt = state.registers.is_kernel && pc == halt_pc; + if halt { log::info!("CPU halted after {} cycles", state.traces.clock()); - } - already_in_halt_loop |= in_halt_loop; - - transition(state)?; - if already_in_halt_loop && state.traces.clock().is_power_of_two() { + // Padding + let mut row = CpuColumnsView::::default(); + row.clock = F::from_canonical_usize(state.traces.clock()); + row.context = F::from_canonical_usize(state.registers.context); + row.program_counter = F::from_canonical_usize(pc); + row.is_kernel_mode = F::ONE; + row.gas = F::from_canonical_u64(state.registers.gas_used); + row.stack_len = F::from_canonical_usize(state.registers.stack_len); + + loop { + state.traces.push_cpu(row); + row.clock += F::ONE; + if state.traces.clock().is_power_of_two() { + break; + } + } log::info!("CPU trace padded to {} cycles", state.traces.clock()); - break; + + return Ok(()); } - } - Ok(()) + transition(state)?; + } } diff --git a/evm/src/witness/transition.rs b/evm/src/witness/transition.rs index e3e7b5840f..1418beba8d 100644 --- a/evm/src/witness/transition.rs +++ b/evm/src/witness/transition.rs @@ -286,7 +286,7 @@ fn log_kernel_instruction(state: &GenerationState, op: Operation) { let pc = state.registers.program_counter; let is_interesting_offset = KERNEL .offset_label(pc) - .filter(|label| !label.starts_with("halt_pc")) + .filter(|label| !label.starts_with("halt")) .is_some(); let level = if is_interesting_offset { log::Level::Debug From 1a4caaa08f587ce1e92e01b5ffa66ca79861d01a Mon Sep 17 00:00:00 2001 From: Hamy Ratoanina Date: Fri, 15 Sep 2023 18:14:07 -0400 Subject: [PATCH 06/19] Move next row logic inside Column Co-authored-by: Nicholas Ward --- evm/src/all_stark.rs | 22 +-- evm/src/arithmetic/arithmetic_stark.rs | 1 - evm/src/cpu/cpu_stark.rs | 8 +- evm/src/cross_table_lookup.rs | 198 ++++++++++++++++--------- evm/src/keccak/keccak_stark.rs | 3 +- evm/src/prover.rs | 6 +- 6 files changed, 137 insertions(+), 101 deletions(-) diff --git a/evm/src/all_stark.rs b/evm/src/all_stark.rs index f374781a2b..068b0bcbf9 100644 --- a/evm/src/all_stark.rs +++ b/evm/src/all_stark.rs @@ -128,19 +128,16 @@ fn ctl_byte_packing() -> CrossTableLookup { let cpu_packing_looking = TableWithColumns::new( Table::Cpu, cpu_stark::ctl_data_byte_packing(), - vec![], Some(cpu_stark::ctl_filter_byte_packing()), ); let cpu_unpacking_looking = TableWithColumns::new( Table::Cpu, cpu_stark::ctl_data_byte_unpacking(), - vec![], Some(cpu_stark::ctl_filter_byte_unpacking()), ); let byte_packing_looked = TableWithColumns::new( Table::BytePacking, byte_packing_stark::ctl_looked_data(), - vec![], Some(byte_packing_stark::ctl_looked_filter()), ); CrossTableLookup::new( @@ -153,13 +150,11 @@ fn ctl_keccak() -> CrossTableLookup { let keccak_sponge_looking = TableWithColumns::new( Table::KeccakSponge, keccak_sponge_stark::ctl_looking_keccak(), - vec![], Some(keccak_sponge_stark::ctl_looking_keccak_filter()), ); let keccak_looked = TableWithColumns::new( Table::Keccak, keccak_stark::ctl_data(), - vec![], Some(keccak_stark::ctl_filter()), ); CrossTableLookup::new(vec![keccak_sponge_looking], keccak_looked) @@ -169,13 +164,11 @@ fn ctl_keccak_sponge() -> CrossTableLookup { let cpu_looking = TableWithColumns::new( Table::Cpu, cpu_stark::ctl_data_keccak_sponge(), - vec![], Some(cpu_stark::ctl_filter_keccak_sponge()), ); let keccak_sponge_looked = TableWithColumns::new( Table::KeccakSponge, keccak_sponge_stark::ctl_looked_data(), - vec![], Some(keccak_sponge_stark::ctl_looked_filter()), ); CrossTableLookup::new(vec![cpu_looking], keccak_sponge_looked) @@ -185,7 +178,6 @@ fn ctl_logic() -> CrossTableLookup { let cpu_looking = TableWithColumns::new( Table::Cpu, cpu_stark::ctl_data_logic(), - vec![], Some(cpu_stark::ctl_filter_logic()), ); let mut all_lookers = vec![cpu_looking]; @@ -193,17 +185,12 @@ fn ctl_logic() -> CrossTableLookup { let keccak_sponge_looking = TableWithColumns::new( Table::KeccakSponge, keccak_sponge_stark::ctl_looking_logic(i), - vec![], Some(keccak_sponge_stark::ctl_looking_logic_filter()), ); all_lookers.push(keccak_sponge_looking); } - let logic_looked = TableWithColumns::new( - Table::Logic, - logic::ctl_data(), - vec![], - Some(logic::ctl_filter()), - ); + let logic_looked = + TableWithColumns::new(Table::Logic, logic::ctl_data(), Some(logic::ctl_filter())); CrossTableLookup::new(all_lookers, logic_looked) } @@ -211,14 +198,12 @@ fn ctl_memory() -> CrossTableLookup { let cpu_memory_code_read = TableWithColumns::new( Table::Cpu, cpu_stark::ctl_data_code_memory(), - vec![], Some(cpu_stark::ctl_filter_code_memory()), ); let cpu_memory_gp_ops = (0..NUM_GP_CHANNELS).map(|channel| { TableWithColumns::new( Table::Cpu, cpu_stark::ctl_data_gp_memory(channel), - vec![], Some(cpu_stark::ctl_filter_gp_memory(channel)), ) }); @@ -226,7 +211,6 @@ fn ctl_memory() -> CrossTableLookup { TableWithColumns::new( Table::KeccakSponge, keccak_sponge_stark::ctl_looking_memory(i), - vec![], Some(keccak_sponge_stark::ctl_looking_memory_filter(i)), ) }); @@ -234,7 +218,6 @@ fn ctl_memory() -> CrossTableLookup { TableWithColumns::new( Table::BytePacking, byte_packing_stark::ctl_looking_memory(i), - vec![], Some(byte_packing_stark::ctl_looking_memory_filter(i)), ) }); @@ -246,7 +229,6 @@ fn ctl_memory() -> CrossTableLookup { let memory_looked = TableWithColumns::new( Table::Memory, memory_stark::ctl_data(), - vec![], Some(memory_stark::ctl_filter()), ); CrossTableLookup::new(all_lookers, memory_looked) diff --git a/evm/src/arithmetic/arithmetic_stark.rs b/evm/src/arithmetic/arithmetic_stark.rs index 034620a403..5441cf2760 100644 --- a/evm/src/arithmetic/arithmetic_stark.rs +++ b/evm/src/arithmetic/arithmetic_stark.rs @@ -96,7 +96,6 @@ pub fn ctl_arithmetic_rows() -> TableWithColumns { TableWithColumns::new( Table::Arithmetic, cpu_arith_data_link(&COMBINED_OPS, ®ISTER_MAP), - vec![], filter_column, ) } diff --git a/evm/src/cpu/cpu_stark.rs b/evm/src/cpu/cpu_stark.rs index 6623b67e2a..1579b3a1c3 100644 --- a/evm/src/cpu/cpu_stark.rs +++ b/evm/src/cpu/cpu_stark.rs @@ -103,7 +103,6 @@ pub fn ctl_arithmetic_base_rows() -> TableWithColumns { TableWithColumns::new( Table::Cpu, columns, - vec![], Some(Column::sum([ COL_MAP.op.binary_op, COL_MAP.op.fp254_op, @@ -121,12 +120,7 @@ pub fn ctl_arithmetic_shift_rows() -> TableWithColumns { // (also `ops` is used as the operation filter). The list of // operations includes binary operations which will simply ignore // the third input. - TableWithColumns::new( - Table::Cpu, - columns, - vec![], - Some(Column::single(COL_MAP.op.shift)), - ) + TableWithColumns::new(Table::Cpu, columns, Some(Column::single(COL_MAP.op.shift))) } pub fn ctl_data_byte_packing() -> Vec> { diff --git a/evm/src/cross_table_lookup.rs b/evm/src/cross_table_lookup.rs index ecc6a34cd7..315bf42f7b 100644 --- a/evm/src/cross_table_lookup.rs +++ b/evm/src/cross_table_lookup.rs @@ -25,6 +25,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, } @@ -32,6 +33,7 @@ impl Column { pub fn single(c: usize) -> Self { Self { linear_combination: vec![(c, F::ONE)], + next_row_linear_combination: vec![], constant: F::ZERO, } } @@ -42,9 +44,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, } } @@ -70,6 +87,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, } } @@ -106,13 +151,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( @@ -136,27 +211,50 @@ 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)] pub struct TableWithColumns { table: Table, - local_columns: Vec>, - next_columns: Vec>, + columns: Vec>, pub(crate) filter_column: Option>, } impl TableWithColumns { - pub fn new( - table: Table, - local_columns: Vec>, - next_columns: Vec>, - filter_column: Option>, - ) -> Self { + pub fn new(table: Table, columns: Vec>, filter_column: Option>) -> Self { Self { table, - local_columns, - next_columns, + columns, filter_column, } } @@ -175,8 +273,7 @@ impl CrossTableLookup { ) -> Self { assert!(looking_tables .iter() - .all(|twc| (twc.local_columns.len() + twc.next_columns.len()) - == (looked_table.local_columns.len() + looked_table.next_columns.len()))); + .all(|twc| twc.columns.len() == looked_table.columns.len())); Self { looking_tables, looked_table, @@ -204,8 +301,7 @@ pub struct CtlData { pub(crate) struct CtlZData { pub(crate) z: PolynomialValues, pub(crate) challenge: GrandProductChallenge, - pub(crate) local_columns: Vec>, - pub(crate) next_columns: Vec>, + pub(crate) columns: Vec>, pub(crate) filter_column: Option>, } @@ -242,16 +338,14 @@ pub(crate) fn cross_table_lookup_data( let zs_looking = looking_tables.iter().map(|table| { partial_products( &trace_poly_values[table.table as usize], - &table.local_columns, - &table.next_columns, + &table.columns, &table.filter_column, challenge, ) }); let z_looked = partial_products( &trace_poly_values[looked_table.table as usize], - &looked_table.local_columns, - &looked_table.next_columns, + &looked_table.columns, &looked_table.filter_column, challenge, ); @@ -261,8 +355,7 @@ pub(crate) fn cross_table_lookup_data( .push(CtlZData { z, challenge, - local_columns: table.local_columns.clone(), - next_columns: table.next_columns.clone(), + columns: table.columns.clone(), filter_column: table.filter_column.clone(), }); } @@ -271,8 +364,7 @@ pub(crate) fn cross_table_lookup_data( .push(CtlZData { z: z_looked, challenge, - local_columns: looked_table.local_columns.clone(), - next_columns: looked_table.next_columns.clone(), + columns: looked_table.columns.clone(), filter_column: looked_table.filter_column.clone(), }); } @@ -282,8 +374,7 @@ pub(crate) fn cross_table_lookup_data( fn partial_products( trace: &[PolynomialValues], - local_columns: &[Column], - next_columns: &[Column], + columns: &[Column], filter_column: &Option>, challenge: GrandProductChallenge, ) -> PolynomialValues { @@ -297,16 +388,9 @@ fn partial_products( F::ONE }; if filter.is_one() { - let evals = local_columns + let evals = columns .iter() .map(|c| c.eval_table(trace, i)) - .chain( - next_columns - .iter() - // The modulo is there to avoid out of bounds. For any CTL using next row - // values, we expect the filter to be 0 at the last row. - .map(|c| c.eval_table(trace, (i + 1) % degree)), - ) .collect::>(); partial_prod *= challenge.combine(evals.iter()); } else { @@ -328,8 +412,7 @@ where pub(crate) local_z: P, pub(crate) next_z: P, pub(crate) challenges: GrandProductChallenge, - pub(crate) local_columns: &'a [Column], - pub(crate) next_columns: &'a [Column], + pub(crate) columns: &'a [Column], pub(crate) filter_column: &'a Option>, } @@ -366,8 +449,7 @@ impl<'a, F: RichField + Extendable, const D: usize> local_z: *looking_z, next_z: *looking_z_next, challenges, - local_columns: &table.local_columns, - next_columns: &table.next_columns, + columns: &table.columns, filter_column: &table.filter_column, }); } @@ -377,8 +459,7 @@ impl<'a, F: RichField + Extendable, const D: usize> local_z: *looked_z, next_z: *looked_z_next, challenges, - local_columns: &looked_table.local_columns, - next_columns: &looked_table.next_columns, + columns: &looked_table.columns, filter_column: &looked_table.filter_column, }); } @@ -406,16 +487,14 @@ pub(crate) fn eval_cross_table_lookup_checks>(); - evals.extend(next_columns.iter().map(|c| c.eval(vars.next_values))); let combined = challenges.combine(evals.iter()); let local_filter = if let Some(column) = filter_column { column.eval(vars.local_values) @@ -436,8 +515,7 @@ pub struct CtlCheckVarsTarget<'a, F: Field, const D: usize> { pub(crate) local_z: ExtensionTarget, pub(crate) next_z: ExtensionTarget, pub(crate) challenges: GrandProductChallenge, - pub(crate) local_columns: &'a [Column], - pub(crate) next_columns: &'a [Column], + pub(crate) columns: &'a [Column], pub(crate) filter_column: &'a Option>, } @@ -473,8 +551,7 @@ impl<'a, F: Field, const D: usize> CtlCheckVarsTarget<'a, F, D> { local_z: *looking_z, next_z: *looking_z_next, challenges, - local_columns: &looking_table.local_columns, - next_columns: &looking_table.next_columns, + columns: &looking_table.columns, filter_column: &looking_table.filter_column, }); } @@ -486,8 +563,7 @@ impl<'a, F: Field, const D: usize> CtlCheckVarsTarget<'a, F, D> { local_z: *looked_z, next_z: *looked_z_next, challenges, - local_columns: &looked_table.local_columns, - next_columns: &looked_table.next_columns, + columns: &looked_table.columns, filter_column: &looked_table.filter_column, }); } @@ -513,8 +589,7 @@ pub(crate) fn eval_cross_table_lookup_checks_circuit< local_z, next_z, challenges, - local_columns, - next_columns, + columns, filter_column, } = lookup_vars; @@ -534,15 +609,10 @@ pub(crate) fn eval_cross_table_lookup_checks_circuit< builder.mul_add_extension(filter, x, tmp) // filter * x + 1 - filter } - let mut evals = local_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::>(); - evals.extend( - next_columns - .iter() - .map(|c| c.eval_circuit(builder, vars.next_values)), - ); let combined = challenges.combine_circuit(builder, &evals); let select = select(builder, local_filter, combined); @@ -692,15 +762,9 @@ pub(crate) mod testutils { }; if filter.is_one() { let row = table - .local_columns + .columns .iter() .map(|c| c.eval_table(trace, i)) - .chain( - table - .next_columns - .iter() - .map(|c| c.eval_table(trace, (i + 1) % trace[0].len())), - ) .collect::>(); multiset.entry(row).or_default().push((table.table, i)); } else { diff --git a/evm/src/keccak/keccak_stark.rs b/evm/src/keccak/keccak_stark.rs index 28e4e939cb..74f92622fc 100644 --- a/evm/src/keccak/keccak_stark.rs +++ b/evm/src/keccak/keccak_stark.rs @@ -763,8 +763,7 @@ mod tests { beta: F::ZERO, gamma: F::ZERO, }, - local_columns: vec![], - next_columns: vec![], + columns: vec![], filter_column: None, }; let ctl_data = CtlData { diff --git a/evm/src/prover.rs b/evm/src/prover.rs index 330dfcd1a8..0426da8ec5 100644 --- a/evm/src/prover.rs +++ b/evm/src/prover.rs @@ -589,8 +589,7 @@ where next_z: permutation_ctl_zs_commitment.get_lde_values_packed(i_next_start, step) [num_permutation_zs + i], challenges: zs_columns.challenge, - local_columns: &zs_columns.local_columns, - next_columns: &zs_columns.next_columns, + columns: &zs_columns.columns, filter_column: &zs_columns.filter_column, }) .collect::>(); @@ -708,8 +707,7 @@ fn check_constraints<'a, F, C, S, const D: usize>( local_z: permutation_ctl_zs_subgroup_evals[i][num_permutation_zs + iii], next_z: permutation_ctl_zs_subgroup_evals[i_next][num_permutation_zs + iii], challenges: zs_columns.challenge, - local_columns: &zs_columns.local_columns, - next_columns: &zs_columns.next_columns, + columns: &zs_columns.columns, filter_column: &zs_columns.filter_column, }) .collect::>(); From d1c00767bdbab0962b77db5c47549a4efc6b4451 Mon Sep 17 00:00:00 2001 From: Robin Salen Date: Mon, 18 Sep 2023 09:50:31 -0400 Subject: [PATCH 07/19] Cleanup lookup_test module and reduce module visibility --- plonky2/src/lib.rs | 4 +- plonky2/src/lookup_test.rs | 928 ++++++++++++++++++------------------- 2 files changed, 444 insertions(+), 488 deletions(-) diff --git a/plonky2/src/lib.rs b/plonky2/src/lib.rs index b4a4b6afc6..c2913023f5 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 165d5dfecc..bca90d59e3 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(()) - } +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}; +#[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; + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + type F = >::F; - let config = CircuitConfig::standard_recursion_config(); - let mut builder = CircuitBuilder::::new(config); + 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); + 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::(); - } + 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}; +#[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; + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + type F = >::F; - let config = CircuitConfig::standard_recursion_config(); - let mut builder = CircuitBuilder::::new(config); + 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); + let dummy = builder.add_virtual_target(); + builder.add_lookup_from_index(dummy, 0); - builder.build::(); - } + 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 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(()) - } +// 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(()) - } +#[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}; - // 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(()) + 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); } - // 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}; + 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(()) +} - const D: usize = 2; - type C = PoseidonGoldilocksConfig; - type F = >::F; +// 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}; - LOGGER_INITIALIZED.call_once(|| init_logger().unwrap()); - let config = CircuitConfig::standard_recursion_config(); - let mut builder = CircuitBuilder::::new(config); + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + type F = >::F; - let initial_a = builder.add_virtual_target(); - let initial_b = builder.add_virtual_target(); + LOGGER_INITIALIZED.call_once(|| init_logger().unwrap()); + let config = CircuitConfig::standard_recursion_config(); + let mut builder = CircuitBuilder::::new(config); - let look_val_a = 1; - let look_val_b = 2; + let initial_a = builder.add_virtual_target(); + let initial_b = builder.add_virtual_target(); - let tip5_table = TIP5_TABLE.to_vec(); - let table: LookupTable = Arc::new((0..256).zip_eq(tip5_table).collect()); + let look_val_a = 1; + let look_val_b = 2; - let table_index = builder.add_lookup_table_from_pairs(table.clone()); - let output_a = builder.add_lookup_from_index(initial_a, table_index); + let tip5_table = TIP5_TABLE.to_vec(); + let table: LookupTable = Arc::new((0..256).zip_eq(tip5_table).collect()); - let output_b = builder.add_lookup_from_index(initial_b, table_index); - let sum = builder.add(output_a, output_b); + let table_index = builder.add_lookup_table_from_pairs(table.clone()); + let output_a = builder.add_lookup_from_index(initial_a, table_index); - let table2_index = builder.add_lookup_table_from_pairs(table); + let output_b = builder.add_lookup_from_index(initial_b, table_index); + let sum = builder.add(output_a, output_b); - let output_final = builder.add_lookup_from_index(sum, table2_index); + let table2_index = builder.add_lookup_table_from_pairs(table); - 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 output_final = builder.add_lookup_from_index(sum, table2_index); - let luts_length = builder.get_luts_length(); + 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); - assert!( - luts_length == 1, - "There are {} LUTs when there should be only one", - luts_length - ); + let luts_length = builder.get_luts_length(); - let mut pw = PartialWitness::new(); + assert!( + luts_length == 1, + "There are {} LUTs when there should be only one", + luts_length + ); - 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 mut pw = PartialWitness::new(); - 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(); + pw.set_target(initial_a, F::from_canonical_usize(look_val_a)); + pw.set_target(initial_b, F::from_canonical_usize(look_val_b)); - Ok(()) - } + 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(); - #[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)); - } + Ok(()) +} - fn init_logger() -> anyhow::Result<()> { - let mut builder = env_logger::Builder::from_default_env(); - builder.format_timestamp(None); - builder.filter_level(LevelFilter::Debug); +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(()) - } + builder.try_init()?; + Ok(()) } From c4be838af44d0bdd313d8dc280316ed1c6a20ffe Mon Sep 17 00:00:00 2001 From: Robin Salen Date: Mon, 18 Sep 2023 09:59:48 -0400 Subject: [PATCH 08/19] Typo --- evm/src/fixed_recursive_verifier.rs | 10 +++++++--- evm/src/util.rs | 4 ++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/evm/src/fixed_recursive_verifier.rs b/evm/src/fixed_recursive_verifier.rs index 0b19c16a02..8b15cde7b1 100644 --- a/evm/src/fixed_recursive_verifier.rs +++ b/evm/src/fixed_recursive_verifier.rs @@ -905,7 +905,9 @@ where &self.root.public_values, &all_proof.public_values, ) - .map_err(|_| anyhow::Error::msg("Invalid conversion when setting public_values target."))?; + .map_err(|_| { + anyhow::Error::msg("Invalid conversion when setting public values targets.") + })?; let root_proof = self.root.circuit.prove(root_inputs)?; @@ -944,7 +946,9 @@ where &self.aggregation.public_values, &public_values, ) - .map_err(|_| anyhow::Error::msg("Invalid conversion when setting public values target."))?; + .map_err(|_| { + anyhow::Error::msg("Invalid conversion when setting public values targets.") + })?; let aggregation_proof = self.aggregation.circuit.prove(agg_inputs)?; Ok((aggregation_proof, public_values)) @@ -1005,7 +1009,7 @@ where set_public_value_targets(&mut block_inputs, &self.block.public_values, &public_values) .map_err(|_| { - anyhow::Error::msg("Invalid conversion when setting public values target.") + anyhow::Error::msg("Invalid conversion when setting public values targets.") })?; let block_proof = self.block.circuit.prove(block_inputs)?; diff --git a/evm/src/util.rs b/evm/src/util.rs index 9a0cd87fd5..ee285e2574 100644 --- a/evm/src/util.rs +++ b/evm/src/util.rs @@ -48,7 +48,7 @@ pub fn trace_rows_to_poly_values( } /// Returns the lowest LE 32-bit limb of a `U256` as a field element, -/// and errors in case the integer is actually greater. +/// and errors if the integer is actually greater. pub(crate) fn u256_lowest_limb(u256: U256) -> Result { if TryInto::::try_into(u256).is_err() { return Err(ProgramError::IntegerTooLarge); @@ -58,7 +58,7 @@ pub(crate) fn u256_lowest_limb(u256: U256) -> Result } /// Returns the lowest LE 64-bit word of a `U256` as two field elements -/// each storing a 32-bit limb, and errors in case the integer is actually greater. +/// each storing a 32-bit limb, and errors if the integer is actually greater. pub(crate) fn u256_lowest_word(u256: U256) -> Result<(F, F), ProgramError> { if TryInto::::try_into(u256).is_err() { return Err(ProgramError::IntegerTooLarge); From b5c28bd65e6a00f3673fdf2956f704beb705196c Mon Sep 17 00:00:00 2001 From: Robin Salen Date: Mon, 18 Sep 2023 14:29:11 -0400 Subject: [PATCH 09/19] Rename utility methods for U256 conversion Co-authored-by: Nicholas Ward --- evm/src/get_challenges.rs | 24 ++++++++++++------------ evm/src/recursive_verifier.rs | 16 ++++++++-------- evm/src/util.rs | 4 ++-- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/evm/src/get_challenges.rs b/evm/src/get_challenges.rs index 8cd6fd810f..ab25a28d57 100644 --- a/evm/src/get_challenges.rs +++ b/evm/src/get_challenges.rs @@ -13,7 +13,7 @@ use crate::permutation::{ get_n_grand_product_challenge_sets_target, }; use crate::proof::*; -use crate::util::{h256_limbs, u256_limbs, u256_lowest_limb, u256_lowest_word}; +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>( @@ -61,15 +61,15 @@ fn observe_block_metadata< challenger.observe_elements( &u256_limbs::(U256::from_big_endian(&block_metadata.block_beneficiary.0))[..5], ); - challenger.observe_element(u256_lowest_limb(block_metadata.block_timestamp)?); - challenger.observe_element(u256_lowest_limb(block_metadata.block_number)?); - challenger.observe_element(u256_lowest_limb(block_metadata.block_difficulty)?); - challenger.observe_element(u256_lowest_limb(block_metadata.block_gaslimit)?); - challenger.observe_element(u256_lowest_limb(block_metadata.block_chain_id)?); - let basefee = u256_lowest_word(block_metadata.block_base_fee)?; + 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_lowest_limb(block_metadata.block_gas_used)?); + 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])); } @@ -106,10 +106,10 @@ fn observe_extra_block_data< challenger: &mut Challenger, extra_data: &ExtraBlockData, ) -> Result<(), ProgramError> { - challenger.observe_element(u256_lowest_limb(extra_data.txn_number_before)?); - challenger.observe_element(u256_lowest_limb(extra_data.txn_number_after)?); - challenger.observe_element(u256_lowest_limb(extra_data.gas_used_before)?); - challenger.observe_element(u256_lowest_limb(extra_data.gas_used_after)?); + 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])); } diff --git a/evm/src/recursive_verifier.rs b/evm/src/recursive_verifier.rs index d58344bbf7..76a923385c 100644 --- a/evm/src/recursive_verifier.rs +++ b/evm/src/recursive_verifier.rs @@ -43,7 +43,7 @@ use crate::proof::{ TrieRootsTarget, }; use crate::stark::Stark; -use crate::util::{h256_limbs, u256_limbs, u256_lowest_limb, u256_lowest_word}; +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; @@ -1009,31 +1009,31 @@ where witness.set_target_arr(&block_metadata_target.block_beneficiary, &beneficiary_limbs); witness.set_target( block_metadata_target.block_timestamp, - u256_lowest_limb(block_metadata.block_timestamp)?, + u256_to_u32(block_metadata.block_timestamp)?, ); witness.set_target( block_metadata_target.block_number, - u256_lowest_limb(block_metadata.block_number)?, + u256_to_u32(block_metadata.block_number)?, ); witness.set_target( block_metadata_target.block_difficulty, - u256_lowest_limb(block_metadata.block_difficulty)?, + u256_to_u32(block_metadata.block_difficulty)?, ); witness.set_target( block_metadata_target.block_gaslimit, - u256_lowest_limb(block_metadata.block_gaslimit)?, + u256_to_u32(block_metadata.block_gaslimit)?, ); witness.set_target( block_metadata_target.block_chain_id, - u256_lowest_limb(block_metadata.block_chain_id)?, + u256_to_u32(block_metadata.block_chain_id)?, ); // Basefee fits in 2 limbs - let basefee = u256_lowest_word(block_metadata.block_base_fee)?; + 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, - u256_lowest_limb(block_metadata.block_gas_used)?, + 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() { diff --git a/evm/src/util.rs b/evm/src/util.rs index ee285e2574..a3f6d0507a 100644 --- a/evm/src/util.rs +++ b/evm/src/util.rs @@ -49,7 +49,7 @@ pub fn trace_rows_to_poly_values( /// 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_lowest_limb(u256: U256) -> Result { +pub(crate) fn u256_to_u32(u256: U256) -> Result { if TryInto::::try_into(u256).is_err() { return Err(ProgramError::IntegerTooLarge); } @@ -59,7 +59,7 @@ pub(crate) fn u256_lowest_limb(u256: U256) -> Result /// 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_lowest_word(u256: U256) -> Result<(F, F), ProgramError> { +pub(crate) fn u256_to_u64(u256: U256) -> Result<(F, F), ProgramError> { if TryInto::::try_into(u256).is_err() { return Err(ProgramError::IntegerTooLarge); } From 053553d4d7cd7c3391c11a3bba3250bd2bf4e071 Mon Sep 17 00:00:00 2001 From: Robin Salen Date: Mon, 18 Sep 2023 16:02:52 -0400 Subject: [PATCH 10/19] Reuse new packing instructions for MLOAD and MSTORE --- evm/src/cpu/kernel/asm/memory/syscalls.asm | 80 ++++------------------ 1 file changed, 12 insertions(+), 68 deletions(-) diff --git a/evm/src/cpu/kernel/asm/memory/syscalls.asm b/evm/src/cpu/kernel/asm/memory/syscalls.asm index 5f02382f41..3548930c36 100644 --- a/evm/src/cpu/kernel/asm/memory/syscalls.asm +++ b/evm/src/cpu/kernel/asm/memory/syscalls.asm @@ -8,41 +8,12 @@ global sys_mload: // stack: expanded_num_bytes, kexit_info, offset %update_mem_bytes // stack: kexit_info, offset - PUSH 0 // acc = 0 - // stack: acc, kexit_info, offset - DUP3 %add_const( 0) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0xf8) ADD - DUP3 %add_const( 1) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0xf0) ADD - DUP3 %add_const( 2) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0xe8) ADD - DUP3 %add_const( 3) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0xe0) ADD - DUP3 %add_const( 4) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0xd8) ADD - DUP3 %add_const( 5) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0xd0) ADD - DUP3 %add_const( 6) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0xc8) ADD - DUP3 %add_const( 7) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0xc0) ADD - DUP3 %add_const( 8) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0xb8) ADD - DUP3 %add_const( 9) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0xb0) ADD - DUP3 %add_const(10) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0xa8) ADD - DUP3 %add_const(11) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0xa0) ADD - DUP3 %add_const(12) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0x98) ADD - DUP3 %add_const(13) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0x90) ADD - DUP3 %add_const(14) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0x88) ADD - DUP3 %add_const(15) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0x80) ADD - DUP3 %add_const(16) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0x78) ADD - DUP3 %add_const(17) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0x70) ADD - DUP3 %add_const(18) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0x68) ADD - DUP3 %add_const(19) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0x60) ADD - DUP3 %add_const(20) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0x58) ADD - DUP3 %add_const(21) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0x50) ADD - DUP3 %add_const(22) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0x48) ADD - DUP3 %add_const(23) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0x40) ADD - DUP3 %add_const(24) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0x38) ADD - DUP3 %add_const(25) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0x30) ADD - DUP3 %add_const(26) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0x28) ADD - DUP3 %add_const(27) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0x20) ADD - DUP3 %add_const(28) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0x18) ADD - DUP3 %add_const(29) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0x10) ADD - DUP3 %add_const(30) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0x08) ADD - DUP3 %add_const(31) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0x00) ADD - %stack (acc, kexit_info, offset) -> (kexit_info, acc) + %stack(kexit_info, offset) -> (offset, 32, kexit_info) + PUSH @SEGMENT_MAIN_MEMORY + GET_CONTEXT + // stack: addr: 3, len, kexit_info + MLOAD_32BYTES + %stack (value, kexit_info) -> (kexit_info, value) EXIT_KERNEL global sys_mstore: @@ -55,39 +26,12 @@ global sys_mstore: // stack: expanded_num_bytes, kexit_info, offset, value %update_mem_bytes // stack: kexit_info, offset, value - DUP3 PUSH 0 BYTE DUP3 %add_const( 0) %mstore_current(@SEGMENT_MAIN_MEMORY) - DUP3 PUSH 1 BYTE DUP3 %add_const( 1) %mstore_current(@SEGMENT_MAIN_MEMORY) - DUP3 PUSH 2 BYTE DUP3 %add_const( 2) %mstore_current(@SEGMENT_MAIN_MEMORY) - DUP3 PUSH 3 BYTE DUP3 %add_const( 3) %mstore_current(@SEGMENT_MAIN_MEMORY) - DUP3 PUSH 4 BYTE DUP3 %add_const( 4) %mstore_current(@SEGMENT_MAIN_MEMORY) - DUP3 PUSH 5 BYTE DUP3 %add_const( 5) %mstore_current(@SEGMENT_MAIN_MEMORY) - DUP3 PUSH 6 BYTE DUP3 %add_const( 6) %mstore_current(@SEGMENT_MAIN_MEMORY) - DUP3 PUSH 7 BYTE DUP3 %add_const( 7) %mstore_current(@SEGMENT_MAIN_MEMORY) - DUP3 PUSH 8 BYTE DUP3 %add_const( 8) %mstore_current(@SEGMENT_MAIN_MEMORY) - DUP3 PUSH 9 BYTE DUP3 %add_const( 9) %mstore_current(@SEGMENT_MAIN_MEMORY) - DUP3 PUSH 10 BYTE DUP3 %add_const(10) %mstore_current(@SEGMENT_MAIN_MEMORY) - DUP3 PUSH 11 BYTE DUP3 %add_const(11) %mstore_current(@SEGMENT_MAIN_MEMORY) - DUP3 PUSH 12 BYTE DUP3 %add_const(12) %mstore_current(@SEGMENT_MAIN_MEMORY) - DUP3 PUSH 13 BYTE DUP3 %add_const(13) %mstore_current(@SEGMENT_MAIN_MEMORY) - DUP3 PUSH 14 BYTE DUP3 %add_const(14) %mstore_current(@SEGMENT_MAIN_MEMORY) - DUP3 PUSH 15 BYTE DUP3 %add_const(15) %mstore_current(@SEGMENT_MAIN_MEMORY) - DUP3 PUSH 16 BYTE DUP3 %add_const(16) %mstore_current(@SEGMENT_MAIN_MEMORY) - DUP3 PUSH 17 BYTE DUP3 %add_const(17) %mstore_current(@SEGMENT_MAIN_MEMORY) - DUP3 PUSH 18 BYTE DUP3 %add_const(18) %mstore_current(@SEGMENT_MAIN_MEMORY) - DUP3 PUSH 19 BYTE DUP3 %add_const(19) %mstore_current(@SEGMENT_MAIN_MEMORY) - DUP3 PUSH 20 BYTE DUP3 %add_const(20) %mstore_current(@SEGMENT_MAIN_MEMORY) - DUP3 PUSH 21 BYTE DUP3 %add_const(21) %mstore_current(@SEGMENT_MAIN_MEMORY) - DUP3 PUSH 22 BYTE DUP3 %add_const(22) %mstore_current(@SEGMENT_MAIN_MEMORY) - DUP3 PUSH 23 BYTE DUP3 %add_const(23) %mstore_current(@SEGMENT_MAIN_MEMORY) - DUP3 PUSH 24 BYTE DUP3 %add_const(24) %mstore_current(@SEGMENT_MAIN_MEMORY) - DUP3 PUSH 25 BYTE DUP3 %add_const(25) %mstore_current(@SEGMENT_MAIN_MEMORY) - DUP3 PUSH 26 BYTE DUP3 %add_const(26) %mstore_current(@SEGMENT_MAIN_MEMORY) - DUP3 PUSH 27 BYTE DUP3 %add_const(27) %mstore_current(@SEGMENT_MAIN_MEMORY) - DUP3 PUSH 28 BYTE DUP3 %add_const(28) %mstore_current(@SEGMENT_MAIN_MEMORY) - DUP3 PUSH 29 BYTE DUP3 %add_const(29) %mstore_current(@SEGMENT_MAIN_MEMORY) - DUP3 PUSH 30 BYTE DUP3 %add_const(30) %mstore_current(@SEGMENT_MAIN_MEMORY) - DUP3 PUSH 31 BYTE DUP3 %add_const(31) %mstore_current(@SEGMENT_MAIN_MEMORY) - %stack (kexit_info, offset, value) -> (kexit_info) + %stack(kexit_info, offset, value) -> (offset, value, 32, kexit_info) + PUSH @SEGMENT_MAIN_MEMORY + GET_CONTEXT + // stack: addr: 3, value, len, kexit_info + MSTORE_32BYTES + // stack: kexit_info EXIT_KERNEL global sys_mstore8: From 4eb6a3b57c3b8616c2e9718ce806b2d52b7954f7 Mon Sep 17 00:00:00 2001 From: Hamy Ratoanina Date: Tue, 19 Sep 2023 14:02:29 -0400 Subject: [PATCH 11/19] Fix eval_table --- evm/src/cross_table_lookup.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/evm/src/cross_table_lookup.rs b/evm/src/cross_table_lookup.rs index 315bf42f7b..a2dad1ab40 100644 --- a/evm/src/cross_table_lookup.rs +++ b/evm/src/cross_table_lookup.rs @@ -179,7 +179,7 @@ impl Column { // 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 { + if !self.next_row_linear_combination.is_empty() && row < table[0].values.len() - 1 { res += self .next_row_linear_combination .iter() @@ -497,7 +497,7 @@ pub(crate) fn eval_cross_table_lookup_checks>(); let combined = challenges.combine(evals.iter()); let local_filter = if let Some(column) = filter_column { - column.eval(vars.local_values) + column.eval_with_next(vars.local_values, vars.next_values) } else { P::ONES }; From 7d7f01da46c994a76185ab5d705ebd9545fc91b1 Mon Sep 17 00:00:00 2001 From: Tamir Hemo Date: Tue, 19 Sep 2023 11:32:13 -0700 Subject: [PATCH 12/19] refactor prove method --- plonky2/src/plonk/prover.rs | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/plonky2/src/plonk/prover.rs b/plonky2/src/plonk/prover.rs index b77f7aa5ff..c53ea78e29 100644 --- a/plonky2/src/plonk/prover.rs +++ b/plonky2/src/plonk/prover.rs @@ -112,6 +112,21 @@ pub fn prove, C: GenericConfig, const D: common_data: &CommonCircuitData, inputs: PartialWitness, timing: &mut TimingTree, +) -> Result> { + let partition_witness = timed!( + timing, + &format!("run {} generators", prover_data.generators.len()), + generate_partial_witness(inputs, prover_data, common_data) + ); + + prove_with_partition_witness(prover_data, common_data, partition_witness, timing) +} + +pub fn prove_with_partition_witness, C: GenericConfig, const D: usize>( + prover_data: &ProverOnlyCircuitData, + common_data: &CommonCircuitData, + mut partition_witness: PartitionWitness, + timing: &mut TimingTree, ) -> Result> where C::Hasher: Hasher, @@ -123,12 +138,6 @@ where let quotient_degree = common_data.quotient_degree(); let degree = common_data.degree(); - let mut partition_witness = timed!( - timing, - &format!("run {} generators", prover_data.generators.len()), - generate_partial_witness(inputs, prover_data, common_data) - ); - set_lookup_wires(prover_data, common_data, &mut partition_witness); let public_inputs = partition_witness.get_targets(&prover_data.public_inputs); From a44379b5a18bc5111e1fa1e305e4e60acffe2d53 Mon Sep 17 00:00:00 2001 From: Tamir Hemo Date: Tue, 19 Sep 2023 11:39:20 -0700 Subject: [PATCH 13/19] fmt and clippy --- plonky2/src/plonk/prover.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/plonky2/src/plonk/prover.rs b/plonky2/src/plonk/prover.rs index c53ea78e29..ee94288e43 100644 --- a/plonky2/src/plonk/prover.rs +++ b/plonky2/src/plonk/prover.rs @@ -122,7 +122,11 @@ pub fn prove, C: GenericConfig, const D: prove_with_partition_witness(prover_data, common_data, partition_witness, timing) } -pub fn prove_with_partition_witness, C: GenericConfig, const D: usize>( +pub fn prove_with_partition_witness< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, +>( prover_data: &ProverOnlyCircuitData, common_data: &CommonCircuitData, mut partition_witness: PartitionWitness, From 8839285f4d6a6f344705cafb8d3f4b04d7635dd4 Mon Sep 17 00:00:00 2001 From: Tamir Hemo Date: Tue, 19 Sep 2023 11:45:42 -0700 Subject: [PATCH 14/19] add trait bound --- plonky2/src/plonk/prover.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/plonky2/src/plonk/prover.rs b/plonky2/src/plonk/prover.rs index ee94288e43..41aebdb1e9 100644 --- a/plonky2/src/plonk/prover.rs +++ b/plonky2/src/plonk/prover.rs @@ -112,7 +112,11 @@ pub fn prove, C: GenericConfig, const D: common_data: &CommonCircuitData, inputs: PartialWitness, timing: &mut TimingTree, -) -> Result> { +) -> Result> +where + C::Hasher: Hasher, + C::InnerHasher: Hasher, +{ let partition_witness = timed!( timing, &format!("run {} generators", prover_data.generators.len()), From edd3f383a176c598a47c6e687ea78d7088d28103 Mon Sep 17 00:00:00 2001 From: Robin Salen Date: Tue, 19 Sep 2023 15:41:36 -0400 Subject: [PATCH 15/19] Add some doc for BlockMetadata / ExtraBlockData --- evm/src/proof.rs | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/evm/src/proof.rs b/evm/src/proof.rs index 76f3af32f0..4da5ad23ab 100644 --- a/evm/src/proof.rs +++ b/evm/src/proof.rs @@ -74,32 +74,66 @@ impl Default for BlockHashes { } } +/// User-provided helper values to compute the `BLOCKHASH` opcode. +/// The proofs across consecutive blocks ensure that these values +/// are consistent (i.e. shifted by one to the left). +/// +/// When the block number is less than 256, dummy values, i.e. `H256::default()`, +/// should be used for the additional block hashes. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct BlockHashes { + /// The previous 256 hashes to the current block. The leftmost hash, i.e. `prev_hashes[0]`, + /// is the oldest, and the rightmost, i.e. `prev_hashes[255]` is the hash of the parent block. pub prev_hashes: Vec, + // The hash of the current block. pub cur_hash: H256, } +/// Metadata contained in a block header. Those are identical between +/// all state transition proofs within the same block. #[derive(Debug, Clone, Default, Deserialize, Serialize)] pub struct BlockMetadata { + /// The address of this block's producer. pub block_beneficiary: Address, + /// The timestamp of this block. pub block_timestamp: U256, + /// The index of this block. pub block_number: U256, + /// The difficulty (before PoS transition) of this block. pub block_difficulty: U256, + /// The gas limit of this block. It must fit in a `u32`. pub block_gaslimit: U256, + /// The chain id of this block. pub block_chain_id: U256, + /// The base fee of this block. pub block_base_fee: U256, + /// The total gas used in this block. It must fit in a `u32`. pub block_gas_used: U256, + /// The block bloom of this block, represented as the consecutive + /// 32-byte chunks of a block's final bloom filter string. pub block_bloom: [U256; 8], } +/// Additional block data that are specific to the local transaction being proven, +/// unlike `BlockMetadata`. #[derive(Debug, Clone, Default, Deserialize, Serialize)] pub struct ExtraBlockData { + /// The transaction count prior execution of the local state transition, starting + /// at 0 for the initial transaction of a block. pub txn_number_before: U256, + /// The transaction count after execution of the local state transition. pub txn_number_after: U256, + /// The accumulated gas used prior execution of the local state transition, starting + /// at 0 for the initial transaction of a block. pub gas_used_before: U256, + /// The accumulated gas used after execution of the local state transition. It should + /// match the `block_gas_used` value after execution of the last transaction in a block. pub gas_used_after: U256, + /// The accumulated bloom filter of this block prior execution of the local state transition, + /// starting with all zeros for the initial transaction of a block. pub block_bloom_before: [U256; 8], + /// The accumulated bloom filter after execution of the local state transition. It should + /// match the `block_bloom` value after execution of the last transaction in a block. pub block_bloom_after: [U256; 8], } From d8874c8389e050b0fb3f9e85c0d1374b786f7f1f Mon Sep 17 00:00:00 2001 From: Robin Salen Date: Tue, 19 Sep 2023 18:38:40 -0400 Subject: [PATCH 16/19] Update ranges indices --- evm/src/fixed_recursive_verifier.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/evm/src/fixed_recursive_verifier.rs b/evm/src/fixed_recursive_verifier.rs index 02887dd939..05ec015c61 100644 --- a/evm/src/fixed_recursive_verifier.rs +++ b/evm/src/fixed_recursive_verifier.rs @@ -375,49 +375,49 @@ where let arithmetic = RecursiveCircuitsForTable::new( Table::Arithmetic, &all_stark.arithmetic_stark, - degree_bits_ranges[0].clone(), + degree_bits_ranges[Table::Arithmetic as usize].clone(), &all_stark.cross_table_lookups, stark_config, ); let byte_packing = RecursiveCircuitsForTable::new( Table::BytePacking, &all_stark.byte_packing_stark, - degree_bits_ranges[1].clone(), + degree_bits_ranges[Table::BytePacking as usize].clone(), &all_stark.cross_table_lookups, stark_config, ); let cpu = RecursiveCircuitsForTable::new( Table::Cpu, &all_stark.cpu_stark, - degree_bits_ranges[2].clone(), + degree_bits_ranges[Table::Cpu as usize].clone(), &all_stark.cross_table_lookups, stark_config, ); let keccak = RecursiveCircuitsForTable::new( Table::Keccak, &all_stark.keccak_stark, - degree_bits_ranges[3].clone(), + degree_bits_ranges[Table::Keccak as usize].clone(), &all_stark.cross_table_lookups, stark_config, ); let keccak_sponge = RecursiveCircuitsForTable::new( Table::KeccakSponge, &all_stark.keccak_sponge_stark, - degree_bits_ranges[4].clone(), + degree_bits_ranges[Table::KeccakSponge as usize].clone(), &all_stark.cross_table_lookups, stark_config, ); let logic = RecursiveCircuitsForTable::new( Table::Logic, &all_stark.logic_stark, - degree_bits_ranges[5].clone(), + degree_bits_ranges[Table::Logic as usize].clone(), &all_stark.cross_table_lookups, stark_config, ); let memory = RecursiveCircuitsForTable::new( Table::Memory, &all_stark.memory_stark, - degree_bits_ranges[6].clone(), + degree_bits_ranges[Table::Memory as usize].clone(), &all_stark.cross_table_lookups, stark_config, ); From 6618cfad4a4ee0e5fe4fa749dfebb60490033e55 Mon Sep 17 00:00:00 2001 From: Robin Salen <30937548+Nashtare@users.noreply.github.com> Date: Wed, 20 Sep 2023 08:31:50 -0400 Subject: [PATCH 17/19] Remove SEQUENCE_LEN in BytePackingStark (#1241) --- evm/src/byte_packing/byte_packing_stark.rs | 81 +++++++++------------- evm/src/byte_packing/columns.rs | 9 +-- 2 files changed, 35 insertions(+), 55 deletions(-) diff --git a/evm/src/byte_packing/byte_packing_stark.rs b/evm/src/byte_packing/byte_packing_stark.rs index f97a2b28ab..aa6a2dcf88 100644 --- a/evm/src/byte_packing/byte_packing_stark.rs +++ b/evm/src/byte_packing/byte_packing_stark.rs @@ -47,7 +47,7 @@ use plonky2::util::transpose; use super::NUM_BYTES; use crate::byte_packing::columns::{ index_bytes, value_bytes, ADDR_CONTEXT, ADDR_SEGMENT, ADDR_VIRTUAL, BYTE_INDICES_COLS, IS_READ, - NUM_COLUMNS, RANGE_COUNTER, RC_COLS, SEQUENCE_END, SEQUENCE_LEN, TIMESTAMP, + NUM_COLUMNS, RANGE_COUNTER, RC_COLS, SEQUENCE_END, TIMESTAMP, }; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::cross_table_lookup::Column; @@ -76,15 +76,16 @@ pub(crate) fn ctl_looked_data() -> Vec> { }) .collect(); - Column::singles([ - ADDR_CONTEXT, - ADDR_SEGMENT, - ADDR_VIRTUAL, - SEQUENCE_LEN, - TIMESTAMP, - ]) - .chain(outputs) - .collect() + // This will correspond to the actual sequence length when the `SEQUENCE_END` flag is on. + let sequence_len: Column = Column::linear_combination( + (0..NUM_BYTES).map(|i| (index_bytes(i), F::from_canonical_usize(i + 1))), + ); + + Column::singles([ADDR_CONTEXT, ADDR_SEGMENT, ADDR_VIRTUAL]) + .chain([sequence_len]) + .chain(Column::singles(&[TIMESTAMP])) + .chain(outputs) + .collect() } pub fn ctl_looked_filter() -> Column { @@ -202,7 +203,6 @@ impl, const D: usize> BytePackingStark { row[ADDR_VIRTUAL] = F::from_canonical_usize(virt + bytes.len() - 1); row[TIMESTAMP] = F::from_canonical_usize(timestamp); - row[SEQUENCE_LEN] = F::from_canonical_usize(bytes.len()); for (i, &byte) in bytes.iter().rev().enumerate() { if i == bytes.len() - 1 { @@ -354,27 +354,20 @@ impl, const D: usize> Stark for BytePackingSt current_sequence_end * next_filter * (next_sequence_start - one), ); - // The remaining length of a byte sequence must decrease by one or be zero. - let current_sequence_length = vars.local_values[SEQUENCE_LEN]; + // The active position in a byte sequence must increase by one on every row + // or be one on the next row (i.e. at the start of a new sequence). let current_position = self.get_active_position(vars.local_values); let next_position = self.get_active_position(vars.next_values); - let current_remaining_length = current_sequence_length - current_position; - let next_sequence_length = vars.next_values[SEQUENCE_LEN]; - let next_remaining_length = next_sequence_length - next_position; yield_constr.constraint_transition( - current_remaining_length * (current_remaining_length - next_remaining_length - one), - ); - - // At the start of a sequence, the remaining length must be equal to the starting length minus one - yield_constr.constraint( - current_sequence_start * (current_sequence_length - current_remaining_length - one), + next_filter * (next_position - one) * (next_position - current_position - one), ); - // The remaining length on the last row must be zero. - yield_constr.constraint_last_row(current_remaining_length); + // The last row must be the end of a sequence or a padding row. + yield_constr.constraint_last_row(current_filter * (current_sequence_end - one)); - // If the current remaining length is zero, the end flag must be one. - yield_constr.constraint(current_remaining_length * current_sequence_end); + // If the next position is one in an active row, the current end flag must be one. + yield_constr + .constraint_transition(next_filter * current_sequence_end * (next_position - one)); // The context, segment and timestamp fields must remain unchanged throughout a byte sequence. // The virtual address must decrement by one at each step of a sequence. @@ -489,36 +482,26 @@ impl, const D: usize> Stark for BytePackingSt let constraint = builder.mul_extension(next_filter, constraint); yield_constr.constraint_transition(builder, constraint); - // The remaining length of a byte sequence must decrease by one or be zero. - let current_sequence_length = vars.local_values[SEQUENCE_LEN]; - let next_sequence_length = vars.next_values[SEQUENCE_LEN]; + // The active position in a byte sequence must increase by one on every row + // or be one on the next row (i.e. at the start of a new sequence). let current_position = self.get_active_position_circuit(builder, vars.local_values); let next_position = self.get_active_position_circuit(builder, vars.next_values); - let current_remaining_length = - builder.sub_extension(current_sequence_length, current_position); - let next_remaining_length = builder.sub_extension(next_sequence_length, next_position); - let length_diff = builder.sub_extension(current_remaining_length, next_remaining_length); - let constraint = builder.mul_sub_extension( - current_remaining_length, - length_diff, - current_remaining_length, - ); + let position_diff = builder.sub_extension(next_position, current_position); + let is_new_or_inactive = builder.mul_sub_extension(next_filter, next_position, next_filter); + let constraint = + builder.mul_sub_extension(is_new_or_inactive, position_diff, is_new_or_inactive); yield_constr.constraint_transition(builder, constraint); - // At the start of a sequence, the remaining length must be equal to the starting length minus one - let current_sequence_length = vars.local_values[SEQUENCE_LEN]; - let length_diff = builder.sub_extension(current_sequence_length, current_remaining_length); + // The last row must be the end of a sequence or a padding row. let constraint = - builder.mul_sub_extension(current_sequence_start, length_diff, current_sequence_start); - yield_constr.constraint(builder, constraint); + builder.mul_sub_extension(current_filter, current_sequence_end, current_filter); + yield_constr.constraint_last_row(builder, constraint); - // The remaining length on the last row must be zero. - yield_constr.constraint_last_row(builder, current_remaining_length); - - // If the current remaining length is zero, the end flag must be one. - let constraint = builder.mul_extension(current_remaining_length, current_sequence_end); - yield_constr.constraint(builder, constraint); + // If the next position is one in an active row, the current end flag must be one. + let constraint = builder.mul_extension(next_filter, current_sequence_end); + let constraint = builder.mul_sub_extension(constraint, next_position, constraint); + yield_constr.constraint_transition(builder, constraint); // The context, segment and timestamp fields must remain unchanged throughout a byte sequence. // The virtual address must decrement by one at each step of a sequence. diff --git a/evm/src/byte_packing/columns.rs b/evm/src/byte_packing/columns.rs index f04f450c51..feb8f2e2c3 100644 --- a/evm/src/byte_packing/columns.rs +++ b/evm/src/byte_packing/columns.rs @@ -16,7 +16,8 @@ pub(crate) const fn index_bytes(i: usize) -> usize { BYTES_INDICES_START + i } -// Note: Those are used as filter for distinguishing active vs padding rows. +// Note: Those are used as filter for distinguishing active vs padding rows, +// and also to obtain the length of a sequence of bytes being processed. pub(crate) const BYTE_INDICES_COLS: Range = BYTES_INDICES_START..BYTES_INDICES_START + NUM_BYTES; @@ -25,12 +26,8 @@ pub(crate) const ADDR_SEGMENT: usize = ADDR_CONTEXT + 1; pub(crate) const ADDR_VIRTUAL: usize = ADDR_SEGMENT + 1; pub(crate) const TIMESTAMP: usize = ADDR_VIRTUAL + 1; -/// The total length of a sequence of bytes. -/// Cannot be greater than 32. -pub(crate) const SEQUENCE_LEN: usize = TIMESTAMP + 1; - // 32 byte limbs hold a total of 256 bits. -const BYTES_VALUES_START: usize = SEQUENCE_LEN + 1; +const BYTES_VALUES_START: usize = TIMESTAMP + 1; pub(crate) const fn value_bytes(i: usize) -> usize { debug_assert!(i < NUM_BYTES); BYTES_VALUES_START + i From d6be2b987b0c103eed6ffe675fc7a9d8e4c6e85b Mon Sep 17 00:00:00 2001 From: Robin Salen <30937548+Nashtare@users.noreply.github.com> Date: Fri, 22 Sep 2023 09:19:13 -0400 Subject: [PATCH 18/19] Remove `generic_const_exprs` feature from EVM crate (#1246) * Remove const_generic_exprs feature from EVM crate * Get a generic impl of StarkFrame --- evm/src/arithmetic/arithmetic_stark.rs | 25 ++- evm/src/byte_packing/byte_packing_stark.rs | 122 +++++++------ evm/src/cpu/bootstrap_kernel.rs | 21 +-- evm/src/cpu/cpu_stark.rs | 43 +++-- evm/src/cross_table_lookup.rs | 20 ++- evm/src/evaluation_frame.rs | 47 +++++ evm/src/fixed_recursive_verifier.rs | 24 +-- evm/src/keccak/keccak_stark.rs | 178 ++++++++++--------- evm/src/keccak/round_flags.rs | 38 ++-- evm/src/keccak_sponge/keccak_sponge_stark.rs | 33 +++- evm/src/lib.rs | 3 +- evm/src/logic.rs | 18 +- evm/src/lookup.rs | 29 +-- evm/src/memory/memory_stark.rs | 98 +++++----- evm/src/permutation.rs | 15 +- evm/src/prover.rs | 68 ++----- evm/src/recursive_verifier.rs | 11 +- evm/src/stark.rs | 21 ++- evm/src/stark_testing.rs | 52 ++---- evm/src/vanishing_poly.rs | 6 +- evm/src/vars.rs | 19 -- evm/src/verifier.rs | 29 +-- 22 files changed, 469 insertions(+), 451 deletions(-) create mode 100644 evm/src/evaluation_frame.rs delete mode 100644 evm/src/vars.rs diff --git a/evm/src/arithmetic/arithmetic_stark.rs b/evm/src/arithmetic/arithmetic_stark.rs index 5441cf2760..a6db127807 100644 --- a/evm/src/arithmetic/arithmetic_stark.rs +++ b/evm/src/arithmetic/arithmetic_stark.rs @@ -7,18 +7,20 @@ use plonky2::field::packed::PackedField; use plonky2::field::polynomial::PolynomialValues; use plonky2::field::types::Field; use plonky2::hash::hash_types::RichField; +use plonky2::iop::ext_target::ExtensionTarget; use plonky2::plonk::circuit_builder::CircuitBuilder; use plonky2::util::transpose; use static_assertions::const_assert; +use super::columns::NUM_ARITH_COLUMNS; use crate::all_stark::Table; use crate::arithmetic::{addcy, byte, columns, divmod, modular, mul, Operation}; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::cross_table_lookup::{Column, TableWithColumns}; +use crate::evaluation_frame::{StarkEvaluationFrame, StarkFrame}; use crate::lookup::{eval_lookups, eval_lookups_circuit, permuted_cols}; use crate::permutation::PermutationPair; use crate::stark::Stark; -use crate::vars::{StarkEvaluationTargets, StarkEvaluationVars}; /// Link the 16-bit columns of the arithmetic table, split into groups /// of N_LIMBS at a time in `regs`, with the corresponding 32-bit @@ -168,11 +170,16 @@ impl ArithmeticStark { } impl, const D: usize> Stark for ArithmeticStark { - const COLUMNS: usize = columns::NUM_ARITH_COLUMNS; + type EvaluationFrame = StarkFrame + where + FE: FieldExtension, + P: PackedField; + + type EvaluationFrameTarget = StarkFrame, NUM_ARITH_COLUMNS>; fn eval_packed_generic( &self, - vars: StarkEvaluationVars, + vars: &Self::EvaluationFrame, yield_constr: &mut ConstraintConsumer

, ) where FE: FieldExtension, @@ -183,8 +190,8 @@ impl, const D: usize> Stark for ArithmeticSta eval_lookups(vars, yield_constr, col, col + 1); } - let lv = vars.local_values; - let nv = vars.next_values; + let lv: &[P; NUM_ARITH_COLUMNS] = vars.get_local_values().try_into().unwrap(); + let nv: &[P; NUM_ARITH_COLUMNS] = vars.get_next_values().try_into().unwrap(); // Check the range column: First value must be 0, last row // must be 2^16-1, and intermediate rows must increment by 0 @@ -207,7 +214,7 @@ impl, const D: usize> Stark for ArithmeticSta fn eval_ext_circuit( &self, builder: &mut CircuitBuilder, - vars: StarkEvaluationTargets, + vars: &Self::EvaluationFrameTarget, yield_constr: &mut RecursiveConstraintConsumer, ) { // Range check all the columns @@ -215,8 +222,10 @@ impl, const D: usize> Stark for ArithmeticSta eval_lookups_circuit(builder, vars, yield_constr, col, col + 1); } - let lv = vars.local_values; - let nv = vars.next_values; + let lv: &[ExtensionTarget; NUM_ARITH_COLUMNS] = + vars.get_local_values().try_into().unwrap(); + let nv: &[ExtensionTarget; NUM_ARITH_COLUMNS] = + vars.get_next_values().try_into().unwrap(); let rc1 = lv[columns::RANGE_COUNTER]; let rc2 = nv[columns::RANGE_COUNTER]; diff --git a/evm/src/byte_packing/byte_packing_stark.rs b/evm/src/byte_packing/byte_packing_stark.rs index aa6a2dcf88..d8f8e2e813 100644 --- a/evm/src/byte_packing/byte_packing_stark.rs +++ b/evm/src/byte_packing/byte_packing_stark.rs @@ -51,9 +51,9 @@ use crate::byte_packing::columns::{ }; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::cross_table_lookup::Column; +use crate::evaluation_frame::{StarkEvaluationFrame, StarkFrame}; use crate::lookup::{eval_lookups, eval_lookups_circuit, permuted_cols}; use crate::stark::Stark; -use crate::vars::{StarkEvaluationTargets, StarkEvaluationVars}; use crate::witness::memory::MemoryAddress; /// Strict upper bound for the individual bytes range-check. @@ -211,7 +211,7 @@ impl, const D: usize> BytePackingStark { row[value_bytes(i)] = F::from_canonical_u8(byte); row[index_bytes(i)] = F::ONE; - rows.push(row.into()); + rows.push(row); row[index_bytes(i)] = F::ZERO; row[ADDR_VIRTUAL] -= F::ONE; } @@ -248,7 +248,7 @@ impl, const D: usize> BytePackingStark { } } - /// There is only one `i` for which `vars.local_values[index_bytes(i)]` is non-zero, + /// There is only one `i` for which `local_values[index_bytes(i)]` is non-zero, /// and `i+1` is the current position: fn get_active_position(&self, row: &[P; NUM_COLUMNS]) -> P where @@ -281,11 +281,16 @@ impl, const D: usize> BytePackingStark { } impl, const D: usize> Stark for BytePackingStark { - const COLUMNS: usize = NUM_COLUMNS; + type EvaluationFrame = StarkFrame + where + FE: FieldExtension, + P: PackedField; + + type EvaluationFrameTarget = StarkFrame, NUM_COLUMNS>; fn eval_packed_generic( &self, - vars: StarkEvaluationVars, + vars: &Self::EvaluationFrame, yield_constr: &mut ConstraintConsumer

, ) where FE: FieldExtension, @@ -296,68 +301,62 @@ impl, const D: usize> Stark for BytePackingSt eval_lookups(vars, yield_constr, col, col + 1); } + let local_values: &[P; NUM_COLUMNS] = vars.get_local_values().try_into().unwrap(); + let next_values: &[P; NUM_COLUMNS] = vars.get_next_values().try_into().unwrap(); + let one = P::ONES; // We filter active columns by summing all the byte indices. // Constraining each of them to be boolean is done later on below. - let current_filter = vars.local_values[BYTE_INDICES_COLS] - .iter() - .copied() - .sum::

(); + let current_filter = local_values[BYTE_INDICES_COLS].iter().copied().sum::

(); yield_constr.constraint(current_filter * (current_filter - one)); // The filter column must start by one. yield_constr.constraint_first_row(current_filter - one); // The is_read flag must be boolean. - let current_is_read = vars.local_values[IS_READ]; + let current_is_read = local_values[IS_READ]; yield_constr.constraint(current_is_read * (current_is_read - one)); // Each byte index must be boolean. for i in 0..NUM_BYTES { - let idx_i = vars.local_values[index_bytes(i)]; + let idx_i = local_values[index_bytes(i)]; yield_constr.constraint(idx_i * (idx_i - one)); } // The sequence start flag column must start by one. - let current_sequence_start = vars.local_values[index_bytes(0)]; + let current_sequence_start = local_values[index_bytes(0)]; yield_constr.constraint_first_row(current_sequence_start - one); // The sequence end flag must be boolean - let current_sequence_end = vars.local_values[SEQUENCE_END]; + let current_sequence_end = local_values[SEQUENCE_END]; yield_constr.constraint(current_sequence_end * (current_sequence_end - one)); // If filter is off, all flags and byte indices must be off. - let byte_indices = vars.local_values[BYTE_INDICES_COLS] - .iter() - .copied() - .sum::

(); + let byte_indices = local_values[BYTE_INDICES_COLS].iter().copied().sum::

(); yield_constr.constraint( (current_filter - one) * (current_is_read + current_sequence_end + byte_indices), ); // Only padding rows have their filter turned off. - let next_filter = vars.next_values[BYTE_INDICES_COLS] - .iter() - .copied() - .sum::

(); + let next_filter = next_values[BYTE_INDICES_COLS].iter().copied().sum::

(); yield_constr.constraint_transition(next_filter * (next_filter - current_filter)); // Unless the current sequence end flag is activated, the is_read filter must remain unchanged. - let next_is_read = vars.next_values[IS_READ]; + let next_is_read = next_values[IS_READ]; yield_constr .constraint_transition((current_sequence_end - one) * (next_is_read - current_is_read)); // If the sequence end flag is activated, the next row must be a new sequence or filter must be off. - let next_sequence_start = vars.next_values[index_bytes(0)]; + let next_sequence_start = next_values[index_bytes(0)]; yield_constr.constraint_transition( current_sequence_end * next_filter * (next_sequence_start - one), ); // The active position in a byte sequence must increase by one on every row // or be one on the next row (i.e. at the start of a new sequence). - let current_position = self.get_active_position(vars.local_values); - let next_position = self.get_active_position(vars.next_values); + let current_position = self.get_active_position(local_values); + let next_position = self.get_active_position(next_values); yield_constr.constraint_transition( next_filter * (next_position - one) * (next_position - current_position - one), ); @@ -371,14 +370,14 @@ impl, const D: usize> Stark for BytePackingSt // The context, segment and timestamp fields must remain unchanged throughout a byte sequence. // The virtual address must decrement by one at each step of a sequence. - let current_context = vars.local_values[ADDR_CONTEXT]; - let next_context = vars.next_values[ADDR_CONTEXT]; - let current_segment = vars.local_values[ADDR_SEGMENT]; - let next_segment = vars.next_values[ADDR_SEGMENT]; - let current_virtual = vars.local_values[ADDR_VIRTUAL]; - let next_virtual = vars.next_values[ADDR_VIRTUAL]; - let current_timestamp = vars.local_values[TIMESTAMP]; - let next_timestamp = vars.next_values[TIMESTAMP]; + let current_context = local_values[ADDR_CONTEXT]; + let next_context = next_values[ADDR_CONTEXT]; + let current_segment = local_values[ADDR_SEGMENT]; + let next_segment = next_values[ADDR_SEGMENT]; + let current_virtual = local_values[ADDR_VIRTUAL]; + let next_virtual = next_values[ADDR_VIRTUAL]; + let current_timestamp = local_values[TIMESTAMP]; + let next_timestamp = next_values[TIMESTAMP]; yield_constr.constraint_transition( next_filter * (next_sequence_start - one) * (next_context - current_context), ); @@ -395,9 +394,9 @@ impl, const D: usize> Stark for BytePackingSt // If not at the end of a sequence, each next byte must equal the current one // when reading through the sequence, or the next byte index must be one. for i in 0..NUM_BYTES { - let current_byte = vars.local_values[value_bytes(i)]; - let next_byte = vars.next_values[value_bytes(i)]; - let next_byte_index = vars.next_values[index_bytes(i)]; + let current_byte = local_values[value_bytes(i)]; + let next_byte = next_values[value_bytes(i)]; + let next_byte_index = next_values[index_bytes(i)]; yield_constr.constraint_transition( (current_sequence_end - one) * (next_byte_index - one) * (next_byte - current_byte), ); @@ -407,7 +406,7 @@ impl, const D: usize> Stark for BytePackingSt fn eval_ext_circuit( &self, builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder, - vars: StarkEvaluationTargets, + vars: &Self::EvaluationFrameTarget, yield_constr: &mut RecursiveConstraintConsumer, ) { // Range check all the columns @@ -415,9 +414,14 @@ impl, const D: usize> Stark for BytePackingSt eval_lookups_circuit(builder, vars, yield_constr, col, col + 1); } + let local_values: &[ExtensionTarget; NUM_COLUMNS] = + vars.get_local_values().try_into().unwrap(); + let next_values: &[ExtensionTarget; NUM_COLUMNS] = + vars.get_next_values().try_into().unwrap(); + // We filter active columns by summing all the byte indices. // Constraining each of them to be boolean is done later on below. - let current_filter = builder.add_many_extension(&vars.local_values[BYTE_INDICES_COLS]); + let current_filter = builder.add_many_extension(&local_values[BYTE_INDICES_COLS]); let constraint = builder.mul_sub_extension(current_filter, current_filter, current_filter); yield_constr.constraint(builder, constraint); @@ -426,25 +430,25 @@ impl, const D: usize> Stark for BytePackingSt yield_constr.constraint_first_row(builder, constraint); // The is_read flag must be boolean. - let current_is_read = vars.local_values[IS_READ]; + let current_is_read = local_values[IS_READ]; let constraint = builder.mul_sub_extension(current_is_read, current_is_read, current_is_read); yield_constr.constraint(builder, constraint); // Each byte index must be boolean. for i in 0..NUM_BYTES { - let idx_i = vars.local_values[index_bytes(i)]; + let idx_i = local_values[index_bytes(i)]; let constraint = builder.mul_sub_extension(idx_i, idx_i, idx_i); yield_constr.constraint(builder, constraint); } // The sequence start flag column must start by one. - let current_sequence_start = vars.local_values[index_bytes(0)]; + let current_sequence_start = local_values[index_bytes(0)]; let constraint = builder.add_const_extension(current_sequence_start, F::NEG_ONE); yield_constr.constraint_first_row(builder, constraint); // The sequence end flag must be boolean - let current_sequence_end = vars.local_values[SEQUENCE_END]; + let current_sequence_end = local_values[SEQUENCE_END]; let constraint = builder.mul_sub_extension( current_sequence_end, current_sequence_end, @@ -453,27 +457,27 @@ impl, const D: usize> Stark for BytePackingSt yield_constr.constraint(builder, constraint); // If filter is off, all flags and byte indices must be off. - let byte_indices = builder.add_many_extension(&vars.local_values[BYTE_INDICES_COLS]); + let byte_indices = builder.add_many_extension(&local_values[BYTE_INDICES_COLS]); let constraint = builder.add_extension(current_sequence_end, byte_indices); let constraint = builder.add_extension(constraint, current_is_read); let constraint = builder.mul_sub_extension(constraint, current_filter, constraint); yield_constr.constraint(builder, constraint); // Only padding rows have their filter turned off. - let next_filter = builder.add_many_extension(&vars.next_values[BYTE_INDICES_COLS]); + let next_filter = builder.add_many_extension(&next_values[BYTE_INDICES_COLS]); let constraint = builder.sub_extension(next_filter, current_filter); let constraint = builder.mul_extension(next_filter, constraint); yield_constr.constraint_transition(builder, constraint); // Unless the current sequence end flag is activated, the is_read filter must remain unchanged. - let next_is_read = vars.next_values[IS_READ]; + let next_is_read = next_values[IS_READ]; let diff_is_read = builder.sub_extension(next_is_read, current_is_read); let constraint = builder.mul_sub_extension(diff_is_read, current_sequence_end, diff_is_read); yield_constr.constraint_transition(builder, constraint); // If the sequence end flag is activated, the next row must be a new sequence or filter must be off. - let next_sequence_start = vars.next_values[index_bytes(0)]; + let next_sequence_start = next_values[index_bytes(0)]; let constraint = builder.mul_sub_extension( current_sequence_end, next_sequence_start, @@ -484,8 +488,8 @@ impl, const D: usize> Stark for BytePackingSt // The active position in a byte sequence must increase by one on every row // or be one on the next row (i.e. at the start of a new sequence). - let current_position = self.get_active_position_circuit(builder, vars.local_values); - let next_position = self.get_active_position_circuit(builder, vars.next_values); + let current_position = self.get_active_position_circuit(builder, local_values); + let next_position = self.get_active_position_circuit(builder, next_values); let position_diff = builder.sub_extension(next_position, current_position); let is_new_or_inactive = builder.mul_sub_extension(next_filter, next_position, next_filter); @@ -505,14 +509,14 @@ impl, const D: usize> Stark for BytePackingSt // The context, segment and timestamp fields must remain unchanged throughout a byte sequence. // The virtual address must decrement by one at each step of a sequence. - let current_context = vars.local_values[ADDR_CONTEXT]; - let next_context = vars.next_values[ADDR_CONTEXT]; - let current_segment = vars.local_values[ADDR_SEGMENT]; - let next_segment = vars.next_values[ADDR_SEGMENT]; - let current_virtual = vars.local_values[ADDR_VIRTUAL]; - let next_virtual = vars.next_values[ADDR_VIRTUAL]; - let current_timestamp = vars.local_values[TIMESTAMP]; - let next_timestamp = vars.next_values[TIMESTAMP]; + let current_context = local_values[ADDR_CONTEXT]; + let next_context = next_values[ADDR_CONTEXT]; + let current_segment = local_values[ADDR_SEGMENT]; + let next_segment = next_values[ADDR_SEGMENT]; + let current_virtual = local_values[ADDR_VIRTUAL]; + let next_virtual = next_values[ADDR_VIRTUAL]; + let current_timestamp = local_values[TIMESTAMP]; + let next_timestamp = next_values[TIMESTAMP]; let addr_filter = builder.mul_sub_extension(next_filter, next_sequence_start, next_filter); { let constraint = builder.sub_extension(next_context, current_context); @@ -538,9 +542,9 @@ impl, const D: usize> Stark for BytePackingSt // If not at the end of a sequence, each next byte must equal the current one // when reading through the sequence, or the next byte index must be one. for i in 0..NUM_BYTES { - let current_byte = vars.local_values[value_bytes(i)]; - let next_byte = vars.next_values[value_bytes(i)]; - let next_byte_index = vars.next_values[index_bytes(i)]; + let current_byte = local_values[value_bytes(i)]; + let next_byte = next_values[value_bytes(i)]; + let next_byte_index = next_values[index_bytes(i)]; let byte_diff = builder.sub_extension(next_byte, current_byte); let constraint = builder.mul_sub_extension(byte_diff, next_byte_index, byte_diff); let constraint = diff --git a/evm/src/cpu/bootstrap_kernel.rs b/evm/src/cpu/bootstrap_kernel.rs index 4aee617c53..759c852aae 100644 --- a/evm/src/cpu/bootstrap_kernel.rs +++ b/evm/src/cpu/bootstrap_kernel.rs @@ -1,22 +1,20 @@ //! The initial phase of execution, where the kernel code is hashed while being written to memory. //! The hash is then checked against a precomputed kernel hash. -use std::borrow::Borrow; - use itertools::Itertools; use plonky2::field::extension::Extendable; use plonky2::field::packed::PackedField; use plonky2::field::types::Field; use plonky2::hash::hash_types::RichField; +use plonky2::iop::ext_target::ExtensionTarget; use plonky2::plonk::circuit_builder::CircuitBuilder; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; -use crate::cpu::columns::{CpuColumnsView, NUM_CPU_COLUMNS}; +use crate::cpu::columns::CpuColumnsView; use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::membus::NUM_GP_CHANNELS; use crate::generation::state::GenerationState; use crate::memory::segments::Segment; -use crate::vars::{StarkEvaluationTargets, StarkEvaluationVars}; use crate::witness::memory::MemoryAddress; use crate::witness::util::{keccak_sponge_log, mem_write_gp_log_and_fill}; @@ -58,13 +56,11 @@ pub(crate) fn generate_bootstrap_kernel(state: &mut GenerationState log::info!("Bootstrapping took {} cycles", state.traces.clock()); } -pub(crate) fn eval_bootstrap_kernel>( - vars: StarkEvaluationVars, +pub(crate) fn eval_bootstrap_kernel_packed>( + local_values: &CpuColumnsView

, + next_values: &CpuColumnsView

, yield_constr: &mut ConstraintConsumer

, ) { - let local_values: &CpuColumnsView<_> = vars.local_values.borrow(); - let next_values: &CpuColumnsView<_> = vars.next_values.borrow(); - // IS_BOOTSTRAP_KERNEL must have an init value of 1, a final value of 0, and a delta in {0, -1}. let local_is_bootstrap = local_values.is_bootstrap_kernel; let next_is_bootstrap = next_values.is_bootstrap_kernel; @@ -103,13 +99,12 @@ pub(crate) fn eval_bootstrap_kernel>( } } -pub(crate) fn eval_bootstrap_kernel_circuit, const D: usize>( +pub(crate) fn eval_bootstrap_kernel_ext_circuit, const D: usize>( builder: &mut CircuitBuilder, - vars: StarkEvaluationTargets, + local_values: &CpuColumnsView>, + next_values: &CpuColumnsView>, yield_constr: &mut RecursiveConstraintConsumer, ) { - let local_values: &CpuColumnsView<_> = vars.local_values.borrow(); - let next_values: &CpuColumnsView<_> = vars.next_values.borrow(); let one = builder.one_extension(); // IS_BOOTSTRAP_KERNEL must have an init value of 1, a final value of 0, and a delta in {0, -1}. diff --git a/evm/src/cpu/cpu_stark.rs b/evm/src/cpu/cpu_stark.rs index bd2fcf193b..14bb6015fc 100644 --- a/evm/src/cpu/cpu_stark.rs +++ b/evm/src/cpu/cpu_stark.rs @@ -7,6 +7,7 @@ use plonky2::field::extension::{Extendable, FieldExtension}; use plonky2::field::packed::PackedField; use plonky2::field::types::Field; use plonky2::hash::hash_types::RichField; +use plonky2::iop::ext_target::ExtensionTarget; use super::halt; use crate::all_stark::Table; @@ -18,10 +19,10 @@ use crate::cpu::{ modfp254, pc, push0, shift, simple_logic, stack, stack_bounds, syscalls_exceptions, }; use crate::cross_table_lookup::{Column, TableWithColumns}; +use crate::evaluation_frame::{StarkEvaluationFrame, StarkFrame}; use crate::memory::segments::Segment; use crate::memory::{NUM_CHANNELS, VALUE_LIMBS}; use crate::stark::Stark; -use crate::vars::{StarkEvaluationTargets, StarkEvaluationVars}; pub fn ctl_data_keccak_sponge() -> Vec> { // When executing KECCAK_GENERAL, the GP memory channels are used as follows: @@ -227,19 +228,29 @@ impl CpuStark { } impl, const D: usize> Stark for CpuStark { - const COLUMNS: usize = NUM_CPU_COLUMNS; + type EvaluationFrame = StarkFrame + where + FE: FieldExtension, + P: PackedField; + + type EvaluationFrameTarget = StarkFrame, NUM_CPU_COLUMNS>; fn eval_packed_generic( &self, - vars: StarkEvaluationVars, + vars: &Self::EvaluationFrame, yield_constr: &mut ConstraintConsumer

, ) where FE: FieldExtension, P: PackedField, { - let local_values = vars.local_values.borrow(); - let next_values = vars.next_values.borrow(); - bootstrap_kernel::eval_bootstrap_kernel(vars, yield_constr); + let local_values = + TryInto::<[P; NUM_CPU_COLUMNS]>::try_into(vars.get_local_values()).unwrap(); + let local_values: &CpuColumnsView

= local_values.borrow(); + let next_values = + TryInto::<[P; NUM_CPU_COLUMNS]>::try_into(vars.get_next_values()).unwrap(); + let next_values: &CpuColumnsView

= next_values.borrow(); + + bootstrap_kernel::eval_bootstrap_kernel_packed(local_values, next_values, yield_constr); contextops::eval_packed(local_values, next_values, yield_constr); control_flow::eval_packed_generic(local_values, next_values, yield_constr); decode::eval_packed_generic(local_values, yield_constr); @@ -262,12 +273,24 @@ impl, const D: usize> Stark for CpuStark, - vars: StarkEvaluationTargets, + vars: &Self::EvaluationFrameTarget, yield_constr: &mut RecursiveConstraintConsumer, ) { - let local_values = vars.local_values.borrow(); - let next_values = vars.next_values.borrow(); - bootstrap_kernel::eval_bootstrap_kernel_circuit(builder, vars, yield_constr); + let local_values = + TryInto::<[ExtensionTarget; NUM_CPU_COLUMNS]>::try_into(vars.get_local_values()) + .unwrap(); + let local_values: &CpuColumnsView> = local_values.borrow(); + let next_values = + TryInto::<[ExtensionTarget; NUM_CPU_COLUMNS]>::try_into(vars.get_next_values()) + .unwrap(); + let next_values: &CpuColumnsView> = next_values.borrow(); + + bootstrap_kernel::eval_bootstrap_kernel_ext_circuit( + builder, + local_values, + next_values, + yield_constr, + ); contextops::eval_ext_circuit(builder, local_values, next_values, yield_constr); control_flow::eval_ext_circuit(builder, local_values, next_values, yield_constr); decode::eval_ext_circuit(builder, local_values, yield_constr); diff --git a/evm/src/cross_table_lookup.rs b/evm/src/cross_table_lookup.rs index a2dad1ab40..28b189947b 100644 --- a/evm/src/cross_table_lookup.rs +++ b/evm/src/cross_table_lookup.rs @@ -16,10 +16,10 @@ use plonky2::plonk::config::GenericConfig; use crate::all_stark::{Table, NUM_TABLES}; use crate::config::StarkConfig; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; +use crate::evaluation_frame::StarkEvaluationFrame; use crate::permutation::{GrandProductChallenge, GrandProductChallengeSet}; use crate::proof::{StarkProofTarget, StarkProofWithMetadata}; use crate::stark::Stark; -use crate::vars::{StarkEvaluationTargets, StarkEvaluationVars}; /// Represent a linear combination of columns. #[derive(Clone, Debug)] @@ -473,7 +473,7 @@ impl<'a, F: RichField + Extendable, const D: usize> /// 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, + vars: &S::EvaluationFrame, ctl_vars: &[CtlCheckVars], consumer: &mut ConstraintConsumer

, ) where @@ -482,6 +482,9 @@ pub(crate) fn eval_cross_table_lookup_checks, S: Stark, { + let local_values = vars.get_local_values(); + let next_values = vars.get_next_values(); + for lookup_vars in ctl_vars { let CtlCheckVars { local_z, @@ -493,11 +496,11 @@ pub(crate) fn eval_cross_table_lookup_checks>(); let combined = challenges.combine(evals.iter()); let local_filter = if let Some(column) = filter_column { - column.eval_with_next(vars.local_values, vars.next_values) + column.eval_with_next(local_values, next_values) } else { P::ONES }; @@ -580,10 +583,13 @@ pub(crate) fn eval_cross_table_lookup_checks_circuit< const D: usize, >( builder: &mut CircuitBuilder, - vars: StarkEvaluationTargets, + vars: &S::EvaluationFrameTarget, ctl_vars: &[CtlCheckVarsTarget], consumer: &mut RecursiveConstraintConsumer, ) { + let local_values = vars.get_local_values(); + let next_values = vars.get_next_values(); + for lookup_vars in ctl_vars { let CtlCheckVarsTarget { local_z, @@ -595,7 +601,7 @@ pub(crate) fn eval_cross_table_lookup_checks_circuit< let one = builder.one_extension(); let local_filter = if let Some(column) = filter_column { - column.eval_circuit(builder, vars.local_values) + column.eval_circuit(builder, local_values) } else { one }; @@ -611,7 +617,7 @@ pub(crate) fn eval_cross_table_lookup_checks_circuit< let evals = columns .iter() - .map(|c| c.eval_with_next_circuit(builder, vars.local_values, vars.next_values)) + .map(|c| c.eval_with_next_circuit(builder, local_values, next_values)) .collect::>(); let combined = challenges.combine_circuit(builder, &evals); diff --git a/evm/src/evaluation_frame.rs b/evm/src/evaluation_frame.rs new file mode 100644 index 0000000000..0f6bbe2ceb --- /dev/null +++ b/evm/src/evaluation_frame.rs @@ -0,0 +1,47 @@ +/// A trait for viewing an evaluation frame of a STARK table. +/// +/// It allows to access the current and next rows at a given step +/// and can be used to implement constraint evaluation both natively +/// and recursively. +pub trait StarkEvaluationFrame: Sized { + /// The number of columns for the STARK table this evaluation frame views. + const COLUMNS: usize; + + /// Returns the local values (i.e. current row) for this evaluation frame. + fn get_local_values(&self) -> &[T]; + /// Returns the next values (i.e. next row) for this evaluation frame. + fn get_next_values(&self) -> &[T]; + + /// Outputs a new evaluation frame from the provided local and next values. + /// + /// **NOTE**: Concrete implementations of this method SHOULD ensure that + /// the provided slices lengths match the `Self::COLUMNS` value. + fn from_values(lv: &[T], nv: &[T]) -> Self; +} + +pub struct StarkFrame { + local_values: [T; N], + next_values: [T; N], +} + +impl StarkEvaluationFrame for StarkFrame { + const COLUMNS: usize = N; + + fn get_local_values(&self) -> &[T] { + &self.local_values + } + + fn get_next_values(&self) -> &[T] { + &self.next_values + } + + fn from_values(lv: &[T], nv: &[T]) -> Self { + assert_eq!(lv.len(), Self::COLUMNS); + assert_eq!(nv.len(), Self::COLUMNS); + + Self { + local_values: lv.try_into().unwrap(), + next_values: nv.try_into().unwrap(), + } + } +} diff --git a/evm/src/fixed_recursive_verifier.rs b/evm/src/fixed_recursive_verifier.rs index 05ec015c61..7fefe95f4f 100644 --- a/evm/src/fixed_recursive_verifier.rs +++ b/evm/src/fixed_recursive_verifier.rs @@ -28,17 +28,10 @@ use plonky2::util::timing::TimingTree; use plonky2_util::log2_ceil; use crate::all_stark::{all_cross_table_lookups, AllStark, Table, NUM_TABLES}; -use crate::arithmetic::arithmetic_stark::ArithmeticStark; -use crate::byte_packing::byte_packing_stark::BytePackingStark; use crate::config::StarkConfig; -use crate::cpu::cpu_stark::CpuStark; use crate::cross_table_lookup::{verify_cross_table_lookups_circuit, CrossTableLookup}; use crate::generation::GenerationInputs; use crate::get_challenges::observe_public_values_target; -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::{ BlockHashesTarget, BlockMetadataTarget, ExtraBlockDataTarget, PublicValues, PublicValuesTarget, @@ -297,13 +290,6 @@ where F: RichField + Extendable, C: GenericConfig + 'static, C::Hasher: AlgebraicHasher, - [(); ArithmeticStark::::COLUMNS]:, - [(); BytePackingStark::::COLUMNS]:, - [(); CpuStark::::COLUMNS]:, - [(); KeccakStark::::COLUMNS]:, - [(); KeccakSpongeStark::::COLUMNS]:, - [(); LogicStark::::COLUMNS]:, - [(); MemoryStark::::COLUMNS]:, { pub fn to_bytes( &self, @@ -1083,10 +1069,7 @@ where degree_bits_range: Range, all_ctls: &[CrossTableLookup], stark_config: &StarkConfig, - ) -> Self - where - [(); S::COLUMNS]:, - { + ) -> Self { let by_stark_size = degree_bits_range .map(|degree_bits| { ( @@ -1207,10 +1190,7 @@ where degree_bits: usize, all_ctls: &[CrossTableLookup], stark_config: &StarkConfig, - ) -> Self - where - [(); S::COLUMNS]:, - { + ) -> Self { let initial_wrapper = recursive_stark_circuit( table, stark, diff --git a/evm/src/keccak/keccak_stark.rs b/evm/src/keccak/keccak_stark.rs index 74f92622fc..c517a5f6ac 100644 --- a/evm/src/keccak/keccak_stark.rs +++ b/evm/src/keccak/keccak_stark.rs @@ -6,12 +6,14 @@ use plonky2::field::packed::PackedField; use plonky2::field::polynomial::PolynomialValues; use plonky2::field::types::Field; use plonky2::hash::hash_types::RichField; +use plonky2::iop::ext_target::ExtensionTarget; use plonky2::plonk::plonk_common::reduce_with_powers_ext_circuit; use plonky2::timed; use plonky2::util::timing::TimingTree; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::cross_table_lookup::Column; +use crate::evaluation_frame::{StarkEvaluationFrame, StarkFrame}; use crate::keccak::columns::{ reg_a, reg_a_prime, reg_a_prime_prime, reg_a_prime_prime_0_0_bit, reg_a_prime_prime_prime, reg_b, reg_c, reg_c_prime, reg_input_limb, reg_output_limb, reg_preimage, reg_step, @@ -24,7 +26,6 @@ use crate::keccak::logic::{ use crate::keccak::round_flags::{eval_round_flags, eval_round_flags_recursively}; use crate::stark::Stark; use crate::util::trace_rows_to_poly_values; -use crate::vars::{StarkEvaluationTargets, StarkEvaluationVars}; /// Number of rounds in a Keccak permutation. pub(crate) const NUM_ROUNDS: usize = 24; @@ -239,11 +240,16 @@ impl, const D: usize> KeccakStark { } impl, const D: usize> Stark for KeccakStark { - const COLUMNS: usize = NUM_COLUMNS; + type EvaluationFrame = StarkFrame + where + FE: FieldExtension, + P: PackedField; + + type EvaluationFrameTarget = StarkFrame, NUM_COLUMNS>; fn eval_packed_generic( &self, - vars: StarkEvaluationVars, + vars: &Self::EvaluationFrame, yield_constr: &mut ConstraintConsumer

, ) where FE: FieldExtension, @@ -251,33 +257,34 @@ impl, const D: usize> Stark for KeccakStark, const D: usize> Stark for KeccakStark, const D: usize> Stark for KeccakStark, const D: usize> Stark for KeccakStark, const D: usize> Stark for KeccakStark, const D: usize> Stark for KeccakStark, const D: usize> Stark for KeccakStark, const D: usize> Stark for KeccakStark, const D: usize> Stark for KeccakStark, const D: usize> Stark for KeccakStark, - vars: StarkEvaluationTargets, + vars: &Self::EvaluationFrameTarget, yield_constr: &mut RecursiveConstraintConsumer, ) { let one_ext = builder.one_extension(); @@ -433,49 +440,44 @@ impl, const D: usize> Stark for KeccakStark, const D: usize> Stark for KeccakStark, const D: usize> Stark for KeccakStark, const D: usize> Stark for KeccakStark, const D: usize> Stark for KeccakStark, const D: usize> Stark for KeccakStark, const D: usize> Stark for KeccakStark, const D: usize> Stark for KeccakStark, const D: usize> Stark for KeccakStark>( - vars: StarkEvaluationVars, + vars: &StarkFrame, yield_constr: &mut ConstraintConsumer

, ) { + let local_values = vars.get_local_values(); + let next_values = vars.get_next_values(); + // Initially, the first step flag should be 1 while the others should be 0. - yield_constr.constraint_first_row(vars.local_values[reg_step(0)] - F::ONE); + yield_constr.constraint_first_row(local_values[reg_step(0)] - F::ONE); for i in 1..NUM_ROUNDS { - yield_constr.constraint_first_row(vars.local_values[reg_step(i)]); + yield_constr.constraint_first_row(local_values[reg_step(i)]); } // Flags should circularly increment, or be all zero for padding rows. - let next_any_flag = (0..NUM_ROUNDS) - .map(|i| vars.next_values[reg_step(i)]) - .sum::

(); + let next_any_flag = (0..NUM_ROUNDS).map(|i| next_values[reg_step(i)]).sum::

(); for i in 0..NUM_ROUNDS { - let current_round_flag = vars.local_values[reg_step(i)]; - let next_round_flag = vars.next_values[reg_step((i + 1) % NUM_ROUNDS)]; + let current_round_flag = local_values[reg_step(i)]; + let next_round_flag = next_values[reg_step((i + 1) % NUM_ROUNDS)]; yield_constr.constraint_transition(next_any_flag * (next_round_flag - current_round_flag)); } // Padding rows should always be followed by padding rows. let current_any_flag = (0..NUM_ROUNDS) - .map(|i| vars.local_values[reg_step(i)]) + .map(|i| local_values[reg_step(i)]) .sum::

(); yield_constr.constraint_transition(next_any_flag * (current_any_flag - F::ONE)); } pub(crate) fn eval_round_flags_recursively, const D: usize>( builder: &mut CircuitBuilder, - vars: StarkEvaluationTargets, + vars: &StarkFrame, NUM_COLUMNS>, yield_constr: &mut RecursiveConstraintConsumer, ) { let one = builder.one_extension(); + let local_values = vars.get_local_values(); + let next_values = vars.get_next_values(); // Initially, the first step flag should be 1 while the others should be 0. - let step_0_minus_1 = builder.sub_extension(vars.local_values[reg_step(0)], one); + let step_0_minus_1 = builder.sub_extension(local_values[reg_step(0)], one); yield_constr.constraint_first_row(builder, step_0_minus_1); for i in 1..NUM_ROUNDS { - yield_constr.constraint_first_row(builder, vars.local_values[reg_step(i)]); + yield_constr.constraint_first_row(builder, local_values[reg_step(i)]); } // Flags should circularly increment, or be all zero for padding rows. let next_any_flag = - builder.add_many_extension((0..NUM_ROUNDS).map(|i| vars.next_values[reg_step(i)])); + builder.add_many_extension((0..NUM_ROUNDS).map(|i| next_values[reg_step(i)])); for i in 0..NUM_ROUNDS { - let current_round_flag = vars.local_values[reg_step(i)]; - let next_round_flag = vars.next_values[reg_step((i + 1) % NUM_ROUNDS)]; + let current_round_flag = local_values[reg_step(i)]; + let next_round_flag = next_values[reg_step((i + 1) % NUM_ROUNDS)]; let diff = builder.sub_extension(next_round_flag, current_round_flag); let constraint = builder.mul_extension(next_any_flag, diff); yield_constr.constraint_transition(builder, constraint); @@ -63,7 +67,7 @@ pub(crate) fn eval_round_flags_recursively, const D // Padding rows should always be followed by padding rows. let current_any_flag = - builder.add_many_extension((0..NUM_ROUNDS).map(|i| vars.local_values[reg_step(i)])); + builder.add_many_extension((0..NUM_ROUNDS).map(|i| local_values[reg_step(i)])); let constraint = builder.mul_sub_extension(next_any_flag, current_any_flag, next_any_flag); yield_constr.constraint_transition(builder, constraint); } diff --git a/evm/src/keccak_sponge/keccak_sponge_stark.rs b/evm/src/keccak_sponge/keccak_sponge_stark.rs index d78e965141..65edc94102 100644 --- a/evm/src/keccak_sponge/keccak_sponge_stark.rs +++ b/evm/src/keccak_sponge/keccak_sponge_stark.rs @@ -17,10 +17,10 @@ use plonky2_util::ceil_div_usize; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::cpu::kernel::keccak_util::keccakf_u32s; use crate::cross_table_lookup::Column; +use crate::evaluation_frame::{StarkEvaluationFrame, StarkFrame}; use crate::keccak_sponge::columns::*; use crate::stark::Stark; use crate::util::trace_rows_to_poly_values; -use crate::vars::{StarkEvaluationTargets, StarkEvaluationVars}; use crate::witness::memory::MemoryAddress; pub(crate) fn ctl_looked_data() -> Vec> { @@ -423,18 +423,27 @@ impl, const D: usize> KeccakSpongeStark { } impl, const D: usize> Stark for KeccakSpongeStark { - const COLUMNS: usize = NUM_KECCAK_SPONGE_COLUMNS; + type EvaluationFrame = StarkFrame + where + FE: FieldExtension, + P: PackedField; + + type EvaluationFrameTarget = StarkFrame, NUM_KECCAK_SPONGE_COLUMNS>; fn eval_packed_generic( &self, - vars: StarkEvaluationVars, + vars: &Self::EvaluationFrame, yield_constr: &mut ConstraintConsumer

, ) where FE: FieldExtension, P: PackedField, { - let local_values: &KeccakSpongeColumnsView

= vars.local_values.borrow(); - let next_values: &KeccakSpongeColumnsView

= vars.next_values.borrow(); + let local_values = + TryInto::<[P; NUM_KECCAK_SPONGE_COLUMNS]>::try_into(vars.get_local_values()).unwrap(); + let local_values: &KeccakSpongeColumnsView

= local_values.borrow(); + let next_values = + TryInto::<[P; NUM_KECCAK_SPONGE_COLUMNS]>::try_into(vars.get_next_values()).unwrap(); + let next_values: &KeccakSpongeColumnsView

= next_values.borrow(); // Each flag (full-input block, final block or implied dummy flag) must be boolean. let is_full_input_block = local_values.is_full_input_block; @@ -537,11 +546,19 @@ impl, const D: usize> Stark for KeccakSpongeS fn eval_ext_circuit( &self, builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder, - vars: StarkEvaluationTargets, + vars: &Self::EvaluationFrameTarget, yield_constr: &mut RecursiveConstraintConsumer, ) { - let local_values: &KeccakSpongeColumnsView> = vars.local_values.borrow(); - let next_values: &KeccakSpongeColumnsView> = vars.next_values.borrow(); + let local_values = TryInto::<[ExtensionTarget; NUM_KECCAK_SPONGE_COLUMNS]>::try_into( + vars.get_local_values(), + ) + .unwrap(); + let local_values: &KeccakSpongeColumnsView> = local_values.borrow(); + let next_values = TryInto::<[ExtensionTarget; NUM_KECCAK_SPONGE_COLUMNS]>::try_into( + vars.get_next_values(), + ) + .unwrap(); + let next_values: &KeccakSpongeColumnsView> = next_values.borrow(); let one = builder.one_extension(); diff --git a/evm/src/lib.rs b/evm/src/lib.rs index ab48cda04f..474d6fafae 100644 --- a/evm/src/lib.rs +++ b/evm/src/lib.rs @@ -4,7 +4,6 @@ #![allow(clippy::type_complexity)] #![allow(clippy::field_reassign_with_default)] #![feature(let_chains)] -#![feature(generic_const_exprs)] pub mod all_stark; pub mod arithmetic; @@ -14,6 +13,7 @@ pub mod constraint_consumer; pub mod cpu; pub mod cross_table_lookup; pub mod curve_pairings; +pub mod evaluation_frame; pub mod extension_tower; pub mod fixed_recursive_verifier; pub mod generation; @@ -31,7 +31,6 @@ pub mod stark; pub mod stark_testing; pub mod util; pub mod vanishing_poly; -pub mod vars; pub mod verifier; pub mod witness; diff --git a/evm/src/logic.rs b/evm/src/logic.rs index 3529ed9a9c..319dfab2d0 100644 --- a/evm/src/logic.rs +++ b/evm/src/logic.rs @@ -7,16 +7,17 @@ use plonky2::field::packed::PackedField; use plonky2::field::polynomial::PolynomialValues; use plonky2::field::types::Field; use plonky2::hash::hash_types::RichField; +use plonky2::iop::ext_target::ExtensionTarget; use plonky2::timed; use plonky2::util::timing::TimingTree; use plonky2_util::ceil_div_usize; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::cross_table_lookup::Column; +use crate::evaluation_frame::{StarkEvaluationFrame, StarkFrame}; use crate::logic::columns::NUM_COLUMNS; use crate::stark::Stark; use crate::util::{limb_from_bits_le, limb_from_bits_le_recursive, trace_rows_to_poly_values}; -use crate::vars::{StarkEvaluationTargets, StarkEvaluationVars}; // Total number of bits per input/output. const VAL_BITS: usize = 256; @@ -181,17 +182,22 @@ impl LogicStark { } impl, const D: usize> Stark for LogicStark { - const COLUMNS: usize = NUM_COLUMNS; + type EvaluationFrame = StarkFrame + where + FE: FieldExtension, + P: PackedField; + + type EvaluationFrameTarget = StarkFrame, NUM_COLUMNS>; fn eval_packed_generic( &self, - vars: StarkEvaluationVars, + vars: &Self::EvaluationFrame, yield_constr: &mut ConstraintConsumer

, ) where FE: FieldExtension, P: PackedField, { - let lv = &vars.local_values; + let lv = vars.get_local_values(); // IS_AND, IS_OR, and IS_XOR come from the CPU table, so we assume they're valid. let is_and = lv[columns::IS_AND]; @@ -237,10 +243,10 @@ impl, const D: usize> Stark for LogicStark, - vars: StarkEvaluationTargets, + vars: &Self::EvaluationFrameTarget, yield_constr: &mut RecursiveConstraintConsumer, ) { - let lv = &vars.local_values; + let lv = vars.get_local_values(); // IS_AND, IS_OR, and IS_XOR come from the CPU table, so we assume they're valid. let is_and = lv[columns::IS_AND]; diff --git a/evm/src/lookup.rs b/evm/src/lookup.rs index d7e12bacf1..d6c1b2178b 100644 --- a/evm/src/lookup.rs +++ b/evm/src/lookup.rs @@ -5,20 +5,24 @@ use plonky2::field::extension::Extendable; use plonky2::field::packed::PackedField; use plonky2::field::types::{Field, PrimeField64}; use plonky2::hash::hash_types::RichField; +use plonky2::iop::ext_target::ExtensionTarget; use plonky2::plonk::circuit_builder::CircuitBuilder; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; -use crate::vars::{StarkEvaluationTargets, StarkEvaluationVars}; +use crate::evaluation_frame::StarkEvaluationFrame; -pub(crate) fn eval_lookups, const COLS: usize>( - vars: StarkEvaluationVars, +pub(crate) fn eval_lookups, E: StarkEvaluationFrame

>( + vars: &E, yield_constr: &mut ConstraintConsumer

, col_permuted_input: usize, col_permuted_table: usize, ) { - let local_perm_input = vars.local_values[col_permuted_input]; - let next_perm_table = vars.next_values[col_permuted_table]; - let next_perm_input = vars.next_values[col_permuted_input]; + let local_values = vars.get_local_values(); + let next_values = vars.get_next_values(); + + let local_perm_input = local_values[col_permuted_input]; + let next_perm_table = next_values[col_permuted_table]; + let next_perm_input = next_values[col_permuted_input]; // A "vertical" diff between the local and next permuted inputs. let diff_input_prev = next_perm_input - local_perm_input; @@ -35,18 +39,21 @@ pub(crate) fn eval_lookups, const COLS: usi pub(crate) fn eval_lookups_circuit< F: RichField + Extendable, + E: StarkEvaluationFrame>, const D: usize, - const COLS: usize, >( builder: &mut CircuitBuilder, - vars: StarkEvaluationTargets, + vars: &E, yield_constr: &mut RecursiveConstraintConsumer, col_permuted_input: usize, col_permuted_table: usize, ) { - let local_perm_input = vars.local_values[col_permuted_input]; - let next_perm_table = vars.next_values[col_permuted_table]; - let next_perm_input = vars.next_values[col_permuted_input]; + let local_values = vars.get_local_values(); + let next_values = vars.get_next_values(); + + let local_perm_input = local_values[col_permuted_input]; + let next_perm_table = next_values[col_permuted_table]; + let next_perm_input = next_values[col_permuted_input]; // A "vertical" diff between the local and next permuted inputs. let diff_input_prev = builder.sub_extension(next_perm_input, local_perm_input); diff --git a/evm/src/memory/memory_stark.rs b/evm/src/memory/memory_stark.rs index 36f7566543..fde93be053 100644 --- a/evm/src/memory/memory_stark.rs +++ b/evm/src/memory/memory_stark.rs @@ -7,6 +7,7 @@ use plonky2::field::packed::PackedField; use plonky2::field::polynomial::PolynomialValues; use plonky2::field::types::Field; use plonky2::hash::hash_types::RichField; +use plonky2::iop::ext_target::ExtensionTarget; use plonky2::timed; use plonky2::util::timing::TimingTree; use plonky2::util::transpose; @@ -14,6 +15,7 @@ use plonky2_maybe_rayon::*; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::cross_table_lookup::Column; +use crate::evaluation_frame::{StarkEvaluationFrame, StarkFrame}; use crate::lookup::{eval_lookups, eval_lookups_circuit, permuted_cols}; use crate::memory::columns::{ value_limb, ADDR_CONTEXT, ADDR_SEGMENT, ADDR_VIRTUAL, CONTEXT_FIRST_CHANGE, COUNTER, @@ -23,7 +25,6 @@ use crate::memory::columns::{ use crate::memory::VALUE_LIMBS; use crate::permutation::PermutationPair; use crate::stark::Stark; -use crate::vars::{StarkEvaluationTargets, StarkEvaluationVars}; use crate::witness::memory::MemoryOpKind::Read; use crate::witness::memory::{MemoryAddress, MemoryOp}; @@ -238,48 +239,55 @@ impl, const D: usize> MemoryStark { } impl, const D: usize> Stark for MemoryStark { - const COLUMNS: usize = NUM_COLUMNS; + type EvaluationFrame = StarkFrame + where + FE: FieldExtension, + P: PackedField; + + type EvaluationFrameTarget = StarkFrame, NUM_COLUMNS>; fn eval_packed_generic( &self, - vars: StarkEvaluationVars, + vars: &Self::EvaluationFrame, yield_constr: &mut ConstraintConsumer

, ) where FE: FieldExtension, P: PackedField, { let one = P::from(FE::ONE); - - let timestamp = vars.local_values[TIMESTAMP]; - let addr_context = vars.local_values[ADDR_CONTEXT]; - let addr_segment = vars.local_values[ADDR_SEGMENT]; - let addr_virtual = vars.local_values[ADDR_VIRTUAL]; - let values: Vec<_> = (0..8).map(|i| vars.local_values[value_limb(i)]).collect(); - - let next_timestamp = vars.next_values[TIMESTAMP]; - let next_is_read = vars.next_values[IS_READ]; - let next_addr_context = vars.next_values[ADDR_CONTEXT]; - let next_addr_segment = vars.next_values[ADDR_SEGMENT]; - let next_addr_virtual = vars.next_values[ADDR_VIRTUAL]; - let next_values: Vec<_> = (0..8).map(|i| vars.next_values[value_limb(i)]).collect(); + let local_values = vars.get_local_values(); + let next_values = vars.get_next_values(); + + let timestamp = local_values[TIMESTAMP]; + let addr_context = local_values[ADDR_CONTEXT]; + let addr_segment = local_values[ADDR_SEGMENT]; + let addr_virtual = local_values[ADDR_VIRTUAL]; + let value_limbs: Vec<_> = (0..8).map(|i| local_values[value_limb(i)]).collect(); + + let next_timestamp = next_values[TIMESTAMP]; + let next_is_read = next_values[IS_READ]; + let next_addr_context = next_values[ADDR_CONTEXT]; + let next_addr_segment = next_values[ADDR_SEGMENT]; + let next_addr_virtual = next_values[ADDR_VIRTUAL]; + let next_values_limbs: Vec<_> = (0..8).map(|i| next_values[value_limb(i)]).collect(); // The filter must be 0 or 1. - let filter = vars.local_values[FILTER]; + let filter = local_values[FILTER]; yield_constr.constraint(filter * (filter - P::ONES)); // If this is a dummy row (filter is off), it must be a read. This means the prover can // insert reads which never appear in the CPU trace (which are harmless), but not writes. let is_dummy = P::ONES - filter; - let is_write = P::ONES - vars.local_values[IS_READ]; + let is_write = P::ONES - local_values[IS_READ]; yield_constr.constraint(is_dummy * is_write); - let context_first_change = vars.local_values[CONTEXT_FIRST_CHANGE]; - let segment_first_change = vars.local_values[SEGMENT_FIRST_CHANGE]; - let virtual_first_change = vars.local_values[VIRTUAL_FIRST_CHANGE]; + let context_first_change = local_values[CONTEXT_FIRST_CHANGE]; + let segment_first_change = local_values[SEGMENT_FIRST_CHANGE]; + let virtual_first_change = local_values[VIRTUAL_FIRST_CHANGE]; let address_unchanged = one - context_first_change - segment_first_change - virtual_first_change; - let range_check = vars.local_values[RANGE_CHECK]; + let range_check = local_values[RANGE_CHECK]; let not_context_first_change = one - context_first_change; let not_segment_first_change = one - segment_first_change; @@ -313,7 +321,7 @@ impl, const D: usize> Stark for MemoryStark, const D: usize> Stark for MemoryStark, - vars: StarkEvaluationTargets, + vars: &Self::EvaluationFrameTarget, yield_constr: &mut RecursiveConstraintConsumer, ) { let one = builder.one_extension(); - - let addr_context = vars.local_values[ADDR_CONTEXT]; - let addr_segment = vars.local_values[ADDR_SEGMENT]; - let addr_virtual = vars.local_values[ADDR_VIRTUAL]; - let values: Vec<_> = (0..8).map(|i| vars.local_values[value_limb(i)]).collect(); - let timestamp = vars.local_values[TIMESTAMP]; - - let next_addr_context = vars.next_values[ADDR_CONTEXT]; - let next_addr_segment = vars.next_values[ADDR_SEGMENT]; - let next_addr_virtual = vars.next_values[ADDR_VIRTUAL]; - let next_values: Vec<_> = (0..8).map(|i| vars.next_values[value_limb(i)]).collect(); - let next_is_read = vars.next_values[IS_READ]; - let next_timestamp = vars.next_values[TIMESTAMP]; + let local_values = vars.get_local_values(); + let next_values = vars.get_next_values(); + + let addr_context = local_values[ADDR_CONTEXT]; + let addr_segment = local_values[ADDR_SEGMENT]; + let addr_virtual = local_values[ADDR_VIRTUAL]; + let value_limbs: Vec<_> = (0..8).map(|i| local_values[value_limb(i)]).collect(); + let timestamp = local_values[TIMESTAMP]; + + let next_addr_context = next_values[ADDR_CONTEXT]; + let next_addr_segment = next_values[ADDR_SEGMENT]; + let next_addr_virtual = next_values[ADDR_VIRTUAL]; + let next_values_limbs: Vec<_> = (0..8).map(|i| next_values[value_limb(i)]).collect(); + let next_is_read = next_values[IS_READ]; + let next_timestamp = next_values[TIMESTAMP]; // The filter must be 0 or 1. - let filter = vars.local_values[FILTER]; + let filter = local_values[FILTER]; let constraint = builder.mul_sub_extension(filter, filter, filter); yield_constr.constraint(builder, constraint); // If this is a dummy row (filter is off), it must be a read. This means the prover can // insert reads which never appear in the CPU trace (which are harmless), but not writes. let is_dummy = builder.sub_extension(one, filter); - let is_write = builder.sub_extension(one, vars.local_values[IS_READ]); + let is_write = builder.sub_extension(one, local_values[IS_READ]); let is_dummy_write = builder.mul_extension(is_dummy, is_write); yield_constr.constraint(builder, is_dummy_write); - let context_first_change = vars.local_values[CONTEXT_FIRST_CHANGE]; - let segment_first_change = vars.local_values[SEGMENT_FIRST_CHANGE]; - let virtual_first_change = vars.local_values[VIRTUAL_FIRST_CHANGE]; + let context_first_change = local_values[CONTEXT_FIRST_CHANGE]; + let segment_first_change = local_values[SEGMENT_FIRST_CHANGE]; + let virtual_first_change = local_values[VIRTUAL_FIRST_CHANGE]; let address_unchanged = { let mut cur = builder.sub_extension(one, context_first_change); cur = builder.sub_extension(cur, segment_first_change); builder.sub_extension(cur, virtual_first_change) }; - let range_check = vars.local_values[RANGE_CHECK]; + let range_check = local_values[RANGE_CHECK]; let not_context_first_change = builder.sub_extension(one, context_first_change); let not_segment_first_change = builder.sub_extension(one, segment_first_change); @@ -433,7 +443,7 @@ impl, const D: usize> Stark for MemoryStark( stark: &S, config: &StarkConfig, - vars: StarkEvaluationVars, + vars: &S::EvaluationFrame, permutation_vars: PermutationCheckVars, consumer: &mut ConstraintConsumer

, ) where @@ -335,6 +335,8 @@ pub(crate) fn eval_permutation_checks, S: Stark, { + let local_values = vars.get_local_values(); + let PermutationCheckVars { local_zs, next_zs, @@ -368,7 +370,7 @@ pub(crate) fn eval_permutation_checks, Vec<_>) = column_pairs .iter() - .map(|&(i, j)| (vars.local_values[i], vars.local_values[j])) + .map(|&(i, j)| (local_values[i], local_values[j])) .unzip(); ( factor.reduce_ext(lhs.into_iter()) + FE::from_basefield(*gamma), @@ -392,14 +394,15 @@ pub(crate) fn eval_permutation_checks_circuit( builder: &mut CircuitBuilder, stark: &S, config: &StarkConfig, - vars: StarkEvaluationTargets, + vars: &S::EvaluationFrameTarget, permutation_data: PermutationCheckDataTarget, consumer: &mut RecursiveConstraintConsumer, ) where F: RichField + Extendable, S: Stark, - [(); S::COLUMNS]:, { + let local_values = vars.get_local_values(); + let PermutationCheckDataTarget { local_zs, next_zs, @@ -437,7 +440,7 @@ pub(crate) fn eval_permutation_checks_circuit( let mut factor = ReducingFactorTarget::new(beta_ext); let (lhs, rhs): (Vec<_>, Vec<_>) = column_pairs .iter() - .map(|&(i, j)| (vars.local_values[i], vars.local_values[j])) + .map(|&(i, j)| (local_values[i], local_values[j])) .unzip(); let reduced_lhs = factor.reduce(&lhs, builder); let reduced_rhs = factor.reduce(&rhs, builder); diff --git a/evm/src/prover.rs b/evm/src/prover.rs index 7b960c95a2..d7368c789f 100644 --- a/evm/src/prover.rs +++ b/evm/src/prover.rs @@ -20,20 +20,14 @@ use plonky2_maybe_rayon::*; use plonky2_util::{log2_ceil, log2_strict}; use crate::all_stark::{AllStark, Table, NUM_TABLES}; -use crate::arithmetic::arithmetic_stark::ArithmeticStark; -use crate::byte_packing::byte_packing_stark::BytePackingStark; use crate::config::StarkConfig; use crate::constraint_consumer::ConstraintConsumer; -use crate::cpu::cpu_stark::CpuStark; use crate::cpu::kernel::aggregator::KERNEL; use crate::cross_table_lookup::{cross_table_lookup_data, CtlCheckVars, CtlData}; +use crate::evaluation_frame::StarkEvaluationFrame; use crate::generation::outputs::GenerationOutputs; use crate::generation::{generate_traces, GenerationInputs}; use crate::get_challenges::observe_public_values; -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::{ compute_permutation_z_polys, get_grand_product_challenge_set, get_n_grand_product_challenge_sets, GrandProductChallengeSet, PermutationCheckVars, @@ -41,7 +35,6 @@ use crate::permutation::{ use crate::proof::{AllProof, PublicValues, StarkOpeningSet, StarkProof, StarkProofWithMetadata}; use crate::stark::Stark; use crate::vanishing_poly::eval_vanishing_poly; -use crate::vars::StarkEvaluationVars; /// Generate traces, then create all STARK proofs. pub fn prove( @@ -53,13 +46,6 @@ pub fn prove( where F: RichField + Extendable, C: GenericConfig, - [(); ArithmeticStark::::COLUMNS]:, - [(); BytePackingStark::::COLUMNS]:, - [(); CpuStark::::COLUMNS]:, - [(); KeccakStark::::COLUMNS]:, - [(); KeccakSpongeStark::::COLUMNS]:, - [(); LogicStark::::COLUMNS]:, - [(); MemoryStark::::COLUMNS]:, { let (proof, _outputs) = prove_with_outputs(all_stark, config, inputs, timing)?; Ok(proof) @@ -76,13 +62,6 @@ pub fn prove_with_outputs( where F: RichField + Extendable, C: GenericConfig, - [(); ArithmeticStark::::COLUMNS]:, - [(); BytePackingStark::::COLUMNS]:, - [(); CpuStark::::COLUMNS]:, - [(); KeccakStark::::COLUMNS]:, - [(); KeccakSpongeStark::::COLUMNS]:, - [(); LogicStark::::COLUMNS]:, - [(); MemoryStark::::COLUMNS]:, { timed!(timing, "build kernel", Lazy::force(&KERNEL)); let (traces, public_values, outputs) = timed!( @@ -105,13 +84,6 @@ pub(crate) fn prove_with_traces( where F: RichField + Extendable, C: GenericConfig, - [(); ArithmeticStark::::COLUMNS]:, - [(); BytePackingStark::::COLUMNS]:, - [(); CpuStark::::COLUMNS]:, - [(); KeccakStark::::COLUMNS]:, - [(); KeccakSpongeStark::::COLUMNS]:, - [(); LogicStark::::COLUMNS]:, - [(); MemoryStark::::COLUMNS]:, { let rate_bits = config.fri_config.rate_bits; let cap_height = config.fri_config.cap_height; @@ -197,13 +169,6 @@ fn prove_with_commitments( where F: RichField + Extendable, C: GenericConfig, - [(); ArithmeticStark::::COLUMNS]:, - [(); BytePackingStark::::COLUMNS]:, - [(); CpuStark::::COLUMNS]:, - [(); KeccakStark::::COLUMNS]:, - [(); KeccakSpongeStark::::COLUMNS]:, - [(); LogicStark::::COLUMNS]:, - [(); MemoryStark::::COLUMNS]:, { let arithmetic_proof = timed!( timing, @@ -322,7 +287,6 @@ where F: RichField + Extendable, C: GenericConfig, S: Stark, - [(); S::COLUMNS]:, { let degree = trace_poly_values[0].len(); let degree_bits = log2_strict(degree); @@ -507,7 +471,6 @@ where P: PackedField, C: GenericConfig, S: Stark, - [(); S::COLUMNS]:, { let degree = 1 << degree_bits; let rate_bits = config.fri_config.rate_bits; @@ -530,12 +493,8 @@ where let z_h_on_coset = ZeroPolyOnCoset::::new(degree_bits, quotient_degree_bits); // Retrieve the LDE values at index `i`. - let get_trace_values_packed = |i_start| -> [P; S::COLUMNS] { - trace_commitment - .get_lde_values_packed(i_start, step) - .try_into() - .unwrap() - }; + let get_trace_values_packed = + |i_start| -> Vec

{ trace_commitment.get_lde_values_packed(i_start, step) }; // Last element of the subgroup. let last = F::primitive_root_of_unity(degree_bits).inverse(); @@ -566,10 +525,10 @@ where lagrange_basis_first, lagrange_basis_last, ); - let vars = StarkEvaluationVars { - local_values: &get_trace_values_packed(i_start), - next_values: &get_trace_values_packed(i_next_start), - }; + let vars = S::EvaluationFrame::from_values( + &get_trace_values_packed(i_start), + &get_trace_values_packed(i_next_start), + ); let permutation_check_vars = permutation_challenges.map(|permutation_challenge_sets| PermutationCheckVars { local_zs: permutation_ctl_zs_commitment.get_lde_values_packed(i_start, step) @@ -597,7 +556,7 @@ where eval_vanishing_poly::( stark, config, - vars, + &vars, permutation_check_vars, &ctl_vars, &mut consumer, @@ -642,7 +601,6 @@ fn check_constraints<'a, F, C, S, const D: usize>( F: RichField + Extendable, C: GenericConfig, S: Stark, - [(); S::COLUMNS]:, { let degree = 1 << degree_bits; let rate_bits = 0; // Set this to higher value to check constraint degree. @@ -688,10 +646,10 @@ fn check_constraints<'a, F, C, S, const D: usize>( lagrange_basis_first, lagrange_basis_last, ); - let vars = StarkEvaluationVars { - local_values: trace_subgroup_evals[i].as_slice().try_into().unwrap(), - next_values: trace_subgroup_evals[i_next].as_slice().try_into().unwrap(), - }; + let vars = S::EvaluationFrame::from_values( + &trace_subgroup_evals[i], + &trace_subgroup_evals[i_next], + ); let permutation_check_vars = permutation_challenges.map(|permutation_challenge_sets| PermutationCheckVars { local_zs: permutation_ctl_zs_subgroup_evals[i][..num_permutation_zs].to_vec(), @@ -715,7 +673,7 @@ fn check_constraints<'a, F, C, S, const D: usize>( eval_vanishing_poly::( stark, config, - vars, + &vars, permutation_check_vars, &ctl_vars, &mut consumer, diff --git a/evm/src/recursive_verifier.rs b/evm/src/recursive_verifier.rs index 76a923385c..f4e76c3931 100644 --- a/evm/src/recursive_verifier.rs +++ b/evm/src/recursive_verifier.rs @@ -30,6 +30,7 @@ use crate::config::StarkConfig; use crate::constraint_consumer::RecursiveConstraintConsumer; use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; use crate::cross_table_lookup::{verify_cross_table_lookups, CrossTableLookup, CtlCheckVarsTarget}; +use crate::evaluation_frame::StarkEvaluationFrame; use crate::memory::segments::Segment; use crate::memory::VALUE_LIMBS; use crate::permutation::{ @@ -45,7 +46,6 @@ use crate::proof::{ use crate::stark::Stark; 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`. @@ -297,7 +297,6 @@ pub(crate) fn recursive_stark_circuit< min_degree_bits: usize, ) -> StarkWrapperCircuit where - [(); S::COLUMNS]:, C::Hasher: AlgebraicHasher, { let mut builder = CircuitBuilder::::new(circuit_config.clone()); @@ -405,7 +404,6 @@ fn verify_stark_proof_with_challenges_circuit< inner_config: &StarkConfig, ) where C::Hasher: AlgebraicHasher, - [(); S::COLUMNS]:, { let zero = builder.zero(); let one = builder.one_extension(); @@ -418,10 +416,7 @@ fn verify_stark_proof_with_challenges_circuit< ctl_zs_first, quotient_polys, } = &proof.openings; - let vars = StarkEvaluationTargets { - local_values: &local_values.to_vec().try_into().unwrap(), - next_values: &next_values.to_vec().try_into().unwrap(), - }; + let vars = S::EvaluationFrameTarget::from_values(local_values, next_values); let degree_bits = proof.recover_degree_bits(inner_config); let zeta_pow_deg = builder.exp_power_of_2_extension(challenges.stark_zeta, degree_bits); @@ -456,7 +451,7 @@ fn verify_stark_proof_with_challenges_circuit< builder, stark, inner_config, - vars, + &vars, permutation_data, ctl_vars, &mut consumer, diff --git a/evm/src/stark.rs b/evm/src/stark.rs index 73b51ada41..b3ea818f9b 100644 --- a/evm/src/stark.rs +++ b/evm/src/stark.rs @@ -12,8 +12,8 @@ use plonky2_util::ceil_div_usize; use crate::config::StarkConfig; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; +use crate::evaluation_frame::StarkEvaluationFrame; use crate::permutation::PermutationPair; -use crate::vars::{StarkEvaluationTargets, StarkEvaluationVars}; const TRACE_ORACLE_INDEX: usize = 0; const PERMUTATION_CTL_ORACLE_INDEX: usize = 1; @@ -22,7 +22,16 @@ const QUOTIENT_ORACLE_INDEX: usize = 2; /// Represents a STARK system. pub trait Stark, const D: usize>: Sync { /// The total number of columns in the trace. - const COLUMNS: usize; + const COLUMNS: usize = Self::EvaluationFrameTarget::COLUMNS; + + /// This is used to evaluate constraints natively. + type EvaluationFrame: StarkEvaluationFrame

+ where + FE: FieldExtension, + P: PackedField; + + /// The `Target` version of `Self::EvaluationFrame`, used to evaluate constraints recursively. + type EvaluationFrameTarget: StarkEvaluationFrame>; /// Evaluate constraints at a vector of points. /// @@ -32,7 +41,7 @@ pub trait Stark, const D: usize>: Sync { /// constraints over `F`. fn eval_packed_generic( &self, - vars: StarkEvaluationVars, + vars: &Self::EvaluationFrame, yield_constr: &mut ConstraintConsumer

, ) where FE: FieldExtension, @@ -41,7 +50,7 @@ pub trait Stark, const D: usize>: Sync { /// Evaluate constraints at a vector of points from the base field `F`. fn eval_packed_base>( &self, - vars: StarkEvaluationVars, + vars: &Self::EvaluationFrame, yield_constr: &mut ConstraintConsumer

, ) { self.eval_packed_generic(vars, yield_constr) @@ -50,7 +59,7 @@ pub trait Stark, const D: usize>: Sync { /// Evaluate constraints at a single point from the degree `D` extension field. fn eval_ext( &self, - vars: StarkEvaluationVars, + vars: &Self::EvaluationFrame, yield_constr: &mut ConstraintConsumer, ) { self.eval_packed_generic(vars, yield_constr) @@ -63,7 +72,7 @@ pub trait Stark, const D: usize>: Sync { fn eval_ext_circuit( &self, builder: &mut CircuitBuilder, - vars: StarkEvaluationTargets, + vars: &Self::EvaluationFrameTarget, yield_constr: &mut RecursiveConstraintConsumer, ); diff --git a/evm/src/stark_testing.rs b/evm/src/stark_testing.rs index e005d2ead9..5fe44127f9 100644 --- a/evm/src/stark_testing.rs +++ b/evm/src/stark_testing.rs @@ -3,17 +3,16 @@ use plonky2::field::extension::{Extendable, FieldExtension}; use plonky2::field::polynomial::{PolynomialCoeffs, PolynomialValues}; use plonky2::field::types::{Field, Sample}; use plonky2::hash::hash_types::RichField; -use plonky2::hash::hashing::PlonkyPermutation; use plonky2::iop::witness::{PartialWitness, WitnessWrite}; use plonky2::plonk::circuit_builder::CircuitBuilder; use plonky2::plonk::circuit_data::CircuitConfig; -use plonky2::plonk::config::{GenericConfig, Hasher}; +use plonky2::plonk::config::GenericConfig; use plonky2::util::transpose; use plonky2_util::{log2_ceil, log2_strict}; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; +use crate::evaluation_frame::StarkEvaluationFrame; use crate::stark::Stark; -use crate::vars::{StarkEvaluationTargets, StarkEvaluationVars}; const WITNESS_SIZE: usize = 1 << 5; @@ -21,10 +20,7 @@ const WITNESS_SIZE: usize = 1 << 5; /// low-degree witness polynomials. pub fn test_stark_low_degree, S: Stark, const D: usize>( stark: S, -) -> Result<()> -where - [(); S::COLUMNS]:, -{ +) -> Result<()> { let rate_bits = log2_ceil(stark.constraint_degree() + 1); let trace_ldes = random_low_degree_matrix::(S::COLUMNS, rate_bits); @@ -39,13 +35,10 @@ where let alpha = F::rand(); let constraint_evals = (0..size) .map(|i| { - let vars = StarkEvaluationVars { - local_values: &trace_ldes[i].clone().try_into().unwrap(), - next_values: &trace_ldes[(i + (1 << rate_bits)) % size] - .clone() - .try_into() - .unwrap(), - }; + let vars = S::EvaluationFrame::from_values( + &trace_ldes[i], + &trace_ldes[(i + (1 << rate_bits)) % size], + ); let mut consumer = ConstraintConsumer::::new( vec![alpha], @@ -53,7 +46,7 @@ where lagrange_first.values[i], lagrange_last.values[i], ); - stark.eval_packed_base(vars, &mut consumer); + stark.eval_packed_base(&vars, &mut consumer); consumer.accumulators()[0] }) .collect::>(); @@ -84,17 +77,13 @@ pub fn test_stark_circuit_constraints< const D: usize, >( stark: S, -) -> Result<()> -where - [(); S::COLUMNS]:, - [(); >::Permutation::WIDTH]:, - [(); >::Permutation::WIDTH]:, -{ +) -> Result<()> { // Compute native constraint evaluation on random values. - let vars = StarkEvaluationVars { - local_values: &F::Extension::rand_array::<{ S::COLUMNS }>(), - next_values: &F::Extension::rand_array::<{ S::COLUMNS }>(), - }; + let vars = S::EvaluationFrame::from_values( + &F::Extension::rand_vec(S::COLUMNS), + &F::Extension::rand_vec(S::COLUMNS), + ); + let alphas = F::rand_vec(1); let z_last = F::Extension::rand(); let lagrange_first = F::Extension::rand(); @@ -109,7 +98,7 @@ where lagrange_first, lagrange_last, ); - stark.eval_ext(vars, &mut consumer); + stark.eval_ext(&vars, &mut consumer); let native_eval = consumer.accumulators()[0]; // Compute circuit constraint evaluation on same random values. @@ -118,9 +107,9 @@ where let mut pw = PartialWitness::::new(); let locals_t = builder.add_virtual_extension_targets(S::COLUMNS); - pw.set_extension_targets(&locals_t, vars.local_values); + pw.set_extension_targets(&locals_t, vars.get_local_values()); let nexts_t = builder.add_virtual_extension_targets(S::COLUMNS); - pw.set_extension_targets(&nexts_t, vars.next_values); + pw.set_extension_targets(&nexts_t, vars.get_next_values()); let alphas_t = builder.add_virtual_targets(1); pw.set_target(alphas_t[0], alphas[0]); let z_last_t = builder.add_virtual_extension_target(); @@ -130,10 +119,7 @@ where let lagrange_last_t = builder.add_virtual_extension_target(); pw.set_extension_target(lagrange_last_t, lagrange_last); - let vars = StarkEvaluationTargets:: { - local_values: &locals_t.try_into().unwrap(), - next_values: &nexts_t.try_into().unwrap(), - }; + let vars = S::EvaluationFrameTarget::from_values(&locals_t, &nexts_t); let mut consumer = RecursiveConstraintConsumer::::new( builder.zero_extension(), alphas_t, @@ -141,7 +127,7 @@ where lagrange_first_t, lagrange_last_t, ); - stark.eval_ext_circuit(&mut builder, vars, &mut consumer); + stark.eval_ext_circuit(&mut builder, &vars, &mut consumer); let circuit_eval = consumer.accumulators()[0]; let native_eval_t = builder.constant_extension(native_eval); builder.connect_extension(circuit_eval, native_eval_t); diff --git a/evm/src/vanishing_poly.rs b/evm/src/vanishing_poly.rs index 3a2da78c53..6c88b16e43 100644 --- a/evm/src/vanishing_poly.rs +++ b/evm/src/vanishing_poly.rs @@ -14,12 +14,11 @@ use crate::permutation::{ PermutationCheckVars, }; use crate::stark::Stark; -use crate::vars::{StarkEvaluationTargets, StarkEvaluationVars}; pub(crate) fn eval_vanishing_poly( stark: &S, config: &StarkConfig, - vars: StarkEvaluationVars, + vars: &S::EvaluationFrame, permutation_vars: Option>, ctl_vars: &[CtlCheckVars], consumer: &mut ConstraintConsumer

, @@ -46,14 +45,13 @@ pub(crate) fn eval_vanishing_poly_circuit( builder: &mut CircuitBuilder, stark: &S, config: &StarkConfig, - vars: StarkEvaluationTargets, + vars: &S::EvaluationFrameTarget, permutation_data: Option>, ctl_vars: &[CtlCheckVarsTarget], consumer: &mut RecursiveConstraintConsumer, ) where F: RichField + Extendable, S: Stark, - [(); S::COLUMNS]:, { stark.eval_ext_circuit(builder, vars, consumer); if let Some(permutation_data) = permutation_data { diff --git a/evm/src/vars.rs b/evm/src/vars.rs deleted file mode 100644 index 6c82675c33..0000000000 --- a/evm/src/vars.rs +++ /dev/null @@ -1,19 +0,0 @@ -use plonky2::field::packed::PackedField; -use plonky2::field::types::Field; -use plonky2::iop::ext_target::ExtensionTarget; - -#[derive(Debug, Copy, Clone)] -pub struct StarkEvaluationVars<'a, F, P, const COLUMNS: usize> -where - F: Field, - P: PackedField, -{ - pub local_values: &'a [P; COLUMNS], - pub next_values: &'a [P; COLUMNS], -} - -#[derive(Debug, Copy, Clone)] -pub struct StarkEvaluationTargets<'a, const D: usize, const COLUMNS: usize> { - pub local_values: &'a [ExtensionTarget; COLUMNS], - pub next_values: &'a [ExtensionTarget; COLUMNS], -} diff --git a/evm/src/verifier.rs b/evm/src/verifier.rs index 11f8155d29..c7b58060fb 100644 --- a/evm/src/verifier.rs +++ b/evm/src/verifier.rs @@ -11,17 +11,11 @@ use plonky2::plonk::config::GenericConfig; use plonky2::plonk::plonk_common::reduce_with_powers; use crate::all_stark::{AllStark, Table, NUM_TABLES}; -use crate::arithmetic::arithmetic_stark::ArithmeticStark; -use crate::byte_packing::byte_packing_stark::BytePackingStark; 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::evaluation_frame::StarkEvaluationFrame; use crate::memory::segments::Segment; use crate::memory::VALUE_LIMBS; use crate::permutation::{GrandProductChallenge, PermutationCheckVars}; @@ -31,7 +25,6 @@ use crate::proof::{ use crate::stark::Stark; use crate::util::h2u; use crate::vanishing_poly::eval_vanishing_poly; -use crate::vars::StarkEvaluationVars; pub fn verify_proof, C: GenericConfig, const D: usize>( all_stark: &AllStark, @@ -39,13 +32,6 @@ pub fn verify_proof, C: GenericConfig, co config: &StarkConfig, ) -> Result<()> where - [(); ArithmeticStark::::COLUMNS]:, - [(); BytePackingStark::::COLUMNS]:, - [(); CpuStark::::COLUMNS]:, - [(); KeccakStark::::COLUMNS]:, - [(); KeccakSpongeStark::::COLUMNS]:, - [(); LogicStark::::COLUMNS]:, - [(); MemoryStark::::COLUMNS]:, { let AllProofChallenges { stark_challenges, @@ -301,10 +287,7 @@ pub(crate) fn verify_stark_proof_with_challenges< challenges: &StarkProofChallenges, ctl_vars: &[CtlCheckVars], config: &StarkConfig, -) -> Result<()> -where - [(); S::COLUMNS]:, -{ +) -> Result<()> { log::debug!("Checking proof: {}", type_name::()); validate_proof_shape(stark, proof, config, ctl_vars.len())?; let StarkOpeningSet { @@ -315,10 +298,7 @@ where ctl_zs_first, quotient_polys, } = &proof.openings; - let vars = StarkEvaluationVars { - local_values: &local_values.to_vec().try_into().unwrap(), - next_values: &next_values.to_vec().try_into().unwrap(), - }; + let vars = S::EvaluationFrame::from_values(local_values, next_values); let degree_bits = proof.recover_degree_bits(config); let (l_0, l_last) = eval_l_0_and_l_last(degree_bits, challenges.stark_zeta); @@ -343,7 +323,7 @@ where eval_vanishing_poly::( stark, config, - vars, + &vars, permutation_data, ctl_vars, &mut consumer, @@ -401,7 +381,6 @@ where F: RichField + Extendable, C: GenericConfig, S: Stark, - [(); S::COLUMNS]:, { let StarkProof { trace_cap, From 0abc3b92104893bb0f725178ad35f6bb94098f35 Mon Sep 17 00:00:00 2001 From: Robin Salen <30937548+Nashtare@users.noreply.github.com> Date: Fri, 22 Sep 2023 10:14:47 -0400 Subject: [PATCH 19/19] Apply comments (#1248) --- evm/src/cpu/cpu_stark.rs | 16 ++++++---------- evm/src/keccak_sponge/keccak_sponge_stark.rs | 20 ++++++++------------ 2 files changed, 14 insertions(+), 22 deletions(-) diff --git a/evm/src/cpu/cpu_stark.rs b/evm/src/cpu/cpu_stark.rs index 14bb6015fc..f23ff308b6 100644 --- a/evm/src/cpu/cpu_stark.rs +++ b/evm/src/cpu/cpu_stark.rs @@ -243,11 +243,9 @@ impl, const D: usize> Stark for CpuStark, P: PackedField, { - let local_values = - TryInto::<[P; NUM_CPU_COLUMNS]>::try_into(vars.get_local_values()).unwrap(); + let local_values: &[P; NUM_CPU_COLUMNS] = vars.get_local_values().try_into().unwrap(); let local_values: &CpuColumnsView

= local_values.borrow(); - let next_values = - TryInto::<[P; NUM_CPU_COLUMNS]>::try_into(vars.get_next_values()).unwrap(); + let next_values: &[P; NUM_CPU_COLUMNS] = vars.get_next_values().try_into().unwrap(); let next_values: &CpuColumnsView

= next_values.borrow(); bootstrap_kernel::eval_bootstrap_kernel_packed(local_values, next_values, yield_constr); @@ -276,13 +274,11 @@ impl, const D: usize> Stark for CpuStark, ) { - let local_values = - TryInto::<[ExtensionTarget; NUM_CPU_COLUMNS]>::try_into(vars.get_local_values()) - .unwrap(); + let local_values: &[ExtensionTarget; NUM_CPU_COLUMNS] = + vars.get_local_values().try_into().unwrap(); let local_values: &CpuColumnsView> = local_values.borrow(); - let next_values = - TryInto::<[ExtensionTarget; NUM_CPU_COLUMNS]>::try_into(vars.get_next_values()) - .unwrap(); + let next_values: &[ExtensionTarget; NUM_CPU_COLUMNS] = + vars.get_next_values().try_into().unwrap(); let next_values: &CpuColumnsView> = next_values.borrow(); bootstrap_kernel::eval_bootstrap_kernel_ext_circuit( diff --git a/evm/src/keccak_sponge/keccak_sponge_stark.rs b/evm/src/keccak_sponge/keccak_sponge_stark.rs index 65edc94102..2ed31c1fee 100644 --- a/evm/src/keccak_sponge/keccak_sponge_stark.rs +++ b/evm/src/keccak_sponge/keccak_sponge_stark.rs @@ -438,11 +438,11 @@ impl, const D: usize> Stark for KeccakSpongeS FE: FieldExtension, P: PackedField, { - let local_values = - TryInto::<[P; NUM_KECCAK_SPONGE_COLUMNS]>::try_into(vars.get_local_values()).unwrap(); + let local_values: &[P; NUM_KECCAK_SPONGE_COLUMNS] = + vars.get_local_values().try_into().unwrap(); let local_values: &KeccakSpongeColumnsView

= local_values.borrow(); - let next_values = - TryInto::<[P; NUM_KECCAK_SPONGE_COLUMNS]>::try_into(vars.get_next_values()).unwrap(); + let next_values: &[P; NUM_KECCAK_SPONGE_COLUMNS] = + vars.get_next_values().try_into().unwrap(); let next_values: &KeccakSpongeColumnsView

= next_values.borrow(); // Each flag (full-input block, final block or implied dummy flag) must be boolean. @@ -549,15 +549,11 @@ impl, const D: usize> Stark for KeccakSpongeS vars: &Self::EvaluationFrameTarget, yield_constr: &mut RecursiveConstraintConsumer, ) { - let local_values = TryInto::<[ExtensionTarget; NUM_KECCAK_SPONGE_COLUMNS]>::try_into( - vars.get_local_values(), - ) - .unwrap(); + let local_values: &[ExtensionTarget; NUM_KECCAK_SPONGE_COLUMNS] = + vars.get_local_values().try_into().unwrap(); let local_values: &KeccakSpongeColumnsView> = local_values.borrow(); - let next_values = TryInto::<[ExtensionTarget; NUM_KECCAK_SPONGE_COLUMNS]>::try_into( - vars.get_next_values(), - ) - .unwrap(); + let next_values: &[ExtensionTarget; NUM_KECCAK_SPONGE_COLUMNS] = + vars.get_next_values().try_into().unwrap(); let next_values: &KeccakSpongeColumnsView> = next_values.borrow(); let one = builder.one_extension();