diff --git a/circuit-benchmarks/src/bit_keccak.rs b/circuit-benchmarks/src/bit_keccak.rs index d3f1299b25..c0cf00fb7e 100644 --- a/circuit-benchmarks/src/bit_keccak.rs +++ b/circuit-benchmarks/src/bit_keccak.rs @@ -28,12 +28,11 @@ mod tests { .parse() .expect("Cannot parse DEGREE env var as u32"); - // Create the circuit. Leave last dozens of rows for blinding. - let mut circuit = KeccakBitCircuit::new(2usize.pow(degree) - 64); - // Use the complete circuit - let inputs = vec![(0u8..135).collect::>(); circuit.capacity()]; - circuit.generate_witness(&inputs); + let inputs = vec![(0u8..135).collect::>(); 3]; + + // Create the circuit. Leave last dozens of rows for blinding. + let circuit = KeccakBitCircuit::new(2usize.pow(degree) - 64, inputs.to_vec()); // Initialize the polynomial commitment parameters let mut rng = XorShiftRng::from_seed([ diff --git a/circuit-benchmarks/src/packed_keccak.rs b/circuit-benchmarks/src/packed_keccak.rs index 5e252ebb36..62ec4ca687 100644 --- a/circuit-benchmarks/src/packed_keccak.rs +++ b/circuit-benchmarks/src/packed_keccak.rs @@ -27,12 +27,11 @@ mod tests { .parse() .expect("Cannot parse DEGREE env var as u32"); - // Create the circuit. Leave last dozens of rows for blinding. - let mut circuit = KeccakPackedCircuit::new(2usize.pow(degree) - 64); - // Use the complete circuit - let inputs = vec![(0u8..135).collect::>(); circuit.capacity()]; - circuit.generate_witness(&inputs); + let inputs = vec![(0u8..135).collect::>(); 3]; + + // Create the circuit. Leave last dozens of rows for blinding. + let circuit = KeccakPackedCircuit::new(2usize.pow(degree) - 64, inputs.as_slice()); // Initialize the polynomial commitment parameters let mut rng = XorShiftRng::from_seed([ diff --git a/circuit-benchmarks/src/packed_multi_keccak.rs b/circuit-benchmarks/src/packed_multi_keccak.rs index 04653b2c0d..aace41d96e 100644 --- a/circuit-benchmarks/src/packed_multi_keccak.rs +++ b/circuit-benchmarks/src/packed_multi_keccak.rs @@ -27,12 +27,11 @@ mod tests { .parse() .expect("Cannot parse DEGREE env var as u32"); - // Create the circuit. Leave last dozens of rows for blinding. - let mut circuit = KeccakPackedCircuit::new(2usize.pow(degree) - 64); - // Use the complete circuit - let inputs = vec![(0u8..135).collect::>(); circuit.capacity()]; - circuit.generate_witness(&inputs); + let inputs = vec![(0u8..135).collect::>(); 3]; + + // Create the circuit. Leave last dozens of rows for blinding. + let circuit = KeccakPackedCircuit::new(2usize.pow(degree) - 64, inputs.to_vec()); // Initialize the polynomial commitment parameters let mut rng = XorShiftRng::from_seed([ diff --git a/zkevm-circuits/src/evm_circuit/util.rs b/zkevm-circuits/src/evm_circuit/util.rs index 20c0ca1770..e204ab6cde 100644 --- a/zkevm-circuits/src/evm_circuit/util.rs +++ b/zkevm-circuits/src/evm_circuit/util.rs @@ -349,7 +349,6 @@ pub(crate) struct RandomLinearCombination { impl RandomLinearCombination { const N_BYTES: usize = N; - // TODO: replace `bytes` type by a reference pub(crate) fn random_linear_combine(bytes: [u8; N], randomness: F) -> F { rlc::value(&bytes, randomness) } diff --git a/zkevm-circuits/src/keccak_circuit/keccak_bit.rs b/zkevm-circuits/src/keccak_circuit/keccak_bit.rs index 4000424c75..0df5741729 100644 --- a/zkevm-circuits/src/keccak_circuit/keccak_bit.rs +++ b/zkevm-circuits/src/keccak_circuit/keccak_bit.rs @@ -9,15 +9,15 @@ use crate::{ NUM_WORDS_TO_ABSORB, RATE, RATE_IN_BITS, RHO_MATRIX, }, table::KeccakTable, - util::Expr, + util::{Challenges, Expr}, }; use eth_types::Field; use gadgets::util::{and, select, sum, xor}; use halo2_proofs::{ circuit::{Layouter, Region, SimpleFloorPlanner, Value}, plonk::{ - Advice, Circuit, Column, ConstraintSystem, Error, Expression, Fixed, TableColumn, - VirtualCells, + Advice, Challenge, Circuit, Column, ConstraintSystem, Error, Expression, Fixed, + SecondPhase, TableColumn, VirtualCells, }, poly::Rotation, }; @@ -44,19 +44,19 @@ fn get_num_bits_per_theta_lookup() -> usize { num_bits as usize } -#[derive(Clone, Debug, PartialEq)] -struct KeccakRow { +#[derive(Clone, Debug)] +pub(crate) struct KeccakRow { q_padding: bool, q_padding_last: bool, state: [u8; KECCAK_WIDTH_IN_BITS], theta_c: [u8; THETA_C_WIDTH], input: [u8; ABSORB_WIDTH_PER_ROW], is_paddings: [bool; ABSORB_WIDTH_PER_ROW_BYTES], - data_rlcs: [F; ABSORB_WIDTH_PER_ROW_BYTES], + data_rlcs: [Value; ABSORB_WIDTH_PER_ROW_BYTES], is_final: bool, length: usize, - data_rlc: F, - hash_rlc: F, + data_rlc: Value, + hash_rlc: Value, } /// KeccakBitConfig @@ -83,19 +83,13 @@ pub struct KeccakBitConfig { /// KeccakBitCircuit #[derive(Default)] pub struct KeccakBitCircuit { - witness: Vec>, + witness_inputs: Vec>, num_rows: usize, _marker: PhantomData, } -impl KeccakBitCircuit { - fn r() -> F { - F::from(123456) - } -} - impl Circuit for KeccakBitCircuit { - type Config = KeccakBitConfig; + type Config = (KeccakBitConfig, Challenges); type FloorPlanner = SimpleFloorPlanner; fn without_witnesses(&self) -> Self { @@ -103,7 +97,12 @@ impl Circuit for KeccakBitCircuit { } fn configure(meta: &mut ConstraintSystem) -> Self::Config { - KeccakBitConfig::configure(meta, Expression::Constant(KeccakBitCircuit::r())) + let challenges = Challenges::construct(meta); + let challenge_exprs = challenges.exprs(meta); + ( + KeccakBitConfig::configure(meta, challenge_exprs), + challenges, + ) } fn synthesize( @@ -111,17 +110,18 @@ impl Circuit for KeccakBitCircuit { config: Self::Config, mut layouter: impl Layouter, ) -> Result<(), Error> { - config.load(&mut layouter)?; - config.assign(&mut layouter, &self.witness)?; + config.0.load(&mut layouter)?; + let witness = self.generate_witness(config.1.values(&mut layouter)); + config.0.assign(&mut layouter, witness)?; Ok(()) } } impl KeccakBitCircuit { /// Creates a new circuit instance - pub fn new(num_rows: usize) -> Self { + pub fn new(num_rows: usize, witness_inputs: Vec>) -> Self { KeccakBitCircuit { - witness: Vec::new(), + witness_inputs, num_rows, _marker: PhantomData, } @@ -134,14 +134,17 @@ impl KeccakBitCircuit { } /// Sets the witness using the data to be hashed - pub fn generate_witness(&mut self, inputs: &[Vec]) { - self.witness = multi_keccak(inputs, KeccakBitCircuit::r(), Some(self.capacity())) - .expect("Too many inputs for given capacity"); + pub(crate) fn generate_witness(&self, challenges: Challenges>) -> Vec> { + multi_keccak(&self.witness_inputs, challenges, Some(self.capacity())) + .expect("Too many inputs for given capacity") } } impl KeccakBitConfig { - pub(crate) fn configure(meta: &mut ConstraintSystem, r: Expression) -> Self { + pub(crate) fn configure( + meta: &mut ConstraintSystem, + challenges: Challenges>, + ) -> Self { let num_bits_per_theta_lookup = get_num_bits_per_theta_lookup(); info!("num_bits_per_theta_lookup: {}", num_bits_per_theta_lookup); @@ -162,7 +165,7 @@ impl KeccakBitConfig { let theta_c = array_init::array_init(|_| meta.advice_column()); let input = array_init::array_init(|_| meta.advice_column()); let is_paddings = array_init::array_init(|_| meta.advice_column()); - let data_rlcs = array_init::array_init(|_| meta.advice_column()); + let data_rlcs = array_init::array_init(|_| meta.advice_column_in(SecondPhase)); let round_cst = array_init::array_init(|_| meta.fixed_column()); let mut theta_c_table = Vec::new(); @@ -379,7 +382,7 @@ impl KeccakBitConfig { .flat_map(|a| to_bytes::expr(&a[0])) .rev() .collect::>(); - let rlc = compose_rlc::expr(&hash_bytes_le, r.clone()); + let rlc = compose_rlc::expr(&hash_bytes_le, challenges.evm_word()); cb.condition(start_new_hash(meta, Rotation::cur()), |cb| { cb.require_equal( "hash rlc check", @@ -547,7 +550,7 @@ impl KeccakBitConfig { new_data_rlc = select::expr( meta.query_advice(*is_padding, Rotation::cur()), new_data_rlc.clone(), - new_data_rlc.clone() * r.clone() + byte.clone(), + new_data_rlc.clone() * challenges.keccak_input() + byte.clone(), ); if idx < data_rlcs.len() - 1 { let next_data_rlc = meta.query_advice(data_rlcs[idx + 1], Rotation::cur()); @@ -607,17 +610,17 @@ impl KeccakBitConfig { &self, layouter: &mut impl Layouter, inputs: &[Vec], - r: F, + challenges: Challenges>, capacity: Option, ) -> Result<(), Error> { - let witness = multi_keccak(inputs, r, capacity)?; - self.assign(layouter, &witness) + let witness = multi_keccak(inputs, challenges, capacity)?; + self.assign(layouter, witness) } fn assign( &self, layouter: &mut impl Layouter, - witness: &[KeccakRow], + witness: Vec>, ) -> Result<(), Error> { layouter.assign_region( || "assign keccak rounds", @@ -663,9 +666,9 @@ impl KeccakBitConfig { region, offset, [ - F::from(row.is_final), + Value::known(F::from(row.is_final)), row.data_rlc, - F::from(row.length as u64), + Value::known(F::from(row.length as u64)), row.hash_rlc, ], )?; @@ -722,7 +725,7 @@ impl KeccakBitConfig { || format!("assign padding selector {} {}", idx, offset), *column, offset, - || Value::known(*data_rlc), + || *data_rlc, )?; } @@ -776,7 +779,7 @@ impl KeccakBitConfig { } } -fn keccak(rows: &mut Vec>, bytes: &[u8], r: F) { +fn keccak(rows: &mut Vec>, bytes: &[u8], challenges: Challenges>) { let mut bits = into_bits(bytes); let mut s = [[[0u8; NUM_BITS_PER_WORD]; 5]; 5]; let absorb_positions = get_absorb_positions(); @@ -790,7 +793,7 @@ fn keccak(rows: &mut Vec>, bytes: &[u8], r: F) { bits.push(1); let mut length = 0usize; - let mut data_rlc = F::zero(); + let mut data_rlc = Value::known(F::zero()); let chunks = bits.chunks(RATE_IN_BITS); let num_chunks = chunks.len(); for (idx, chunk) in chunks.enumerate() { @@ -860,7 +863,7 @@ fn keccak(rows: &mut Vec>, bytes: &[u8], r: F) { // Padding/Length/Data rlc let mut is_paddings = [false; ABSORB_WIDTH_PER_ROW_BYTES]; - let mut data_rlcs = [F::zero(); ABSORB_WIDTH_PER_ROW_BYTES]; + let mut data_rlcs = [Value::known(F::zero()); ABSORB_WIDTH_PER_ROW_BYTES]; if round < NUM_WORDS_TO_ABSORB { for (padding_idx, is_padding) in is_paddings.iter_mut().enumerate() { let byte_idx = round * 8 + padding_idx; @@ -878,7 +881,7 @@ fn keccak(rows: &mut Vec>, bytes: &[u8], r: F) { { if !*padding { let byte_value: F = pack_with_base(byte_bits, 2); - data_rlc = data_rlc * r + byte_value; + data_rlc = data_rlc * challenges.keccak_input() + Value::known(byte_value); } if idx < data_rlcs.len() - 1 { data_rlcs[idx + 1] = data_rlc; @@ -897,9 +900,11 @@ fn keccak(rows: &mut Vec>, bytes: &[u8], r: F) { .flat_map(|a| to_bytes::value(&a[0])) .rev() .collect::>(); - rlc::value(&hash_bytes_le, r) + challenges + .evm_word() + .map(|challenge_value| rlc::value(&hash_bytes_le, challenge_value)) } else { - F::zero() + Value::known(F::zero()) }; rows.push(KeccakRow { @@ -969,7 +974,7 @@ fn keccak(rows: &mut Vec>, bytes: &[u8], r: F) { fn multi_keccak( bytes: &[Vec], - r: F, + challenges: Challenges>, capacity: Option, ) -> Result>, Error> { // Dummy first row so that the initial data can be absorbed @@ -981,20 +986,20 @@ fn multi_keccak( theta_c: [0u8; THETA_C_WIDTH], input: [0u8; ABSORB_WIDTH_PER_ROW], is_paddings: [false; ABSORB_WIDTH_PER_ROW_BYTES], - data_rlcs: [F::zero(); ABSORB_WIDTH_PER_ROW_BYTES], + data_rlcs: [Value::known(F::zero()); ABSORB_WIDTH_PER_ROW_BYTES], is_final: false, length: 0usize, - data_rlc: F::zero(), - hash_rlc: F::zero(), + data_rlc: Value::known(F::zero()), + hash_rlc: Value::known(F::zero()), }]; // Actual keccaks for bytes in bytes { - keccak(&mut rows, bytes, r); + keccak(&mut rows, bytes, challenges); } if let Some(capacity) = capacity { // Pad with no data hashes to the expected capacity while rows.len() < (1 + capacity * (NUM_ROUNDS + 1)) { - keccak(&mut rows, &[], r); + keccak(&mut rows, &[], challenges); } // Check that we are not over capacity if rows.len() > (1 + capacity * (NUM_ROUNDS + 1)) { @@ -1010,8 +1015,7 @@ mod tests { use halo2_proofs::{dev::MockProver, halo2curves::bn256::Fr}; fn verify(k: u32, inputs: Vec>, success: bool) { - let mut circuit = KeccakBitCircuit::new(2usize.pow(k)); - circuit.generate_witness(&inputs); + let circuit = KeccakBitCircuit::new(2usize.pow(k), inputs); let prover = MockProver::::run(k, &circuit, vec![]).unwrap(); let verify_result = prover.verify(); diff --git a/zkevm-circuits/src/keccak_circuit/keccak_packed.rs b/zkevm-circuits/src/keccak_circuit/keccak_packed.rs index 3223e84273..37357aab25 100644 --- a/zkevm-circuits/src/keccak_circuit/keccak_packed.rs +++ b/zkevm-circuits/src/keccak_circuit/keccak_packed.rs @@ -5,14 +5,16 @@ use super::util::{ }; use crate::evm_circuit::util::{not, rlc}; use crate::keccak_circuit::util::{ - compose_rlc, pack_with_base, rotate, scatter, target_part_sizes, to_bytes, unpack, - NUM_BITS_PER_BYTE, NUM_BITS_PER_WORD, NUM_WORDS_TO_ABSORB, NUM_WORDS_TO_SQUEEZE, RATE, + compose_rlc, extract_field, pack_with_base, rotate, scatter, target_part_sizes, to_bytes, + unpack, NUM_BITS_PER_BYTE, NUM_BITS_PER_WORD, NUM_WORDS_TO_ABSORB, NUM_WORDS_TO_SQUEEZE, RATE, RATE_IN_BITS, RHO_MATRIX, }; use crate::table::KeccakTable; +use crate::util::Challenges; use crate::{evm_circuit::util::constraint_builder::BaseConstraintBuilder, util::Expr}; use eth_types::Field; use gadgets::util::{and, select, sum}; +use halo2_proofs::plonk::Challenge; use halo2_proofs::{ circuit::{Layouter, Region, SimpleFloorPlanner, Value}, plonk::{ @@ -88,7 +90,7 @@ pub(crate) struct SqueezeData { } /// KeccakRow -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug)] pub(crate) struct KeccakRow { q_padding: bool, q_padding_last: bool, @@ -98,8 +100,8 @@ pub(crate) struct KeccakRow { cell_values: Vec, is_final: bool, length: usize, - data_rlc: F, - hash_rlc: F, + data_rlc: Value, + hash_rlc: Value, } /// Part @@ -148,19 +150,13 @@ pub struct KeccakPackedConfig { /// KeccakPackedCircuit #[derive(Default)] pub struct KeccakPackedCircuit { - witness: Vec>, + witness: Vec>, num_rows: usize, _marker: PhantomData, } -impl KeccakPackedCircuit { - fn r() -> F { - F::from(123456) - } -} - impl Circuit for KeccakPackedCircuit { - type Config = KeccakPackedConfig; + type Config = (KeccakPackedConfig, Challenges); type FloorPlanner = SimpleFloorPlanner; fn without_witnesses(&self) -> Self { @@ -168,7 +164,12 @@ impl Circuit for KeccakPackedCircuit { } fn configure(meta: &mut ConstraintSystem) -> Self::Config { - KeccakPackedConfig::configure(meta, Expression::Constant(KeccakPackedCircuit::r())) + let challenges = Challenges::construct(meta); + let challenge_exprs = challenges.exprs(meta); + ( + KeccakPackedConfig::configure(meta, challenge_exprs), + challenges, + ) } fn synthesize( @@ -176,17 +177,18 @@ impl Circuit for KeccakPackedCircuit { config: Self::Config, mut layouter: impl Layouter, ) -> Result<(), Error> { - config.load(&mut layouter)?; - config.assign(&mut layouter, &self.witness)?; + config.0.load(&mut layouter)?; + let witness = self.generate_witness(config.1.values(&mut layouter)); + config.0.assign(&mut layouter, &witness)?; Ok(()) } } impl KeccakPackedCircuit { /// Creates a new circuit instance - pub fn new(num_rows: usize) -> Self { + pub fn new(num_rows: usize, inputs: &[Vec]) -> Self { KeccakPackedCircuit { - witness: Vec::new(), + witness: inputs.to_vec(), num_rows, _marker: PhantomData, } @@ -199,9 +201,9 @@ impl KeccakPackedCircuit { } /// Sets the witness using the data to be hashed - pub fn generate_witness(&mut self, inputs: &[Vec]) { - self.witness = multi_keccak(inputs, KeccakPackedCircuit::r(), Some(self.capacity())) - .expect("Too many inputs for given capacity"); + pub(crate) fn generate_witness(&self, challenges: Challenges>) -> Vec> { + multi_keccak(&self.witness, challenges, Some(self.capacity())) + .expect("Too many inputs for given capacity") } } @@ -464,7 +466,10 @@ mod combine { } impl KeccakPackedConfig { - pub(crate) fn configure(meta: &mut ConstraintSystem, r: Expression) -> Self { + pub(crate) fn configure( + meta: &mut ConstraintSystem, + challenges: Challenges>, + ) -> Self { let q_enable = meta.fixed_column(); let q_first = meta.fixed_column(); let q_round = meta.fixed_column(); @@ -977,7 +982,7 @@ impl KeccakPackedConfig { } } let hash_bytes_le = hash_bytes.into_iter().rev().collect::>(); - let rlc = compose_rlc::expr(&hash_bytes_le, r.clone()); + let rlc = compose_rlc::expr(&hash_bytes_le, challenges.evm_word()); cb.condition(start_new_hash, |cb| { cb.require_equal( "hash rlc check", @@ -1139,7 +1144,7 @@ impl KeccakPackedConfig { new_data_rlc = select::expr( is_padding.expr(), new_data_rlc.clone(), - new_data_rlc.clone() * r.clone() + byte.expr.clone(), + new_data_rlc.clone() * challenges.keccak_input() + byte.expr.clone(), ); if idx < data_rlcs.len() - 1 { cb.require_equal( @@ -1223,10 +1228,10 @@ impl KeccakPackedConfig { &self, layouter: &mut impl Layouter, inputs: &[Vec], - r: F, + challenges: Challenges>, capacity: Option, ) -> Result<(), Error> { - let witness = multi_keccak(inputs, r, capacity)?; + let witness = multi_keccak(inputs, challenges, capacity)?; self.assign(layouter, &witness) } @@ -1303,9 +1308,9 @@ impl KeccakPackedConfig { region, offset, [ - F::from(row.is_final), + Value::known(F::from(row.is_final)), row.data_rlc, - F::from(row.length as u64), + Value::known(F::from(row.length as u64)), row.hash_rlc, ], )?; @@ -1368,7 +1373,7 @@ impl KeccakPackedConfig { } } -fn keccak(rows: &mut Vec>, bytes: &[u8], r: F) { +fn keccak(rows: &mut Vec>, bytes: &[u8], challenges: Challenges>) { let mut bits = into_bits(bytes); let mut s = [[F::zero(); 5]; 5]; let absorb_positions = get_absorb_positions(); @@ -1385,7 +1390,7 @@ fn keccak(rows: &mut Vec>, bytes: &[u8], r: F) { bits.push(1); let mut length = 0usize; - let mut data_rlc = F::zero(); + let mut data_rlc = Value::known(F::zero()); let chunks = bits.chunks(RATE_IN_BITS); let num_chunks = chunks.len(); for (idx, chunk) in chunks.enumerate() { @@ -1461,14 +1466,14 @@ fn keccak(rows: &mut Vec>, bytes: &[u8], r: F) { cell_values[*is_padding] = F::from(padding as u64); } - cell_values[data_rlcs[0]] = data_rlc; + cell_values[data_rlcs[0]] = extract_field(data_rlc); for (idx, (byte, padding)) in input_bytes.iter().zip(paddings.iter()).enumerate() { if !*padding { - let byte_value: F = byte.value; - data_rlc = data_rlc * r + byte_value; + let byte_value = Value::known(byte.value); + data_rlc = data_rlc * challenges.keccak_input() + byte_value; } if idx < data_rlcs.len() - 1 { - cell_values[data_rlcs[idx + 1]] = data_rlc; + cell_values[data_rlcs[idx + 1]] = extract_field(data_rlc); } } } @@ -1605,9 +1610,11 @@ fn keccak(rows: &mut Vec>, bytes: &[u8], r: F) { .flat_map(|a| to_bytes::value(&unpack(a[0]))) .rev() .collect::>(); - rlc::value(&hash_bytes_le, r) + challenges + .evm_word() + .map(|evm_word| rlc::value(&hash_bytes_le, evm_word)) } else { - F::zero() + Value::known(F::zero()) }; rows.push(KeccakRow { @@ -1654,7 +1661,7 @@ fn keccak(rows: &mut Vec>, bytes: &[u8], r: F) { fn multi_keccak( bytes: &[Vec], - r: F, + challenges: Challenges>, capacity: Option, ) -> Result>, Error> { // Dummy first row so that the initial data is absorbed @@ -1671,18 +1678,18 @@ fn multi_keccak( squeeze_data: SqueezeData { packed: F::zero() }, is_final: false, length: 0usize, - data_rlc: F::zero(), - hash_rlc: F::zero(), + data_rlc: Value::known(F::zero()), + hash_rlc: Value::known(F::zero()), cell_values: Vec::new(), }]; // Actual keccaks for bytes in bytes { - keccak(&mut rows, bytes, r); + keccak(&mut rows, bytes, challenges); } if let Some(capacity) = capacity { // Pad with no data hashes to the expected capacity while rows.len() < (1 + capacity * (NUM_ROUNDS + 1)) { - keccak(&mut rows, &[], r); + keccak(&mut rows, &[], challenges); } // Check that we are not over capacity if rows.len() > (1 + capacity * (NUM_ROUNDS + 1)) { @@ -1698,9 +1705,7 @@ mod tests { use halo2_proofs::{dev::MockProver, halo2curves::bn256::Fr}; fn verify(k: u32, inputs: Vec>, success: bool) { - let mut circuit = KeccakPackedCircuit::new(2usize.pow(k)); - circuit.generate_witness(&inputs); - + let circuit = KeccakPackedCircuit::new(2usize.pow(k), inputs.as_slice()); let prover = MockProver::::run(k, &circuit, vec![]).unwrap(); let verify_result = prover.verify(); if verify_result.is_ok() != success { diff --git a/zkevm-circuits/src/keccak_circuit/keccak_packed_multi.rs b/zkevm-circuits/src/keccak_circuit/keccak_packed_multi.rs index afaeee9808..ec15e51e0c 100644 --- a/zkevm-circuits/src/keccak_circuit/keccak_packed_multi.rs +++ b/zkevm-circuits/src/keccak_circuit/keccak_packed_multi.rs @@ -9,6 +9,7 @@ use crate::keccak_circuit::util::{ NUM_WORDS_TO_SQUEEZE, RATE, RATE_IN_BITS, RHO_MATRIX, ROUND_CST, }; use crate::table::KeccakTable; +use crate::util::Challenges; use crate::{evm_circuit::util::constraint_builder::BaseConstraintBuilder, util::Expr}; use eth_types::Field; use gadgets::util::{and, select, sum}; @@ -16,7 +17,9 @@ use halo2_proofs::arithmetic::FieldExt; use halo2_proofs::plonk::VirtualCells; use halo2_proofs::{ circuit::{Layouter, Region, SimpleFloorPlanner, Value}, - plonk::{Advice, Circuit, Column, ConstraintSystem, Error, Expression, Fixed, TableColumn}, + plonk::{ + Advice, Challenge, Circuit, Column, ConstraintSystem, Error, Expression, Fixed, TableColumn, + }, poly::Rotation, }; use log::{debug, info}; @@ -66,7 +69,7 @@ pub(crate) struct SqueezeData { } /// KeccakRow -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug)] pub(crate) struct KeccakRow { q_enable: bool, q_round: bool, @@ -78,8 +81,8 @@ pub(crate) struct KeccakRow { is_final: bool, cell_values: Vec, length: usize, - data_rlc: F, - hash_rlc: F, + data_rlc: Value, + hash_rlc: Value, } /// Part @@ -178,6 +181,24 @@ impl Cell { value, ); } + + pub(crate) fn assign_value(&self, region: &mut KeccakRegion, offset: i32, value: Value) { + // This is really ugly. But since there's no way to easily adapt the CellManager + // API customized for this impl specifically, for now I'm opening the + // value and extracting it. Once https://github.com/privacy-scaling-explorations/zkevm-circuits/issues/933 is resolved, + // this shouldn't be needed. + let mut value_f = F::zero(); + value.map(|f| { + value_f = f; + f + }); + + region.assign( + self.column_idx, + ((offset as i32) + self.rotation) as usize, + value_f, + ); + } } impl Expr for Cell { @@ -336,19 +357,13 @@ pub struct KeccakPackedConfig { /// KeccakPackedCircuit #[derive(Default)] pub struct KeccakPackedCircuit { - witness: Vec>, + inputs: Vec>, num_rows: usize, _marker: PhantomData, } -impl KeccakPackedCircuit { - fn r() -> F { - F::from(123456) - } -} - impl Circuit for KeccakPackedCircuit { - type Config = KeccakPackedConfig; + type Config = (KeccakPackedConfig, Challenges); type FloorPlanner = SimpleFloorPlanner; fn without_witnesses(&self) -> Self { @@ -356,7 +371,12 @@ impl Circuit for KeccakPackedCircuit { } fn configure(meta: &mut ConstraintSystem) -> Self::Config { - KeccakPackedConfig::configure(meta, Expression::Constant(KeccakPackedCircuit::r())) + let challenges = Challenges::construct(meta); + let challenge_exprs = challenges.exprs(meta); + ( + KeccakPackedConfig::configure(meta, challenge_exprs), + challenges, + ) } fn synthesize( @@ -364,17 +384,18 @@ impl Circuit for KeccakPackedCircuit { config: Self::Config, mut layouter: impl Layouter, ) -> Result<(), Error> { - config.load(&mut layouter)?; - config.assign(&mut layouter, &self.witness)?; + config.0.load(&mut layouter)?; + let witness = self.generate_witness(config.1.values(&mut layouter)); + config.0.assign(&mut layouter, witness.as_slice())?; Ok(()) } } impl KeccakPackedCircuit { /// Creates a new circuit instance - pub fn new(num_rows: usize) -> Self { + pub fn new(num_rows: usize, inputs: Vec>) -> Self { KeccakPackedCircuit { - witness: Vec::new(), + inputs, num_rows, _marker: PhantomData, } @@ -387,9 +408,9 @@ impl KeccakPackedCircuit { } /// Sets the witness using the data to be hashed - pub fn generate_witness(&mut self, inputs: &[Vec]) { - self.witness = multi_keccak(inputs, KeccakPackedCircuit::r(), Some(self.capacity())) - .expect("Too many inputs for given capacity"); + pub(crate) fn generate_witness(&self, challenges: Challenges>) -> Vec> { + multi_keccak(self.inputs.as_slice(), challenges, Some(self.capacity())) + .expect("Too many inputs for given capacity") } } @@ -778,7 +799,10 @@ mod transform_to { } impl KeccakPackedConfig { - pub(crate) fn configure(meta: &mut ConstraintSystem, r: Expression) -> Self { + pub(crate) fn configure( + meta: &mut ConstraintSystem, + challenges: Challenges>, + ) -> Self { let q_enable = meta.fixed_column(); let q_first = meta.fixed_column(); let q_round = meta.fixed_column(); @@ -1289,7 +1313,7 @@ impl KeccakPackedConfig { }); } let hash_bytes_le = hash_bytes.into_iter().rev().collect::>(); - let rlc = compose_rlc::expr(&hash_bytes_le, r.clone()); + let rlc = compose_rlc::expr(&hash_bytes_le, challenges.evm_word()); cb.condition(start_new_hash, |cb| { cb.require_equal( "hash rlc check", @@ -1483,7 +1507,7 @@ impl KeccakPackedConfig { new_data_rlc = select::expr( is_padding.expr(), new_data_rlc.clone(), - new_data_rlc.clone() * r.clone() + byte.expr.clone(), + new_data_rlc.clone() * challenges.keccak_input() + byte.expr.clone(), ); if idx < data_rlcs.len() - 1 { cb.require_equal( @@ -1562,10 +1586,10 @@ impl KeccakPackedConfig { &self, layouter: &mut impl Layouter, inputs: &[Vec], - r: F, + challenges: Challenges>, capacity: Option, ) -> Result<(), Error> { - let witness = multi_keccak(inputs, r, capacity)?; + let witness = multi_keccak(inputs, challenges, capacity)?; self.assign(layouter, &witness) } @@ -1617,9 +1641,9 @@ impl KeccakPackedConfig { region, offset, [ - F::from(row.is_final), + Value::known(F::from(row.is_final)), row.data_rlc, - F::from(row.length as u64), + Value::known(F::from(row.length as u64)), row.hash_rlc, ], )?; @@ -1665,7 +1689,7 @@ impl KeccakPackedConfig { } } -fn keccak(rows: &mut Vec>, bytes: &[u8], r: F) { +fn keccak(rows: &mut Vec>, bytes: &[u8], challenges: Challenges>) { let mut bits = into_bits(bytes); let mut s = [[F::zero(); 5]; 5]; let absorb_positions = get_absorb_positions(); @@ -1680,7 +1704,7 @@ fn keccak(rows: &mut Vec>, bytes: &[u8], r: F) { bits.push(1); let mut length = 0usize; - let mut data_rlc = F::zero(); + let mut data_rlc = Value::known(F::zero()); let chunks = bits.chunks(RATE_IN_BITS); let num_chunks = chunks.len(); for (idx, chunk) in chunks.enumerate() { @@ -1704,7 +1728,7 @@ fn keccak(rows: &mut Vec>, bytes: &[u8], r: F) { let mut cell_managers = Vec::new(); let mut regions = Vec::new(); - let mut hash_rlc = F::zero(); + let mut hash_rlc = Value::known(F::zero()); let mut round_lengths = Vec::new(); let mut round_data_rlcs = Vec::new(); for round in 0..NUM_ROUNDS + 1 { @@ -1792,14 +1816,14 @@ fn keccak(rows: &mut Vec>, bytes: &[u8], r: F) { is_padding.assign(&mut region, 0, if padding { F::one() } else { F::zero() }); } - data_rlcs[0].assign(&mut region, 0, data_rlc); + data_rlcs[0].assign_value(&mut region, 0, data_rlc); for (idx, (byte, padding)) in input_bytes.iter().zip(paddings.iter()).enumerate() { if !*padding { - let byte_value: F = byte.value; - data_rlc = data_rlc * r + byte_value; + let byte_value = Value::known(byte.value); + data_rlc = data_rlc * challenges.keccak_input() + byte_value; } if idx < data_rlcs.len() - 1 { - data_rlcs[idx + 1].assign(&mut region, 0, data_rlc); + data_rlcs[idx + 1].assign_value(&mut region, 0, data_rlc); } } } @@ -1953,9 +1977,11 @@ fn keccak(rows: &mut Vec>, bytes: &[u8], r: F) { .flat_map(|a| to_bytes::value(&unpack(a[0]))) .rev() .collect::>(); - rlc::value(&hash_bytes_le, r) + challenges + .evm_word() + .map(|challenge_value| rlc::value(&hash_bytes_le, challenge_value)) } else { - F::zero() + Value::known(F::zero()) }; // The words to squeeze out @@ -2022,7 +2048,7 @@ fn keccak(rows: &mut Vec>, bytes: &[u8], r: F) { fn multi_keccak( bytes: &[Vec], - r: F, + challenges: Challenges>, capacity: Option, ) -> Result>, Error> { let mut rows: Vec> = Vec::new(); @@ -2039,19 +2065,19 @@ fn multi_keccak( round_cst: F::zero(), is_final: false, length: 0usize, - data_rlc: F::zero(), - hash_rlc: F::zero(), + data_rlc: Value::known(F::zero()), + hash_rlc: Value::known(F::zero()), cell_values: Vec::new(), }); } // Actual keccaks for bytes in bytes { - keccak(&mut rows, bytes, r); + keccak(&mut rows, bytes, challenges); } if let Some(capacity) = capacity { // Pad with no data hashes to the expected capacity while rows.len() < (1 + capacity * (NUM_ROUNDS + 1)) * get_num_rows_per_round() { - keccak(&mut rows, &[], r); + keccak(&mut rows, &[], challenges); } // Check that we are not over capacity if rows.len() > (1 + capacity * (NUM_ROUNDS + 1)) * get_num_rows_per_round() { @@ -2067,8 +2093,7 @@ mod tests { use halo2_proofs::{dev::MockProver, halo2curves::bn256::Fr}; fn verify(k: u32, inputs: Vec>, success: bool) { - let mut circuit = KeccakPackedCircuit::new(2usize.pow(k)); - circuit.generate_witness(&inputs); + let circuit = KeccakPackedCircuit::new(2usize.pow(k), inputs); let prover = MockProver::::run(k, &circuit, vec![]).unwrap(); let verify_result = prover.verify(); diff --git a/zkevm-circuits/src/keccak_circuit/util.rs b/zkevm-circuits/src/keccak_circuit/util.rs index 60654c9661..408c39870d 100644 --- a/zkevm-circuits/src/keccak_circuit/util.rs +++ b/zkevm-circuits/src/keccak_circuit/util.rs @@ -452,3 +452,12 @@ pub fn load_lookup_table( }, ) } + +pub(crate) fn extract_field(value: Value) -> F { + let mut field = F::zero(); + let _ = value.map(|f| { + field = f; + f + }); + field +} diff --git a/zkevm-circuits/src/super_circuit.rs b/zkevm-circuits/src/super_circuit.rs index 57192ec432..7209cc187b 100644 --- a/zkevm-circuits/src/super_circuit.rs +++ b/zkevm-circuits/src/super_circuit.rs @@ -188,7 +188,7 @@ impl, offset: usize, - values: [F; 4], + values: [Value; 4], ) -> Result<(), Error> { for (column, value) in self.columns().iter().zip(values.iter()) { - region.assign_advice( - || format!("assign {}", offset), - *column, - offset, - || Value::known(*value), - )?; + region.assign_advice(|| format!("assign {}", offset), *column, offset, || *value)?; } Ok(()) } diff --git a/zkevm-circuits/src/util.rs b/zkevm-circuits/src/util.rs index c50a32d2b9..2ff94f85fb 100644 --- a/zkevm-circuits/src/util.rs +++ b/zkevm-circuits/src/util.rs @@ -45,7 +45,7 @@ pub fn power_of_randomness_from_instance( } /// All challenges used in `SuperCircuit`. -#[derive(Clone, Copy, Debug)] +#[derive(Default, Clone, Copy, Debug)] pub struct Challenges { evm_word: T, keccak_input: T, @@ -54,6 +54,9 @@ pub struct Challenges { impl Challenges { /// Construct `Challenges` by allocating challenges in specific phases. pub fn construct(meta: &mut ConstraintSystem) -> Self { + #[cfg(test)] + let _dummy_col = meta.advice_column(); + Self { evm_word: meta.challenge_usable_after(FirstPhase), keccak_input: meta.challenge_usable_after(FirstPhase),