From 21235c9555547f4aee4334faca3f3109a9fa03b0 Mon Sep 17 00:00:00 2001 From: ChihChengLiang Date: Fri, 24 Jun 2022 19:18:28 +0100 Subject: [PATCH 01/12] replace base conversion with from9 table + generic --- keccak256/src/permutation.rs | 1 - keccak256/src/permutation/base_conversion.rs | 564 ------------------- keccak256/src/permutation/circuit.rs | 53 +- keccak256/src/permutation/mixing.rs | 61 +- keccak256/src/permutation/tables.rs | 215 ++++--- 5 files changed, 161 insertions(+), 733 deletions(-) delete mode 100644 keccak256/src/permutation/base_conversion.rs diff --git a/keccak256/src/permutation.rs b/keccak256/src/permutation.rs index 6339f9dfce..2045343244 100644 --- a/keccak256/src/permutation.rs +++ b/keccak256/src/permutation.rs @@ -1,7 +1,6 @@ #![allow(clippy::type_complexity)] #![allow(clippy::too_many_arguments)] pub(crate) mod absorb; -pub(crate) mod base_conversion; pub mod circuit; pub(crate) mod generic; pub(crate) mod iota; diff --git a/keccak256/src/permutation/base_conversion.rs b/keccak256/src/permutation/base_conversion.rs deleted file mode 100644 index b9bae03ded..0000000000 --- a/keccak256/src/permutation/base_conversion.rs +++ /dev/null @@ -1,564 +0,0 @@ -use halo2_proofs::{ - circuit::{AssignedCell, Layouter}, - plonk::{Advice, Column, ConstraintSystem, Error, Selector}, - poly::Rotation, -}; - -use super::tables::BaseInfo; -use eth_types::Field; - -#[derive(Clone, Debug)] -pub(crate) struct BaseConversionConfig { - q_running_sum: Selector, - q_lookup: Selector, - base_info: BaseInfo, - // Flag is copied from the parent flag. Parent flag is assumed to be binary - // constrained. - flag: Column, - input_coef: Column, - input_acc: Column, - output_coef: Column, - output_acc: Column, -} - -impl BaseConversionConfig { - /// Side effect: lane and parent_flag is equality enabled - pub(crate) fn configure( - meta: &mut ConstraintSystem, - base_info: BaseInfo, - input_lane: Column, - parent_flag: Column, - advices: [Column; 5], - ) -> Self { - let q_running_sum = meta.selector(); - let q_lookup = meta.complex_selector(); - let [flag, input_coef, input_acc, output_coef, output_acc] = advices; - - meta.enable_equality(flag); - meta.enable_equality(input_coef); - meta.enable_equality(input_acc); - meta.enable_equality(output_coef); - meta.enable_equality(output_acc); - meta.enable_equality(input_lane); - meta.enable_equality(parent_flag); - - meta.create_gate("input running sum", |meta| { - let q_enable = meta.query_selector(q_running_sum); - let flag = meta.query_advice(flag, Rotation::cur()); - let coef = meta.query_advice(input_coef, Rotation::cur()); - let acc_prev = meta.query_advice(input_acc, Rotation::prev()); - let acc = meta.query_advice(input_acc, Rotation::cur()); - let power_of_base = base_info.input_pob(); - vec![q_enable * flag * (acc - acc_prev * power_of_base - coef)] - }); - meta.create_gate("output running sum", |meta| { - let q_enable = meta.query_selector(q_running_sum); - let flag = meta.query_advice(flag, Rotation::cur()); - let coef = meta.query_advice(output_coef, Rotation::cur()); - let acc_prev = meta.query_advice(output_acc, Rotation::prev()); - let acc = meta.query_advice(output_acc, Rotation::cur()); - let power_of_base = base_info.output_pob(); - vec![q_enable * flag * (acc - acc_prev * power_of_base - coef)] - }); - meta.lookup("Lookup i/o_coeff at Base conversion table", |meta| { - let q_enable = meta.query_selector(q_lookup); - let flag = meta.query_advice(flag, Rotation::cur()); - let input_slices = meta.query_advice(input_coef, Rotation::cur()); - let output_slices = meta.query_advice(output_coef, Rotation::cur()); - vec![ - ( - q_enable.clone() * flag.clone() * input_slices, - base_info.input_tc, - ), - (q_enable * flag * output_slices, base_info.output_tc), - ] - }); - - Self { - q_running_sum, - q_lookup, - base_info, - flag, - input_coef, - input_acc, - output_coef, - output_acc, - } - } - - pub(crate) fn assign_lane( - &self, - layouter: &mut impl Layouter, - input: AssignedCell, - flag: AssignedCell, - ) -> Result, Error> { - let (input_coefs, output_coefs, _) = self - .base_info - .compute_coefs(input.value().copied().unwrap_or_default())?; - - layouter.assign_region( - || "Base conversion", - |mut region| { - let mut input_acc = F::zero(); - let input_pob = self.base_info.input_pob(); - let mut output_acc = F::zero(); - let output_pob = self.base_info.output_pob(); - for (offset, (&input_coef, &output_coef)) in - input_coefs.iter().zip(output_coefs.iter()).enumerate() - { - self.q_lookup.enable(&mut region, offset)?; - if offset != 0 { - self.q_running_sum.enable(&mut region, offset)?; - } - flag.copy_advice(|| "Base conv flag", &mut region, self.flag, offset)?; - - let input_coef_cell = region.assign_advice( - || "Input Coef", - self.input_coef, - offset, - || Ok(input_coef), - )?; - input_acc = input_acc * input_pob + input_coef; - let input_acc_cell = region.assign_advice( - || "Input Acc", - self.input_acc, - offset, - || Ok(input_acc), - )?; - let output_coef_cell = region.assign_advice( - || "Output Coef", - self.output_coef, - offset, - || Ok(output_coef), - )?; - output_acc = output_acc * output_pob + output_coef; - let output_acc_cell = region.assign_advice( - || "Output Acc", - self.output_acc, - offset, - || Ok(output_acc), - )?; - - if offset == 0 { - // bind first acc to first coef - region.constrain_equal(input_acc_cell.cell(), input_coef_cell.cell())?; - region.constrain_equal(output_acc_cell.cell(), output_coef_cell.cell())?; - } else if offset == input_coefs.len() - 1 { - //region.constrain_equal(input_acc_cell, input.0)?; - return Ok(output_acc_cell); - } - } - unreachable!(); - }, - ) - } - - pub(crate) fn assign_state( - &self, - layouter: &mut impl Layouter, - state: &[AssignedCell; 25], - flag: AssignedCell, - ) -> Result<[AssignedCell; 25], Error> { - let state: Result>, Error> = state - .iter() - .map(|lane| { - let output = self.assign_lane(layouter, lane.clone(), flag.clone())?; - Ok(output) - }) - .into_iter() - .collect(); - let state = state?; - let state: [AssignedCell; 25] = state.try_into().unwrap(); - Ok(state) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::arith_helpers::{convert_b2_to_b13, convert_b9_lane_to_b13}; - use crate::gate_helpers::biguint_to_f; - use crate::permutation::tables::{FromBase9TableConfig, FromBinaryTableConfig}; - use halo2_proofs::{ - circuit::{Layouter, SimpleFloorPlanner}, - dev::MockProver, - pairing::bn256::Fr as Fp, - plonk::{Advice, Circuit, Column, ConstraintSystem, Error}, - }; - use itertools::Itertools; - use num_bigint::BigUint; - use pretty_assertions::assert_eq; - #[test] - fn test_base_conversion_from_b2() { - // We have to use a MyConfig because: - // We need to load the table - #[derive(Debug, Clone)] - struct MyConfig { - lane: Column, - flag: Column, - table: FromBinaryTableConfig, - conversion: BaseConversionConfig, - } - impl MyConfig { - pub fn configure(meta: &mut ConstraintSystem) -> Self { - let table = FromBinaryTableConfig::configure(meta); - let lane = meta.advice_column(); - let flag = meta.advice_column(); - let advices = (0..5) - .map(|_| meta.advice_column()) - .collect_vec() - .try_into() - .unwrap(); - let base_info = table.get_base_info(false); - let conversion = - BaseConversionConfig::configure(meta, base_info, lane, flag, advices); - Self { - lane, - flag, - table, - conversion, - } - } - - pub fn load(&self, layouter: &mut impl Layouter) -> Result<(), Error> { - self.table.load(layouter) - } - - pub fn assign_region( - &self, - layouter: &mut impl Layouter, - input: F, - ) -> Result, Error> { - // The main flag is enabled - let flag_value = F::one(); - let (lane, flag) = layouter.assign_region( - || "Input lane", - |mut region| { - let lane = - region.assign_advice(|| "Input lane", self.lane, 0, || Ok(input))?; - let flag = region.assign_advice( - || "main flag", - self.flag, - 0, - || Ok(flag_value), - )?; - Ok((lane, flag)) - }, - )?; - let output = self.conversion.assign_lane(layouter, lane, flag)?; - layouter.assign_region( - || "Input lane", - |mut region| output.copy_advice(|| "Output lane", &mut region, self.lane, 0), - )?; - Ok(output) - } - } - - #[derive(Default)] - struct MyCircuit { - input_b2_lane: F, - output_b13_lane: F, - } - impl Circuit for MyCircuit { - type Config = MyConfig; - type FloorPlanner = SimpleFloorPlanner; - - fn without_witnesses(&self) -> Self { - Self::default() - } - - fn configure(meta: &mut ConstraintSystem) -> Self::Config { - Self::Config::configure(meta) - } - - fn synthesize( - &self, - config: Self::Config, - mut layouter: impl Layouter, - ) -> Result<(), Error> { - config.load(&mut layouter)?; - let output = config.assign_region(&mut layouter, self.input_b2_lane)?; - if output.value().is_some() { - assert_eq!(output.value(), Some(&self.output_b13_lane)); - } - Ok(()) - } - } - let input = 12345678u64; - let circuit = MyCircuit:: { - input_b2_lane: Fp::from(input), - output_b13_lane: biguint_to_f::(&convert_b2_to_b13(input)), - }; - let k = 17; - - #[cfg(feature = "dev-graph")] - { - use plotters::prelude::*; - let root = BitMapBackend::new("base-conversion.png", (1024, 32768)).into_drawing_area(); - root.fill(&WHITE).unwrap(); - let root = root.titled("Base conversion", ("sans-serif", 60)).unwrap(); - halo2_proofs::dev::CircuitLayout::default() - .mark_equality_cells(true) - .render(k, &circuit, &root) - .unwrap(); - } - let prover = MockProver::::run(k, &circuit, vec![]).unwrap(); - assert_eq!(prover.verify(), Ok(())); - } - - #[test] - fn test_base_conversion_from_b9() { - #[derive(Debug, Clone)] - struct MyConfig { - lane: Column, - flag: Column, - table: FromBase9TableConfig, - conversion: BaseConversionConfig, - } - impl MyConfig { - pub fn configure(meta: &mut ConstraintSystem) -> Self { - let table = FromBase9TableConfig::configure(meta); - let lane = meta.advice_column(); - let flag = meta.advice_column(); - let advices = (0..5) - .map(|_| meta.advice_column()) - .collect_vec() - .try_into() - .unwrap(); - let base_info = table.get_base_info(false); - let conversion = - BaseConversionConfig::configure(meta, base_info, lane, flag, advices); - Self { - lane, - flag, - table, - conversion, - } - } - - pub fn load(&self, layouter: &mut impl Layouter) -> Result<(), Error> { - self.table.load(layouter) - } - - pub fn assign_region( - &self, - layouter: &mut impl Layouter, - input: F, - ) -> Result, Error> { - // The main flag is enabled - let flag_value = F::one(); - let (lane, flag) = layouter.assign_region( - || "Input lane", - |mut region| { - let lane = - region.assign_advice(|| "Input lane", self.lane, 0, || Ok(input))?; - let flag = region.assign_advice( - || "main flag", - self.flag, - 0, - || Ok(flag_value), - )?; - Ok((lane, flag)) - }, - )?; - - let output = self.conversion.assign_lane(layouter, lane, flag)?; - layouter.assign_region( - || "Input lane", - |mut region| output.copy_advice(|| "Output lane", &mut region, self.lane, 0), - )?; - - Ok(output) - } - } - - #[derive(Default)] - struct MyCircuit { - input_lane: F, - output_lane: F, - } - impl Circuit for MyCircuit { - type Config = MyConfig; - type FloorPlanner = SimpleFloorPlanner; - - fn without_witnesses(&self) -> Self { - Self::default() - } - - fn configure(meta: &mut ConstraintSystem) -> Self::Config { - Self::Config::configure(meta) - } - - fn synthesize( - &self, - config: Self::Config, - mut layouter: impl Layouter, - ) -> Result<(), Error> { - config.load(&mut layouter)?; - let output = config.assign_region(&mut layouter, self.input_lane)?; - if output.value().is_some() { - assert_eq!(output.value(), Some(&self.output_lane)); - } - Ok(()) - } - } - let input = BigUint::parse_bytes(b"02939a42ef593e37757abe328e9e409e75dcd76cf1b3427bc3", 16) - .unwrap(); - let circuit = MyCircuit:: { - input_lane: biguint_to_f::(&input), - output_lane: biguint_to_f::(&convert_b9_lane_to_b13(input)), - }; - let k = 16; - let prover = MockProver::::run(k, &circuit, vec![]).unwrap(); - assert_eq!(prover.verify(), Ok(())); - } - #[test] - fn test_state_base_conversion() { - // We have to use a MyConfig because: - // We need to load the table - #[derive(Debug, Clone)] - struct MyConfig { - flag: Column, - state: [Column; 25], - table: FromBinaryTableConfig, - conversion: BaseConversionConfig, - } - impl MyConfig { - pub fn configure(meta: &mut ConstraintSystem) -> Self { - let table = FromBinaryTableConfig::configure(meta); - let state: [Column; 25] = (0..25) - .map(|_| meta.advice_column()) - .collect::>() - .try_into() - .unwrap(); - let flag = meta.advice_column(); - let lane = meta.advice_column(); - let advices = (0..5) - .map(|_| meta.advice_column()) - .collect_vec() - .try_into() - .unwrap(); - let bi = table.get_base_info(false); - let conversion = BaseConversionConfig::configure(meta, bi, lane, flag, advices); - Self { - flag, - state, - table, - conversion, - } - } - - pub fn load(&self, layouter: &mut impl Layouter) -> Result<(), Error> { - self.table.load(layouter) - } - - pub fn assign_region( - &self, - layouter: &mut impl Layouter, - input: [F; 25], - ) -> Result<[F; 25], Error> { - let flag_value = F::one(); - let (state, flag) = layouter.assign_region( - || "Input state", - |mut region| { - let state: [AssignedCell; 25] = input - .iter() - .enumerate() - .map(|(idx, &value)| { - region - .assign_advice( - || format!("State {}", idx), - self.state[idx], - 0, - || Ok(value), - ) - .unwrap() - }) - .collect::>() - .try_into() - .unwrap(); - let flag = - region.assign_advice(|| "Flag", self.flag, 0, || Ok(flag_value))?; - Ok((state, flag)) - }, - )?; - let output_state = self.conversion.assign_state(layouter, &state, flag)?; - let output_state: [F; 25] = output_state - .iter() - .map(|cell| cell.value().copied().unwrap_or_default()) - .collect::>() - .try_into() - .unwrap(); - Ok(output_state) - } - } - - #[derive(Default)] - struct MyCircuit { - in_state: [F; 25], - out_state: [F; 25], - } - impl Circuit for MyCircuit { - type Config = MyConfig; - type FloorPlanner = SimpleFloorPlanner; - - fn without_witnesses(&self) -> Self { - Self::default() - } - - fn configure(meta: &mut ConstraintSystem) -> Self::Config { - Self::Config::configure(meta) - } - - fn synthesize( - &self, - config: Self::Config, - mut layouter: impl Layouter, - ) -> Result<(), Error> { - config.load(&mut layouter)?; - let out_state = config.assign_region(&mut layouter, self.in_state)?; - assert_eq!(out_state, self.out_state); - Ok(()) - } - } - let in_state: [[u64; 5]; 5] = [ - [4398046511105, 8, 2, 268436480, 2305844108725321728], - [ - 17592186044416, - 52776560230400, - 544, - 68719493120, - 2199023255552, - ], - [ - 4398046543872, - 1152921504606846984, - 262144, - 1024, - 1099511627780, - ], - [0, 52776558133248, 514, 268451840, 2305845208236949504], - [17592186077184, 1152921504608944128, 262176, 68719476736, 4], - ]; - - let in_state_flat = in_state.iter().flatten().collect::>(); - let in_state: [Fp; 25] = in_state_flat - .iter() - .map(|&x| Fp::from(*x)) - .collect::>() - .try_into() - .unwrap(); - let out_state: [Fp; 25] = in_state_flat - .iter() - .map(|&x| biguint_to_f::(&convert_b2_to_b13(*x))) - .collect::>() - .try_into() - .unwrap(); - let circuit = MyCircuit:: { - in_state, - out_state, - }; - let prover = MockProver::::run(17, &circuit, vec![]).unwrap(); - assert_eq!(prover.verify(), Ok(())); - } -} diff --git a/keccak256/src/permutation/circuit.rs b/keccak256/src/permutation/circuit.rs index 6ff04313e9..b139c26b9c 100644 --- a/keccak256/src/permutation/circuit.rs +++ b/keccak256/src/permutation/circuit.rs @@ -3,7 +3,6 @@ use crate::{ common::{NEXT_INPUTS_LANES, PERMUTATION, ROUND_CONSTANTS}, keccak_arith::*, permutation::{ - base_conversion::BaseConversionConfig, generic::GenericConfig, iota::IotaConstants, mixing::MixingConfig, @@ -29,11 +28,9 @@ pub struct KeccakFConfig { xi_config: XiConfig, base13to9_config: Base13toBase9TableConfig, from_b9_table: FromBase9TableConfig, - base_conversion_config: BaseConversionConfig, mixing_config: MixingConfig, pub state: [Column; 25], q_out: Selector, - base_conv_activator: Column, } impl KeccakFConfig { @@ -61,6 +58,11 @@ impl KeccakFConfig { .collect_vec() .try_into() .unwrap(); + let from_base9_cols: [TableColumn; 3] = (0..3) + .map(|_| meta.lookup_table_column()) + .collect_vec() + .try_into() + .unwrap(); let stackable = StackableTable::configure(meta, state[0..3].try_into().unwrap(), stackable_cols); let base13to9_config = Base13toBase9TableConfig::configure( @@ -74,26 +76,15 @@ impl KeccakFConfig { // xi let xi_config = XiConfig::configure(meta.selector(), meta, state); - // Allocate space for the activation flag of the base_conversion. - let base_conv_activator = meta.advice_column(); - meta.enable_equality(base_conv_activator); // Base conversion config. - let from_b9_table = FromBase9TableConfig::configure(meta); - let base_info = from_b9_table.get_base_info(false); - let base_conv_lane = meta.advice_column(); - let base_conversion_config = BaseConversionConfig::configure( - meta, - base_info, - base_conv_lane, - base_conv_activator, - state[0..5].try_into().unwrap(), - ); + let from_b9_table = + FromBase9TableConfig::configure(meta, state[0..3].try_into().unwrap(), from_base9_cols); // Mixing will make sure that the flag is binary constrained and that // the out state matches the expected result. let mixing_config = MixingConfig::configure( meta, - &from_b9_table, + from_b9_table.clone(), state, generic.clone(), stackable.clone(), @@ -122,11 +113,9 @@ impl KeccakFConfig { xi_config, base13to9_config, from_b9_table, - base_conversion_config, mixing_config, state, q_out, - base_conv_activator, } } @@ -200,20 +189,18 @@ impl KeccakFConfig { // base_13 which is what Theta requires again at the // start of the loop. state = { - let activation_flag = layouter.assign_region( - || "Base conversion enable", - |mut region| { - region.assign_advice( - || "Enable base conversion", - self.base_conv_activator, - 0, - || Ok(F::one()), - ) - }, - )?; - - self.base_conversion_config - .assign_state(layouter, &state, activation_flag)? + let state = state + .iter() + .map(|lane| { + let (base9s, base_13s, _) = + self.from_b9_table.assign_region(layouter, lane)?; + self.generic + .running_sum(layouter, base9s, Some(lane.clone()))?; + self.generic.running_sum(layouter, base_13s, None) + }) + .collect::>, Error>>()?; + let state: [AssignedCell; 25] = state.try_into().unwrap(); + state } } diff --git a/keccak256/src/permutation/mixing.rs b/keccak256/src/permutation/mixing.rs index a96496b9a9..e473205b1f 100644 --- a/keccak256/src/permutation/mixing.rs +++ b/keccak256/src/permutation/mixing.rs @@ -1,7 +1,7 @@ use super::super::arith_helpers::*; use super::generic::GenericConfig; use super::tables::{FromBase9TableConfig, StackableTable}; -use super::{absorb::AbsorbConfig, base_conversion::BaseConversionConfig, iota::IotaConstants}; +use super::{absorb::AbsorbConfig, iota::IotaConstants}; use crate::common::*; use crate::keccak_arith::KeccakFArith; use eth_types::Field; @@ -15,7 +15,7 @@ use halo2_proofs::{ pub struct MixingConfig { iota_constants: IotaConstants, absorb_config: AbsorbConfig, - base_conv_config: BaseConversionConfig, + pub table: FromBase9TableConfig, state: [Column; 25], flag: Column, q_out_copy: Selector, @@ -26,7 +26,7 @@ pub struct MixingConfig { impl MixingConfig { pub fn configure( meta: &mut ConstraintSystem, - table: &FromBase9TableConfig, + table: FromBase9TableConfig, state: [Column; 25], generic: GenericConfig, stackable: StackableTable, @@ -36,16 +36,6 @@ impl MixingConfig { // We mix -> Flag = true let absorb_config = AbsorbConfig::configure(meta, state); - let base_info = table.get_base_info(false); - let base_conv_lane = meta.advice_column(); - let base_conv_config = BaseConversionConfig::configure( - meta, - base_info, - base_conv_lane, - flag, - state[0..5].try_into().unwrap(), - ); - let q_out_copy = meta.selector(); meta.create_gate("Mixing result copies and constraints", |meta| { @@ -70,7 +60,7 @@ impl MixingConfig { MixingConfig { iota_constants, absorb_config, - base_conv_config, + table, state, flag, q_out_copy, @@ -172,10 +162,18 @@ impl MixingConfig { flag.clone(), )?; - // Base conversion assign - let base_conv_cells = - self.base_conv_config - .assign_state(layouter, &out_state_absorb_cells, flag.clone())?; + let base_conv_cells = { + let mut state = vec![]; + for lane in out_state_absorb_cells.iter() { + let (input_coefs, output_b13, _) = self.table.assign_region(layouter, &lane)?; + self.generic + .running_sum(layouter, input_coefs, Some(lane.clone()))?; + let out_lane = self.generic.running_sum(layouter, output_b13, None)?; + state.push(out_lane); + } + let state: [AssignedCell; 25] = state.try_into().unwrap(); + state + }; // IotaB13 let mix_res = { @@ -251,7 +249,6 @@ mod tests { #[derive(Clone)] struct MyConfig { mixing_conf: MixingConfig, - table: FromBase9TableConfig, stackable: StackableTable, } @@ -264,8 +261,6 @@ mod tests { } fn configure(meta: &mut ConstraintSystem) -> Self::Config { - let table = FromBase9TableConfig::configure(meta); - let state: [Column; 25] = (0..25) .map(|_| { let col = meta.advice_column(); @@ -278,19 +273,31 @@ mod tests { let fixed = meta.fixed_column(); let generic = GenericConfig::configure(meta, state[0..3].try_into().unwrap(), fixed); - let table_cols: [TableColumn; 3] = (0..3) + let stackable_cols: [TableColumn; 3] = (0..3) + .map(|_| meta.lookup_table_column()) + .collect_vec() + .try_into() + .unwrap(); + let fromb9_cols: [TableColumn; 3] = (0..3) .map(|_| meta.lookup_table_column()) .collect_vec() .try_into() .unwrap(); - let stackable = - StackableTable::configure(meta, state[0..3].try_into().unwrap(), table_cols); + let stackable = StackableTable::configure( + meta, + state[0..3].try_into().unwrap(), + stackable_cols, + ); + let table = FromBase9TableConfig::configure( + meta, + state[0..3].try_into().unwrap(), + fromb9_cols, + ); let mixing_conf = - MixingConfig::configure(meta, &table, state, generic, stackable.clone()); + MixingConfig::configure(meta, table, state, generic, stackable.clone()); MyConfig { mixing_conf, - table, stackable, } } @@ -301,7 +308,7 @@ mod tests { mut layouter: impl Layouter, ) -> Result<(), Error> { // Load the table - config.table.load(&mut layouter)?; + config.mixing_conf.table.load(&mut layouter)?; config.stackable.load(&mut layouter)?; let offset: usize = 0; diff --git a/keccak256/src/permutation/tables.rs b/keccak256/src/permutation/tables.rs index 4a041e6426..f208e050b4 100644 --- a/keccak256/src/permutation/tables.rs +++ b/keccak256/src/permutation/tables.rs @@ -17,7 +17,9 @@ use super::rho_helpers::{Conversion, STEP2_RANGE, STEP3_RANGE}; const MAX_CHUNKS: usize = 64; const NUM_OF_BINARY_CHUNKS: usize = 16; -const NUM_OF_B9_CHUNKS: usize = 5; +const NUM_OF_B9_CHUNKS_PER_SLICE: usize = 5; +/// is ceil(`MAX_CHUNKS`/ `NUM_OF_B9_CHUNKS_PER_SLICE`) = 13 +const NUM_OF_B9_SLICES: usize = 13; #[derive(Debug, Clone)] struct ThreeColumnsLookup { @@ -486,71 +488,6 @@ impl Base13toBase9TableConfig { } } -#[derive(Clone, Debug)] -pub(crate) struct BaseInfo { - input_base: u8, - output_base: u8, - // How many chunks we perform in a lookup? - num_chunks: usize, - // How many chunks in total - pub max_chunks: usize, - pub input_tc: TableColumn, - pub output_tc: TableColumn, - _marker: PhantomData, -} - -impl BaseInfo { - pub fn input_pob(&self) -> F { - F::from(self.input_base as u64).pow(&[self.num_chunks as u64, 0, 0, 0]) - } - pub fn output_pob(&self) -> F { - F::from(self.output_base as u64).pow(&[self.num_chunks as u64, 0, 0, 0]) - } - - pub fn compute_coefs(&self, input: F) -> Result<(Vec, Vec, F), Error> { - // big-endian - let input_chunks: Vec = { - let raw = f_to_biguint(input); - let mut v = raw.to_radix_le(self.input_base.into()); - debug_assert!(v.len() <= self.max_chunks); - // fill 0 to max chunks - v.resize(self.max_chunks, 0); - // v is big-endian now - v.reverse(); - v - }; - // Use rchunks + rev so that the remainder chunks stay at the big-endian - // side - let input_coefs: Vec = input_chunks - .rchunks(self.num_chunks) - .rev() - .map(|chunks| f_from_radix_be(chunks, self.input_base)) - .collect(); - let convert_chunk = match self.input_base { - B2 => |x| x, - B13 => convert_b13_coef, - B9 => convert_b9_coef, - _ => unreachable!(), - }; - let output: F = { - let converted_chunks: Vec = - input_chunks.iter().map(|&x| convert_chunk(x)).collect_vec(); - f_from_radix_be(&converted_chunks, self.output_base) - }; - - let output_coefs: Vec = input_chunks - .rchunks(self.num_chunks) - .rev() - .map(|chunks| { - let converted_chunks: Vec = - chunks.iter().map(|&x| convert_chunk(x)).collect_vec(); - f_from_radix_be(&converted_chunks, self.output_base) - }) - .collect(); - Ok((input_coefs, output_coefs, output)) - } -} - #[allow(dead_code)] #[derive(Debug, Clone)] pub struct FromBinaryTableConfig { @@ -605,58 +542,78 @@ impl FromBinaryTableConfig { _marker: PhantomData, } } +} - pub(crate) fn get_base_info(&self, output_b9: bool) -> BaseInfo { - BaseInfo { - input_base: B2, - output_base: if output_b9 { B9 } else { B13 }, - num_chunks: NUM_OF_BINARY_CHUNKS, - max_chunks: MAX_CHUNKS, - input_tc: self.base2, - output_tc: if output_b9 { self.base9 } else { self.base13 }, - _marker: PhantomData, - } - } +fn compute_input_coefs( + input: Option<&F>, + base: u8, + num_chunks: usize, +) -> [Option; SLICES] { + input.map_or([None; SLICES], |&input| { + // big-endian + let input_chunks: Vec = { + let raw = f_to_biguint(input); + let mut v = raw.to_radix_le(base.into()); + debug_assert!(v.len() <= MAX_CHUNKS); + // fill 0 to max chunks + v.resize(MAX_CHUNKS, 0); + // v is big-endian now + v.reverse(); + v + }; + // Use rchunks + rev so that the remainder chunks stay at the big-endian + // side + let input_coefs = input_chunks + .rchunks(num_chunks) + .rev() + .map(|chunks| Some(f_from_radix_be(chunks, base))) + .collect_vec(); + input_coefs.try_into().unwrap() + }) } #[derive(Debug, Clone)] pub struct FromBase9TableConfig { - base9: TableColumn, - base13: TableColumn, - base2: TableColumn, - _marker: PhantomData, + lookup_config: ThreeColumnsLookup, + // mapping from base9 input to base13 and base2 output + map: HashMap<[u8; 32], (F, F)>, } impl FromBase9TableConfig { - pub fn load(&self, layouter: &mut impl Layouter) -> Result<(), Error> { + pub fn load(&mut self, layouter: &mut impl Layouter) -> Result<(), Error> { layouter.assign_table( || "9 -> (2 and 13)", |mut table| { // Iterate over all possible base 9 values of size 5 - for (i, b9_chunks) in (0..NUM_OF_B9_CHUNKS) + for (i, b9_chunks) in (0..NUM_OF_B9_CHUNKS_PER_SLICE) .map(|_| 0..B9) .multi_cartesian_product() .enumerate() { + let input_b9 = f_from_radix_be::(&b9_chunks, B9); + let converted_chunks: Vec = + b9_chunks.iter().map(|&x| convert_b9_coef(x)).collect_vec(); + let output_b13 = f_from_radix_be::(&converted_chunks, B13); + let output_b2 = f_from_radix_be::(&converted_chunks, B2); + self.map.insert(input_b9.to_repr(), (output_b13, output_b2)); table.assign_cell( || "base 9", - self.base9, + self.lookup_config.col0.1, i, - || Ok(f_from_radix_be::(&b9_chunks, B9)), + || Ok(input_b9), )?; - let converted_chunks: Vec = - b9_chunks.iter().map(|&x| convert_b9_coef(x)).collect_vec(); + table.assign_cell( || "base 13", - self.base13, + self.lookup_config.col1.1, i, - || Ok(f_from_radix_be::(&converted_chunks, B13)), + || Ok(output_b13), )?; table.assign_cell( || "base 2", - self.base2, + self.lookup_config.col2.1, i, - || Ok(f_from_radix_be::(&converted_chunks, B2)), + || Ok(output_b2), )?; } Ok(()) @@ -664,24 +621,66 @@ impl FromBase9TableConfig { ) } - pub fn configure(meta: &mut ConstraintSystem) -> Self { - Self { - base2: meta.lookup_table_column(), - base9: meta.lookup_table_column(), - base13: meta.lookup_table_column(), - _marker: PhantomData, - } + pub fn configure( + meta: &mut ConstraintSystem, + adv_cols: [Column; 3], + table_cols: [TableColumn; 3], + ) -> Self { + let lookup_config = ThreeColumnsLookup::configure(meta, adv_cols, table_cols, "from base9"); + let map = HashMap::new(); + Self { lookup_config, map } } + pub fn assign_region( + &self, + layouter: &mut impl Layouter, + input: &AssignedCell, + ) -> Result< + ( + Vec>, + Vec>, + Vec>, + ), + Error, + > { + let input_coefs = compute_input_coefs::( + input.value(), + B9, + NUM_OF_B9_CHUNKS_PER_SLICE, + ); + layouter.assign_region( + || "base 9", + |mut region| { + let mut input_cells = vec![]; + let mut output_b13_cells = vec![]; + let mut output_b2_cells = vec![]; + for (offset, input_coef) in input_coefs.iter().enumerate() { + let input = region.assign_advice( + || "base 9", + self.lookup_config.col0.0, + offset, + || input_coef.ok_or(Error::Synthesis), + )?; + input_cells.push(input.clone()); - pub(crate) fn get_base_info(&self, output_b2: bool) -> BaseInfo { - BaseInfo { - input_base: B9, - output_base: if output_b2 { B2 } else { B13 }, - num_chunks: NUM_OF_B9_CHUNKS, - max_chunks: MAX_CHUNKS, - input_tc: self.base9, - output_tc: if output_b2 { self.base2 } else { self.base13 }, - _marker: PhantomData, - } + let output = input_coef.and_then(|v| self.map.get(&v.to_repr())); + + let output_b13 = region.assign_advice( + || "base 13", + self.lookup_config.col1.0, + offset, + || output.map(|v| v.0).ok_or(Error::Synthesis), + )?; + output_b13_cells.push(output_b13); + let output_b2 = region.assign_advice( + || "base 2", + self.lookup_config.col2.0, + offset, + || output.map(|v| v.1).ok_or(Error::Synthesis), + )?; + output_b2_cells.push(output_b2); + } + Ok((input_cells, output_b13_cells, output_b2_cells)) + }, + ) } } From 491f8f4c41b0fbd55d8e8e76e9b4e88423bdd3c3 Mon Sep 17 00:00:00 2001 From: ChihChengLiang Date: Sat, 25 Jun 2022 21:03:25 +0100 Subject: [PATCH 02/12] circuit done --- keccak256/src/permutation/circuit.rs | 396 +++++++++++++-------------- keccak256/src/permutation/generic.rs | 31 ++- keccak256/src/permutation/pi.rs | 2 +- keccak256/src/permutation/tables.rs | 179 ++++++++---- keccak256/src/permutation/theta.rs | 180 +++++------- keccak256/src/permutation/xi.rs | 174 +++++------- 6 files changed, 480 insertions(+), 482 deletions(-) diff --git a/keccak256/src/permutation/circuit.rs b/keccak256/src/permutation/circuit.rs index b139c26b9c..7d71a7bd61 100644 --- a/keccak256/src/permutation/circuit.rs +++ b/keccak256/src/permutation/circuit.rs @@ -1,42 +1,120 @@ use crate::{ arith_helpers::*, - common::{NEXT_INPUTS_LANES, PERMUTATION, ROUND_CONSTANTS}, - keccak_arith::*, + common::{NEXT_INPUTS_LANES, PERMUTATION}, + gate_helpers::{biguint_to_f, f_to_biguint}, permutation::{ generic::GenericConfig, iota::IotaConstants, - mixing::MixingConfig, pi::pi_gate_permutation, rho::assign_rho, tables::{Base13toBase9TableConfig, FromBase9TableConfig, StackableTable}, - theta::ThetaConfig, - xi::XiConfig, }, }; use eth_types::Field; use halo2_proofs::{ - circuit::{AssignedCell, Layouter, Region}, - plonk::{Advice, Column, ConstraintSystem, Error, Selector, TableColumn}, - poly::Rotation, + circuit::{AssignedCell, Layouter}, + plonk::{Advice, Column, ConstraintSystem, Error, TableColumn}, }; use itertools::Itertools; +use num_bigint::BigUint; + +use super::{ + tables::{ + FromBinaryTableConfig, NUM_OF_B9_CHUNKS_PER_SLICE, NUM_OF_B9_SLICES, + NUM_OF_BINARY_CHUNKS_PER_SLICE, NUM_OF_BINARY_SLICES, + }, + theta::assign_theta, + xi::assign_xi, +}; + +fn assign_next_input( + layouter: &mut impl Layouter, + next_input_col: &Column, + next_input: &Option<[F; NEXT_INPUTS_LANES]>, +) -> Result<[AssignedCell; NEXT_INPUTS_LANES], Error> { + let next_input_b9 = layouter.assign_region( + || "next input words", + |mut region| { + let mut next_input_b9: Vec> = vec![]; + let next_input = next_input.map_or( + [None; NEXT_INPUTS_LANES], + |v| -> [Option; NEXT_INPUTS_LANES] { + v.map(|vv| Some(vv)) + .iter() + .cloned() + .collect_vec() + .try_into() + .unwrap() + }, + ); + for (offset, input) in next_input.iter().enumerate() { + let cell = region.assign_advice( + || "next input words", + *next_input_col, + offset, + || { + Ok(input + .map(|input| { + let input = f_to_biguint(input); + let input = convert_b2_to_b9( + *input.to_u64_digits().first().unwrap_or(&0u64), + ); + biguint_to_f(&input) + }) + .unwrap_or(F::zero())) + }, + )?; + next_input_b9.push(cell); + } + Ok(next_input_b9) + }, + )?; + Ok(next_input_b9.try_into().unwrap()) +} + +fn convert_from_b9_to_b13( + layouter: &mut impl Layouter, + from_b9_table: &FromBase9TableConfig, + generic: &GenericConfig, + state: [AssignedCell; 25], +) -> Result<[AssignedCell; 25], Error> { + let state = state + .iter() + .map(|lane| { + let (base9s, base_13s, _) = from_b9_table.assign_region(layouter, lane)?; + let vs = (0..NUM_OF_B9_SLICES) + .map(|i| { + biguint_to_f(&BigUint::from(B9).pow((NUM_OF_B9_CHUNKS_PER_SLICE * i) as u32)) + }) + .rev() + .collect_vec(); + generic.linear_combine_consts(layouter, base9s, vs, Some(lane.clone()))?; + let vs = (0..NUM_OF_B9_SLICES) + .map(|i| { + biguint_to_f(&BigUint::from(B13).pow((NUM_OF_B9_CHUNKS_PER_SLICE * i) as u32)) + }) + .rev() + .collect_vec(); + generic.linear_combine_consts(layouter, base_13s, vs, None) + }) + .collect::>, Error>>()?; + Ok(state.try_into().unwrap()) +} + #[derive(Clone, Debug)] pub struct KeccakFConfig { generic: GenericConfig, stackable: StackableTable, - theta_config: ThetaConfig, - xi_config: XiConfig, base13to9_config: Base13toBase9TableConfig, from_b9_table: FromBase9TableConfig, - mixing_config: MixingConfig, - pub state: [Column; 25], - q_out: Selector, + from_b2_table: FromBinaryTableConfig, + advice: Column, } impl KeccakFConfig { // We assume state is received in base-9. pub fn configure(meta: &mut ConstraintSystem) -> Self { - let state: [Column; 25] = (0..25) + let advices: [Column; 3] = (0..3) .map(|_| { let column = meta.advice_column(); meta.enable_equality(column); @@ -47,7 +125,7 @@ impl KeccakFConfig { .unwrap(); let fixed = meta.fixed_column(); - let generic = GenericConfig::configure(meta, state[0..3].try_into().unwrap(), fixed); + let generic = GenericConfig::configure(meta, advices, fixed); let stackable_cols: [TableColumn; 3] = (0..3) .map(|_| meta.lookup_table_column()) .collect_vec() @@ -63,93 +141,48 @@ impl KeccakFConfig { .collect_vec() .try_into() .unwrap(); - let stackable = - StackableTable::configure(meta, state[0..3].try_into().unwrap(), stackable_cols); - let base13to9_config = Base13toBase9TableConfig::configure( - meta, - state[0..3].try_into().unwrap(), - base13to9_cols, - ); - - // theta - let theta_config = ThetaConfig::configure(meta.selector(), meta, state); - // xi - let xi_config = XiConfig::configure(meta.selector(), meta, state); - + let from_base2_cols: [TableColumn; 3] = (0..3) + .map(|_| meta.lookup_table_column()) + .collect_vec() + .try_into() + .unwrap(); + let stackable = StackableTable::configure(meta, advices, stackable_cols); + let base13to9_config = Base13toBase9TableConfig::configure(meta, advices, base13to9_cols); // Base conversion config. - let from_b9_table = - FromBase9TableConfig::configure(meta, state[0..3].try_into().unwrap(), from_base9_cols); - - // Mixing will make sure that the flag is binary constrained and that - // the out state matches the expected result. - let mixing_config = MixingConfig::configure( - meta, - from_b9_table.clone(), - state, - generic.clone(), - stackable.clone(), - ); - - // Allocate the `out state correctness` gate selector - let q_out = meta.selector(); - // Constraint the out of the mixing gate to be equal to the out state - // announced. - meta.create_gate("Constraint out_state correctness", |meta| { - (0..25usize) - .into_iter() - .map(|idx| { - let q_out = meta.query_selector(q_out); - let out_mixing = meta.query_advice(state[idx], Rotation::cur()); - let out_expected_state = meta.query_advice(state[idx], Rotation::next()); - q_out * (out_mixing - out_expected_state) - }) - .collect_vec() - }); + let from_b9_table = FromBase9TableConfig::configure(meta, advices, from_base9_cols); + let from_b2_table = FromBinaryTableConfig::configure(meta, advices, from_base2_cols); - KeccakFConfig { + Self { generic, stackable, - theta_config, - xi_config, base13to9_config, from_b9_table, - mixing_config, - state, - q_out, + from_b2_table, + advice: advices[0], } } pub fn load(&mut self, layouter: &mut impl Layouter) -> Result<(), Error> { self.stackable.load(layouter)?; self.base13to9_config.load(layouter)?; - self.from_b9_table.load(layouter) + self.from_b9_table.load(layouter)?; + self.from_b2_table.load(layouter) } pub fn assign_all( &self, layouter: &mut impl Layouter, in_state: [AssignedCell; 25], - out_state: [F; 25], flag: bool, next_mixing: Option<[F; NEXT_INPUTS_LANES]>, ) -> Result<[AssignedCell; 25], Error> { + let iota_constants = IotaConstants::default(); let mut state = in_state; // First 23 rounds for round_idx in 0..PERMUTATION { // State in base-13 - // theta - state = { - // Apply theta outside circuit - let out_state = - KeccakFArith::theta(&state_to_biguint(split_state_cells(state.clone()))); - let out_state = state_bigint_to_field(out_state); - // assignment - self.theta_config - .assign_state(layouter, &state, out_state)? - }; - - // rho + state = assign_theta(&self.generic, layouter, &state)?; state = assign_rho( layouter, &self.base13to9_config, @@ -158,27 +191,14 @@ impl KeccakFConfig { &state, )?; // Outputs in base-9 which is what Pi requires - - // Apply Pi permutation - state = pi_gate_permutation(state.clone()); - - // xi - state = { - // Apply xi outside circuit - let out_state = - KeccakFArith::xi(&state_to_biguint(split_state_cells(state.clone()))); - let out_state = state_bigint_to_field(out_state); - // assignment - self.xi_config.assign_state(layouter, &state, out_state)? - }; + state = pi_gate_permutation(&state); + state = assign_xi(&self.generic, layouter, &state)?; // Last round before Mixing does not run IotaB9 nor BaseConversion if round_idx == PERMUTATION - 1 { break; } - // iota_b9 - let iota_constants = IotaConstants::default(); state[0] = self.generic.add_fixed( layouter, state[0].clone(), @@ -188,95 +208,74 @@ impl KeccakFConfig { // The resulting state is in Base-9 now. We now convert it to // base_13 which is what Theta requires again at the // start of the loop. - state = { - let state = state - .iter() - .map(|lane| { - let (base9s, base_13s, _) = - self.from_b9_table.assign_region(layouter, lane)?; - self.generic - .running_sum(layouter, base9s, Some(lane.clone()))?; - self.generic.running_sum(layouter, base_13s, None) - }) - .collect::>, Error>>()?; - let state: [AssignedCell; 25] = state.try_into().unwrap(); - state - } + state = convert_from_b9_to_b13(layouter, &self.from_b9_table, &self.generic, state)?; } + let (f_pos, f_neg) = self.stackable.assign_boolean_flag(layouter, Some(flag))?; - // Mixing step - let mix_res = KeccakFArith::mixing( - &state_to_biguint(split_state_cells(state.clone())), - next_mixing - .map(|state| state_to_state_bigint::(state)) - .as_ref(), - *ROUND_CONSTANTS.last().unwrap(), - ); - - let mix_res = self.mixing_config.assign_state( + state[0] = self.generic.conditional_add_const( layouter, - &state, - state_bigint_to_field(mix_res), - flag, - next_mixing, + state[0].clone(), + f_neg.clone(), + iota_constants.a4_times_round_constants_b9[PERMUTATION - 1], )?; - self.constrain_out_state(layouter, &mix_res, out_state) - } - - pub fn constrain_out_state( - &self, - layouter: &mut impl Layouter, - out_mixing: &[AssignedCell; 25], - out_state: [F; 25], - ) -> Result<[AssignedCell; 25], Error> { - layouter.assign_region( - || "Constraint out_state and out_mixing", - |mut region| { - // Enable selector at offset = 0 - self.q_out.enable(&mut region, 0)?; - - // Allocate out_mixing at offset = 0 in `state` column. - self.copy_state(&mut region, 0, self.state, out_mixing)?; - - // Witness out_state at offset = 1 in `state` column. - let out_state: [AssignedCell; 25] = { - let mut out_vec: Vec> = vec![]; - for (idx, lane) in out_state.iter().enumerate() { - let out_cell = region.assign_advice( - || format!("assign out_state [{}]", idx), - self.state[idx], - 1, - || Ok(*lane), - )?; - out_vec.push(out_cell); - } - out_vec.try_into().unwrap() - }; - - Ok(out_state) - }, - ) - } - - /// Copies the `state` cells to the passed [Column; 25]. - fn copy_state( - &self, - region: &mut Region<'_, F>, - offset: usize, - columns: [Column; 25], - state: &[AssignedCell; 25], - ) -> Result<(), Error> { - for (idx, cell) in state.iter().enumerate() { - cell.copy_advice( - || format!("Copy state {}", idx), - region, - columns[idx], - offset, + let next_input = assign_next_input(layouter, &self.advice, &next_mixing)?; + + // Convert to base 9 and multiply by A4 + let next_input = { + let next_input = next_input + .iter() + .map(|input| { + let (base2s, base9s, _) = self.from_b2_table.assign_region(layouter, input)?; + let vs = (0..NUM_OF_BINARY_SLICES) + .map(|i| { + biguint_to_f( + &BigUint::from(B2).pow((NUM_OF_BINARY_CHUNKS_PER_SLICE * i) as u32), + ) + }) + .rev() + .collect_vec(); + self.generic.linear_combine_consts( + layouter, + base2s, + vs, + Some(input.clone()), + )?; + let vs = (0..NUM_OF_BINARY_SLICES) + .map(|i| { + biguint_to_f( + &BigUint::from(B13) + .pow((NUM_OF_BINARY_CHUNKS_PER_SLICE * i) as u32), + ) + }) + .rev() + .collect_vec(); + let output = + self.generic + .linear_combine_consts(layouter, base9s.clone(), vs, None)?; + self.generic.mul_fixed(layouter, output, F::from(A4)) + }) + .collect::>, Error>>()?; + let next_input: [AssignedCell; NEXT_INPUTS_LANES] = + next_input.try_into().unwrap(); + next_input + }; + for (i, input) in next_input.iter().enumerate() { + state[i] = self.generic.conditional_add_advice( + layouter, + state[i].clone(), + f_pos.clone(), + input.clone(), )?; } - - Ok(()) + state = convert_from_b9_to_b13(layouter, &self.from_b9_table, &self.generic, state)?; + state[0] = self.generic.conditional_add_const( + layouter, + state[0].clone(), + f_pos.clone(), + iota_constants.round_constant_b13, + )?; + Ok(state.try_into().unwrap()) } } @@ -285,14 +284,12 @@ mod tests { use super::*; use crate::common::{State, NEXT_INPUTS_LANES}; use crate::gate_helpers::biguint_to_f; + use crate::keccak_arith::KeccakFArith; use halo2_proofs::circuit::Layouter; use halo2_proofs::pairing::bn256::Fr as Fp; use halo2_proofs::plonk::{ConstraintSystem, Error}; use halo2_proofs::{circuit::SimpleFloorPlanner, dev::MockProver, plonk::Circuit}; - use pretty_assertions::assert_eq; - // TODO: Remove ignore once this can run in the CI without hanging. - #[ignore] #[test] fn test_keccak_round() { #[derive(Default)] @@ -323,36 +320,39 @@ mod tests { ) -> Result<(), Error> { // Load the table config.load(&mut layouter)?; - let offset: usize = 0; - let in_state = layouter.assign_region( + let state: [AssignedCell; 25] = layouter.assign_region( || "Keccak round Wittnes & flag assignation", |mut region| { - // Witness `state` - let in_state: [AssignedCell; 25] = { - let mut state: Vec> = Vec::with_capacity(25); - for (idx, val) in self.in_state.iter().enumerate() { - let cell = region.assign_advice( + let state = self + .in_state + .iter() + .enumerate() + .map(|(offset, val)| { + region.assign_advice( || "witness input state", - config.state[idx], + config.advice, offset, || Ok(*val), - )?; - state.push(cell) - } - state.try_into().unwrap() - }; + ) + }) + .collect::>, Error>>()?; - Ok(in_state) + Ok(state.try_into().unwrap()) }, )?; - config.assign_all( - &mut layouter, - in_state, - self.out_state, - self.is_mixing, - self.next_mixing, + let state = + config.assign_all(&mut layouter, state, self.is_mixing, self.next_mixing)?; + + layouter.assign_region( + || "check final states", + |mut region| { + for (assigned, value) in state.iter().zip(self.out_state.iter()) { + region.constrain_constant(assigned.cell(), value)?; + } + Ok(()) + }, )?; Ok(()) } @@ -414,7 +414,7 @@ mod tests { let prover = MockProver::::run(17, &circuit, vec![]).unwrap(); - assert_eq!(prover.verify(), Ok(())); + assert_eq!(prover.verify(), Ok(()), "is_mixing: false"); // With wrong input and/or output witnesses, the proof should fail // to be verified. @@ -454,7 +454,7 @@ mod tests { let prover = MockProver::::run(17, &circuit, vec![]).unwrap(); - assert_eq!(prover.verify(), Ok(())); + assert_eq!(prover.verify(), Ok(()), "is_mixing: true"); // With wrong input and/or output witnesses, the proof should fail // to be verified. diff --git a/keccak256/src/permutation/generic.rs b/keccak256/src/permutation/generic.rs index 192ee5b8bb..ef23d70da0 100644 --- a/keccak256/src/permutation/generic.rs +++ b/keccak256/src/permutation/generic.rs @@ -53,7 +53,7 @@ impl GenericConfig { fn add_generic( &self, layouter: &mut impl Layouter, - input: AssignedCell, + input: Option>, left: Option>, right: Option>, value: Option, @@ -63,7 +63,11 @@ impl GenericConfig { |mut region| { let offset = 0; self.q_enable.enable(&mut region, offset)?; - input.copy_advice(|| "input", &mut region, self.io, offset)?; + let input = input.as_ref().map_or( + region.assign_advice_from_constant(|| "input is 0", self.io, offset, F::zero()), + |input| input.copy_advice(|| "input", &mut region, self.io, offset), + )?; + let left = match &left { Some(x) => { // copy x to use as a flag @@ -130,7 +134,7 @@ impl GenericConfig { x: AssignedCell, v: F, ) -> Result, Error> { - self.add_generic(layouter, input, Some(x), None, Some(v)) + self.add_generic(layouter, Some(input), Some(x), None, Some(v)) } /// input -= x pub fn sub_advice( @@ -139,7 +143,7 @@ impl GenericConfig { input: AssignedCell, x: AssignedCell, ) -> Result, Error> { - self.add_generic(layouter, input, Some(x), None, Some(-F::one())) + self.add_generic(layouter, Some(input), Some(x), None, Some(-F::one())) } /// input += v pub fn add_fixed( @@ -148,7 +152,16 @@ impl GenericConfig { input: AssignedCell, value: F, ) -> Result, Error> { - self.add_generic(layouter, input, None, None, Some(value)) + self.add_generic(layouter, Some(input), None, None, Some(value)) + } + /// output = input * v + pub fn mul_fixed( + &self, + layouter: &mut impl Layouter, + input: AssignedCell, + value: F, + ) -> Result, Error> { + self.add_generic(layouter, None, Some(input), None, Some(value)) } /// input += flag * v /// No boolean check on the flag, we assume the flag is checked before @@ -160,7 +173,7 @@ impl GenericConfig { flag: AssignedCell, value: F, ) -> Result, Error> { - self.add_generic(layouter, input, Some(flag), None, Some(value)) + self.add_generic(layouter, Some(input), Some(flag), None, Some(value)) } /// input += flag * x /// No boolean check on the flag, we assume the flag is checked before @@ -172,7 +185,7 @@ impl GenericConfig { flag: AssignedCell, x: AssignedCell, ) -> Result, Error> { - self.add_generic(layouter, input, Some(flag), Some(x), None) + self.add_generic(layouter, Some(input), Some(flag), Some(x), None) } fn linear_combine_generic( &self, @@ -243,6 +256,10 @@ impl GenericConfig { )?; } if let Some(outcome) = &outcome { + if outcome.value().is_some() && acc.value().is_some() { + debug_assert_eq!(outcome.value(), acc.value()); + } + region.constrain_equal(outcome.cell(), acc.cell())?; } Ok(acc) diff --git a/keccak256/src/permutation/pi.rs b/keccak256/src/permutation/pi.rs index fae778e4d2..41c815ed59 100644 --- a/keccak256/src/permutation/pi.rs +++ b/keccak256/src/permutation/pi.rs @@ -7,7 +7,7 @@ use itertools::Itertools; /// It has no gates. We just have to permute the previous state into the correct /// order. The copy constrain in the next gate can then enforce the Pi step /// permutation. -pub fn pi_gate_permutation(state: [AssignedCell; 25]) -> [AssignedCell; 25] { +pub fn pi_gate_permutation(state: &[AssignedCell; 25]) -> [AssignedCell; 25] { let state: [AssignedCell; 25] = (0..5) .cartesian_product(0..5) .map(|(x, y)| state[5 * ((x + 3 * y) % 5) + x].clone()) diff --git a/keccak256/src/permutation/tables.rs b/keccak256/src/permutation/tables.rs index f208e050b4..ca7e8bbf1e 100644 --- a/keccak256/src/permutation/tables.rs +++ b/keccak256/src/permutation/tables.rs @@ -16,10 +16,11 @@ use strum_macros::{Display, EnumIter}; use super::rho_helpers::{Conversion, STEP2_RANGE, STEP3_RANGE}; const MAX_CHUNKS: usize = 64; -const NUM_OF_BINARY_CHUNKS: usize = 16; -const NUM_OF_B9_CHUNKS_PER_SLICE: usize = 5; +pub const NUM_OF_BINARY_CHUNKS_PER_SLICE: usize = 16; +pub const NUM_OF_BINARY_SLICES: usize = 4; +pub const NUM_OF_B9_CHUNKS_PER_SLICE: usize = 5; /// is ceil(`MAX_CHUNKS`/ `NUM_OF_B9_CHUNKS_PER_SLICE`) = 13 -const NUM_OF_B9_SLICES: usize = 13; +pub const NUM_OF_B9_SLICES: usize = 13; #[derive(Debug, Clone)] struct ThreeColumnsLookup { @@ -488,62 +489,6 @@ impl Base13toBase9TableConfig { } } -#[allow(dead_code)] -#[derive(Debug, Clone)] -pub struct FromBinaryTableConfig { - base2: TableColumn, - base9: TableColumn, - base13: TableColumn, - _marker: PhantomData, -} - -#[allow(dead_code)] -impl FromBinaryTableConfig { - pub(crate) fn load(&self, layouter: &mut impl Layouter) -> Result<(), Error> { - layouter.assign_table( - || "2 -> (9 and 13)", - |mut table| { - // Iterate over all possible binary values of size 16 - for (i, b2_chunks) in (0..NUM_OF_BINARY_CHUNKS) - .map(|_| 0..B2) - .multi_cartesian_product() - .enumerate() - { - table.assign_cell( - || "base 2", - self.base2, - i, - || Ok(f_from_radix_be::(&b2_chunks, B2)), - )?; - - table.assign_cell( - || "base 9", - self.base9, - i, - || Ok(f_from_radix_be::(&b2_chunks, B9)), - )?; - table.assign_cell( - || "base 13", - self.base13, - i, - || Ok(f_from_radix_be::(&b2_chunks, B13)), - )?; - } - Ok(()) - }, - ) - } - - pub(crate) fn configure(meta: &mut ConstraintSystem) -> Self { - Self { - base2: meta.lookup_table_column(), - base9: meta.lookup_table_column(), - base13: meta.lookup_table_column(), - _marker: PhantomData, - } - } -} - fn compute_input_coefs( input: Option<&F>, base: u8, @@ -654,6 +599,7 @@ impl FromBase9TableConfig { let mut output_b13_cells = vec![]; let mut output_b2_cells = vec![]; for (offset, input_coef) in input_coefs.iter().enumerate() { + self.lookup_config.q_enable.enable(&mut region, offset)?; let input = region.assign_advice( || "base 9", self.lookup_config.col0.0, @@ -661,7 +607,6 @@ impl FromBase9TableConfig { || input_coef.ok_or(Error::Synthesis), )?; input_cells.push(input.clone()); - let output = input_coef.and_then(|v| self.map.get(&v.to_repr())); let output_b13 = region.assign_advice( @@ -684,3 +629,117 @@ impl FromBase9TableConfig { ) } } + +#[derive(Debug, Clone)] +pub struct FromBinaryTableConfig { + lookup_config: ThreeColumnsLookup, + // mapping from base9 input to base13 and base2 output + map: HashMap<[u8; 32], (F, F)>, +} + +impl FromBinaryTableConfig { + pub fn load(&mut self, layouter: &mut impl Layouter) -> Result<(), Error> { + layouter.assign_table( + || "2 -> (9 and 13)", + |mut table| { + for (i, b2_chunks) in (0..NUM_OF_BINARY_CHUNKS_PER_SLICE) + .map(|_| 0..B2) + .multi_cartesian_product() + .enumerate() + { + let input_b2 = f_from_radix_be::(&b2_chunks, B9); + let output_b9 = f_from_radix_be::(&b2_chunks, B9); + let output_b13 = f_from_radix_be::(&b2_chunks, B13); + + self.map.insert(input_b2.to_repr(), (output_b9, output_b13)); + // Iterate over all possible binary values of size 16 + + table.assign_cell( + || "base 2", + self.lookup_config.col0.1, + i, + || Ok(input_b2), + )?; + + table.assign_cell( + || "base 9", + self.lookup_config.col1.1, + i, + || Ok(output_b9), + )?; + table.assign_cell( + || "base 13", + self.lookup_config.col2.1, + i, + || Ok(output_b13), + )?; + } + Ok(()) + }, + ) + } + + pub fn configure( + meta: &mut ConstraintSystem, + adv_cols: [Column; 3], + table_cols: [TableColumn; 3], + ) -> Self { + let lookup_config = ThreeColumnsLookup::configure(meta, adv_cols, table_cols, "from base9"); + let map = HashMap::new(); + Self { lookup_config, map } + } + pub fn assign_region( + &self, + layouter: &mut impl Layouter, + input: &AssignedCell, + ) -> Result< + ( + Vec>, + Vec>, + Vec>, + ), + Error, + > { + let input_coefs = compute_input_coefs::( + input.value(), + B2, + NUM_OF_BINARY_CHUNKS_PER_SLICE, + ); + layouter.assign_region( + || "base 2", + |mut region| { + let mut input_cells = vec![]; + let mut output_b9_cells = vec![]; + let mut output_b13_cells = vec![]; + for (offset, input_coef) in input_coefs.iter().enumerate() { + self.lookup_config.q_enable.enable(&mut region, offset)?; + let input = region.assign_advice( + || "base 2", + self.lookup_config.col0.0, + offset, + || input_coef.ok_or(Error::Synthesis), + )?; + input_cells.push(input.clone()); + + let output = input_coef.and_then(|v| self.map.get(&v.to_repr())); + + let output_b9 = region.assign_advice( + || "base 9", + self.lookup_config.col1.0, + offset, + || output.map(|v| v.0).ok_or(Error::Synthesis), + )?; + output_b9_cells.push(output_b9); + let output_b13 = region.assign_advice( + || "base 13", + self.lookup_config.col2.0, + offset, + || output.map(|v| v.1).ok_or(Error::Synthesis), + )?; + output_b13_cells.push(output_b13); + } + Ok((input_cells, output_b9_cells, output_b13_cells)) + }, + ) + } +} diff --git a/keccak256/src/permutation/theta.rs b/keccak256/src/permutation/theta.rs index d72faf52e1..d443b70002 100644 --- a/keccak256/src/permutation/theta.rs +++ b/keccak256/src/permutation/theta.rs @@ -1,98 +1,43 @@ use crate::arith_helpers::*; +use crate::permutation::generic::GenericConfig; use eth_types::Field; use halo2_proofs::{ circuit::{AssignedCell, Layouter}, - plonk::{Advice, Column, ConstraintSystem, Error, Expression, Selector}, - poly::Rotation, + plonk::Error, }; use itertools::Itertools; -use std::marker::PhantomData; -#[derive(Clone, Debug)] -pub struct ThetaConfig { - q_enable: Selector, - pub(crate) state: [Column; 25], - _marker: PhantomData, -} - -impl ThetaConfig { - pub fn configure( - q_enable: Selector, - meta: &mut ConstraintSystem, - state: [Column; 25], - ) -> ThetaConfig { - meta.create_gate("theta", |meta| { - let q_enable = meta.query_selector(q_enable); - let column_sum: Vec> = (0..5) - .map(|x| { - let state_x0 = meta.query_advice(state[5 * x], Rotation::cur()); - let state_x1 = meta.query_advice(state[5 * x + 1], Rotation::cur()); - let state_x2 = meta.query_advice(state[5 * x + 2], Rotation::cur()); - let state_x3 = meta.query_advice(state[5 * x + 3], Rotation::cur()); - let state_x4 = meta.query_advice(state[5 * x + 4], Rotation::cur()); - state_x0 + state_x1 + state_x2 + state_x3 + state_x4 - }) - .collect::>(); - - (0..5) - .cartesian_product(0..5) - .map(|(x, y)| { - let new_state = meta.query_advice(state[5 * x + y], Rotation::next()); - let old_state = meta.query_advice(state[5 * x + y], Rotation::cur()); - let right = old_state - + column_sum[(x + 4) % 5].clone() - + Expression::Constant(F::from(B13 as u64)) - * column_sum[(x + 1) % 5].clone(); - q_enable.clone() * (new_state - right) - }) - .collect::>() - }); - - ThetaConfig { - q_enable, - state, - _marker: PhantomData, - } - } - - pub fn assign_state( - &self, - layouter: &mut impl Layouter, - state: &[AssignedCell; 25], - out_state: [F; 25], - ) -> Result<[AssignedCell; 25], Error> { - layouter.assign_region( - || "Theta gate", - |mut region| { - let offset = 0; - self.q_enable.enable(&mut region, offset)?; - - for (idx, state) in state.iter().enumerate() { - state.copy_advice( - || format!("assign state {}", idx), - &mut region, - self.state[idx], - offset, - )?; - } - - let mut out_vec: Vec> = vec![]; - let out_state: [AssignedCell; 25] = { - for (idx, lane) in out_state.iter().enumerate() { - let out_cell = region.assign_advice( - || format!("assign out_state {}", idx), - self.state[idx], - offset + 1, - || Ok(*lane), - )?; - out_vec.push(out_cell); - } - out_vec.try_into().unwrap() - }; - Ok(out_state) - }, - ) - } +pub fn assign_theta( + generic: &GenericConfig, + layouter: &mut impl Layouter, + state: &[AssignedCell; 25], +) -> Result<[AssignedCell; 25], Error> { + let theta_col_sums = (0..5) + .map(|x| { + let col_sum = generic.running_sum( + layouter, + (0..5).map(|y| state[5 * x + y].clone()).collect(), + None, + )?; + Ok(col_sum) + }) + .collect::>, Error>>()?; + + let out_state = (0..5) + .cartesian_product(0..5) + .map(|(x, y)| { + let cells = vec![ + state[5 * x + y].clone(), + theta_col_sums[(x + 4) % 5].clone(), + theta_col_sums[(x + 1) % 5].clone(), + ]; + let vs = vec![F::one(), F::one(), F::from(B13 as u64)]; + let new_lane = generic.linear_combine_consts(layouter, cells, vs, None)?; + Ok(new_lane) + }) + .collect::>, Error>>()?; + + Ok(out_state.try_into().unwrap()) } #[cfg(test)] @@ -113,6 +58,30 @@ mod tests { #[test] fn test_theta_gates() { + #[derive(Clone, Debug)] + struct MyConfig { + lane: Column, + generic: GenericConfig, + } + + impl MyConfig { + pub fn configure(meta: &mut ConstraintSystem) -> Self { + let advices: [Column; 3] = (0..3) + .map(|_| { + let column = meta.advice_column(); + meta.enable_equality(column); + column + }) + .collect::>() + .try_into() + .unwrap(); + let fixed = meta.fixed_column(); + + let lane = advices[0]; + let generic = GenericConfig::configure(meta, advices, fixed); + Self { lane, generic } + } + } #[derive(Default)] struct MyCircuit { in_state: [F; 25], @@ -120,7 +89,7 @@ mod tests { _marker: PhantomData, } impl Circuit for MyCircuit { - type Config = ThetaConfig; + type Config = MyConfig; type FloorPlanner = SimpleFloorPlanner; fn without_witnesses(&self) -> Self { @@ -128,19 +97,10 @@ mod tests { } fn configure(meta: &mut ConstraintSystem) -> Self::Config { - let q_enable = meta.complex_selector(); - - let state: [Column; 25] = (0..25) - .map(|_| { - let column = meta.advice_column(); - meta.enable_equality(column); - column - }) - .collect::>() - .try_into() - .unwrap(); - - ThetaConfig::configure(q_enable, meta, state) + // this column is required by `constrain_constant` + let constant = meta.fixed_column(); + meta.enable_constant(constant); + Self::Config::configure(meta) } fn synthesize( @@ -148,17 +108,16 @@ mod tests { config: Self::Config, mut layouter: impl Layouter, ) -> Result<(), Error> { - let offset = 0; let in_state = layouter.assign_region( || "Wittnes & assignation", |mut region| { // Witness `state` let in_state: [AssignedCell; 25] = { let mut state: Vec> = Vec::with_capacity(25); - for (idx, val) in self.in_state.iter().enumerate() { + for (offset, val) in self.in_state.iter().enumerate() { let cell = region.assign_advice( || "witness input state", - config.state[idx], + config.lane, offset, || Ok(*val), )?; @@ -170,8 +129,17 @@ mod tests { }, )?; - config.assign_state(&mut layouter, &in_state, self.out_state)?; + let out_state = assign_theta(&config.generic, &mut layouter, &in_state)?; + layouter.assign_region( + || "Check outstate", + |mut region| { + for (assigned, value) in out_state.iter().zip(self.out_state.iter()) { + region.constrain_constant(assigned.cell(), value)?; + } + Ok(()) + }, + )?; Ok(()) } } diff --git a/keccak256/src/permutation/xi.rs b/keccak256/src/permutation/xi.rs index 1324f6c8a5..6202c2a6e9 100644 --- a/keccak256/src/permutation/xi.rs +++ b/keccak256/src/permutation/xi.rs @@ -1,101 +1,31 @@ +use crate::arith_helpers::{A1, A2, A3}; +use crate::permutation::generic::GenericConfig; use eth_types::Field; use halo2_proofs::{ circuit::{AssignedCell, Layouter}, - plonk::{Advice, Column, ConstraintSystem, Error, Expression, Selector}, - poly::Rotation, + plonk::Error, }; use itertools::Itertools; -use std::marker::PhantomData; -#[derive(Clone, Debug)] -pub struct XiConfig { - #[allow(dead_code)] - q_enable: Selector, - state: [Column; 25], - _marker: PhantomData, -} - -impl XiConfig { - // We assume state is recieved in base-9. - pub fn configure( - q_enable: Selector, - meta: &mut ConstraintSystem, - state: [Column; 25], - ) -> XiConfig { - meta.create_gate("xi", |meta| { - // state in base 9, coefficient in 0~1 - // def xi(state: List[List[int]]): - // new_state = [[0 for x in range(5)] for y in range(5)] - // # Xi step - // for x in range(5): - // for y in range(5): - // # a, b, c, d are base9, coefficient in 0~1 - // a = state[x][y] - // b = state[(x + 1) % 5][y] - // c = state[(x + 2) % 5][y] - // # coefficient in 0~6 - // new_state[x][y] = 2*a + b + 3*c - // return new_state - (0..5) - .cartesian_product(0..5usize) - .map(|(x, y)| { - let a = meta.query_advice(state[5 * x + y], Rotation::cur()); - let b = meta.query_advice(state[5 * ((x + 1) % 5) + y], Rotation::cur()); - let c = meta.query_advice(state[5 * ((x + 2) % 5) + y], Rotation::cur()); - let next_lane = meta.query_advice(state[5 * x + y], Rotation::next()); - meta.query_selector(q_enable) - * ((Expression::Constant(F::from(2)) * a - + b - + Expression::Constant(F::from(3)) * c) - - next_lane) - }) - .collect::>() - }); - - XiConfig { - q_enable, - state, - _marker: PhantomData, - } - } - - pub fn assign_state( - &self, - layouter: &mut impl Layouter, - state: &[AssignedCell; 25], - out_state: [F; 25], - ) -> Result<[AssignedCell; 25], Error> { - layouter.assign_region( - || "Xi assignation", - |mut region| { - let offset = 0; - self.q_enable.enable(&mut region, offset)?; - for (idx, state_item) in state.iter().enumerate() { - state_item.copy_advice( - || format!("assign state {}", idx), - &mut region, - self.state[idx], - offset, - )?; - } - - let mut out_vec: Vec> = vec![]; - let out_state: [AssignedCell; 25] = { - for (idx, lane) in out_state.iter().enumerate() { - let out_cell = region.assign_advice( - || format!("assign out_state {}", idx), - self.state[idx], - offset + 1, - || Ok(*lane), - )?; - out_vec.push(out_cell); - } - out_vec.try_into().unwrap() - }; - Ok(out_state) - }, - ) - } +pub fn assign_xi( + generic: &GenericConfig, + layouter: &mut impl Layouter, + state: &[AssignedCell; 25], +) -> Result<[AssignedCell; 25], Error> { + let out_state = (0..5) + .cartesian_product(0..5) + .map(|(x, y)| { + let cells = vec![ + state[5 * x + y].clone(), + state[5 * ((x + 1) % 5) + y].clone(), + state[5 * ((x + 2) % 5) + y].clone(), + ]; + let vs = vec![F::from(A1), F::from(A2), F::from(A3)]; + let new_lane = generic.linear_combine_consts(layouter, cells, vs, None)?; + Ok(new_lane) + }) + .collect::>, Error>>()?; + Ok(out_state.try_into().unwrap()) } #[cfg(test)] @@ -114,6 +44,30 @@ mod tests { #[test] fn test_xi_gate() { + #[derive(Clone, Debug)] + struct MyConfig { + lane: Column, + generic: GenericConfig, + } + + impl MyConfig { + pub fn configure(meta: &mut ConstraintSystem) -> Self { + let advices: [Column; 3] = (0..3) + .map(|_| { + let column = meta.advice_column(); + meta.enable_equality(column); + column + }) + .collect::>() + .try_into() + .unwrap(); + let fixed = meta.fixed_column(); + + let lane = advices[0]; + let generic = GenericConfig::configure(meta, advices, fixed); + Self { lane, generic } + } + } #[derive(Default)] struct MyCircuit { in_state: [F; 25], @@ -122,7 +76,7 @@ mod tests { } impl Circuit for MyCircuit { - type Config = XiConfig; + type Config = MyConfig; type FloorPlanner = SimpleFloorPlanner; fn without_witnesses(&self) -> Self { @@ -130,19 +84,10 @@ mod tests { } fn configure(meta: &mut ConstraintSystem) -> Self::Config { - let q_enable = meta.complex_selector(); - - let state: [Column; 25] = (0..25) - .map(|_| { - let column = meta.advice_column(); - meta.enable_equality(column); - column - }) - .collect::>() - .try_into() - .unwrap(); - - XiConfig::configure(q_enable, meta, state) + // this column is required by `constrain_constant` + let constant = meta.fixed_column(); + meta.enable_constant(constant); + Self::Config::configure(meta) } fn synthesize( @@ -150,17 +95,16 @@ mod tests { config: Self::Config, mut layouter: impl Layouter, ) -> Result<(), Error> { - let offset = 0; let in_state = layouter.assign_region( || "Wittnes & assignation", |mut region| { // Witness `state` let in_state: [AssignedCell; 25] = { let mut state: Vec> = Vec::with_capacity(25); - for (idx, val) in self.in_state.iter().enumerate() { + for (offset, val) in self.in_state.iter().enumerate() { let cell = region.assign_advice( || "witness input state", - config.state[idx], + config.lane, offset, || Ok(*val), )?; @@ -172,7 +116,17 @@ mod tests { }, )?; - config.assign_state(&mut layouter, &in_state, self.out_state)?; + let out_state = assign_xi(&config.generic, &mut layouter, &in_state)?; + + layouter.assign_region( + || "Check outstate", + |mut region| { + for (assigned, value) in out_state.iter().zip(self.out_state.iter()) { + region.constrain_constant(assigned.cell(), value)?; + } + Ok(()) + }, + )?; Ok(()) } } From 38bb885505acf82174b79636ce688b8fad0910ee Mon Sep 17 00:00:00 2001 From: ChihChengLiang Date: Thu, 30 Jun 2022 00:47:38 +0100 Subject: [PATCH 03/12] prettify optional handling --- keccak256/src/permutation/generic.rs | 129 +++++++++++++++------------ 1 file changed, 71 insertions(+), 58 deletions(-) diff --git a/keccak256/src/permutation/generic.rs b/keccak256/src/permutation/generic.rs index ef23d70da0..81df2d14d1 100644 --- a/keccak256/src/permutation/generic.rs +++ b/keccak256/src/permutation/generic.rs @@ -63,54 +63,61 @@ impl GenericConfig { |mut region| { let offset = 0; self.q_enable.enable(&mut region, offset)?; - let input = input.as_ref().map_or( - region.assign_advice_from_constant(|| "input is 0", self.io, offset, F::zero()), - |input| input.copy_advice(|| "input", &mut region, self.io, offset), - )?; + let input = input + .as_ref() + .map(|input| input.copy_advice(|| "input", &mut region, self.io, offset)) + .unwrap_or_else(|| { + region.assign_advice_from_constant( + || "input is 0", + self.io, + offset, + F::zero(), + ) + })?; - let left = match &left { - Some(x) => { + let left = left + .as_ref() + .map(|x| // copy x to use as a flag - (*x).copy_advice(|| "left adv", &mut region, self.left, offset)? - } - None => { + x.copy_advice(|| "left adv", &mut region, self.left, offset)) + .unwrap_or_else(|| { // constrain advice to 1 for a simple add. region.assign_advice_from_constant( || "left const", self.left, offset, F::one(), - )? - } - }; + ) + })?; - let right = match &right { - Some(right) => { + let right = right + .as_ref() + .map(|right| { if value.is_some() { panic!("right and value can't be both some"); } - right.copy_advice(|| "right adv", &mut region, self.right, offset)? - } - None => { - match value { - Some(value) => region.assign_advice_from_constant( - || "fixed value", - self.right, - offset, - value, - )?, - None => { + right.copy_advice(|| "right adv", &mut region, self.right, offset) + }) + .unwrap_or_else(|| { + value + .map(|value| { + region.assign_advice_from_constant( + || "fixed value", + self.right, + offset, + value, + ) + }) + .unwrap_or_else(|| { // constrain fixed to 1 for a simple add. region.assign_advice_from_constant( || "fixed value", self.right, offset, F::one(), - )? - } - } - } - }; + ) + }) + })?; let offset = 1; region.assign_advice( @@ -118,9 +125,12 @@ impl GenericConfig { self.io, offset, || { - Ok(input.value().cloned().ok_or(Error::Synthesis)? - + left.value().cloned().ok_or(Error::Synthesis)? - * right.value().cloned().ok_or(Error::Synthesis)?) + input + .value() + .zip(left.value()) + .zip(right.value()) + .map(|((&input, &left), &right)| input + left * right) + .ok_or(Error::Synthesis) }, ) }, @@ -217,51 +227,54 @@ impl GenericConfig { // | ... | ... | ... | ... | // | N - 1 | | x_(N-1) | y_(N-1) | // | N | (sum) | | | - let mut acc = region.assign_advice(|| "input 0", self.io, 0, || Ok(F::zero()))?; - region.constrain_constant(acc.cell(), F::zero())?; + let mut acc = + region.assign_advice_from_constant(|| "input 0", self.io, 0, F::zero())?; + let mut sum = F::zero(); for (offset, x) in xs.iter().enumerate() { self.q_enable.enable(&mut region, offset)?; x.copy_advice(|| "x", &mut region, self.left, offset)?; - let right = { - match &vs { - Some(vs) => region.assign_advice_from_constant( + let right = vs + .as_ref() + .map(|vs| { + region.assign_advice_from_constant( || "v", self.right, offset, vs[offset], - )?, - None => match &ys { - Some(ys) => ys[offset].copy_advice( - || "y", - &mut region, - self.right, - offset, - )?, - None => { - unreachable!() - } - }, - } - }; + ) + }) + .unwrap_or_else(|| { + ys.as_ref() + .map(|ys| { + ys[offset].copy_advice(|| "y", &mut region, self.right, offset) + }) + .expect("ys should have something") + })?; acc = region.assign_advice( || "accumulation", self.io, offset + 1, || { - sum += x.value().cloned().ok_or(Error::Synthesis)? - * right.value().cloned().ok_or(Error::Synthesis)?; + sum += x + .value() + .zip(right.value()) + .map(|(&x, &right)| x * right) + .ok_or(Error::Synthesis)?; Ok(sum) }, )?; } if let Some(outcome) = &outcome { - if outcome.value().is_some() && acc.value().is_some() { - debug_assert_eq!(outcome.value(), acc.value()); - } - region.constrain_equal(outcome.cell(), acc.cell())?; } + if let Some((outcome, acc)) = outcome + .as_ref() + .map(|oc| oc.value().zip(acc.value())) + .flatten() + { + debug_assert_eq!(outcome, acc); + } Ok(acc) }, ) From 9c9442d3863adb16141577a2599c518dad120b74 Mon Sep 17 00:00:00 2001 From: ChihChengLiang Date: Thu, 30 Jun 2022 01:26:57 +0100 Subject: [PATCH 04/12] fix base2 to 9 conv --- keccak256/src/permutation/circuit.rs | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/keccak256/src/permutation/circuit.rs b/keccak256/src/permutation/circuit.rs index 7d71a7bd61..021d5e3eee 100644 --- a/keccak256/src/permutation/circuit.rs +++ b/keccak256/src/permutation/circuit.rs @@ -1,7 +1,7 @@ use crate::{ arith_helpers::*, common::{NEXT_INPUTS_LANES, PERMUTATION}, - gate_helpers::{biguint_to_f, f_to_biguint}, + gate_helpers::biguint_to_f, permutation::{ generic::GenericConfig, iota::IotaConstants, @@ -52,17 +52,7 @@ fn assign_next_input( || "next input words", *next_input_col, offset, - || { - Ok(input - .map(|input| { - let input = f_to_biguint(input); - let input = convert_b2_to_b9( - *input.to_u64_digits().first().unwrap_or(&0u64), - ); - biguint_to_f(&input) - }) - .unwrap_or(F::zero())) - }, + || Ok(input.unwrap_or_default()), )?; next_input_b9.push(cell); } @@ -353,8 +343,7 @@ mod tests { } Ok(()) }, - )?; - Ok(()) + ) } } From af97d15d9871065b6355b87040a6a49c4edbccd5 Mon Sep 17 00:00:00 2001 From: ChihChengLiang Date: Thu, 30 Jun 2022 19:45:08 +0100 Subject: [PATCH 05/12] test all fixed --- keccak256/src/arith_helpers.rs | 4 +++ keccak256/src/permutation/circuit.rs | 40 +++++++++++--------------- keccak256/src/permutation/generic.rs | 42 ++++++++++++++-------------- keccak256/src/permutation/mixing.rs | 12 ++++---- keccak256/src/permutation/rho.rs | 2 +- keccak256/src/permutation/tables.rs | 2 +- 6 files changed, 50 insertions(+), 52 deletions(-) diff --git a/keccak256/src/arith_helpers.rs b/keccak256/src/arith_helpers.rs index 62d5b8207b..0104d81a32 100644 --- a/keccak256/src/arith_helpers.rs +++ b/keccak256/src/arith_helpers.rs @@ -175,6 +175,10 @@ pub fn convert_b9_lane_to_b2(x: Lane9) -> u64 { .unwrap_or(0) } +pub fn convert_b9_lane_to_b2_biguint(x: Lane9) -> BigUint { + convert_lane(x, B9, 2, convert_b9_coef) +} + pub fn convert_b9_lane_to_b2_normal(x: Lane9) -> u64 { convert_lane(x, B9, 2, |y| y) .iter_u64_digits() diff --git a/keccak256/src/permutation/circuit.rs b/keccak256/src/permutation/circuit.rs index 021d5e3eee..775e4ce81c 100644 --- a/keccak256/src/permutation/circuit.rs +++ b/keccak256/src/permutation/circuit.rs @@ -138,7 +138,6 @@ impl KeccakFConfig { .unwrap(); let stackable = StackableTable::configure(meta, advices, stackable_cols); let base13to9_config = Base13toBase9TableConfig::configure(meta, advices, base13to9_cols); - // Base conversion config. let from_b9_table = FromBase9TableConfig::configure(meta, advices, from_base9_cols); let from_b2_table = FromBinaryTableConfig::configure(meta, advices, from_base2_cols); @@ -191,8 +190,8 @@ impl KeccakFConfig { state[0] = self.generic.add_fixed( layouter, - state[0].clone(), - iota_constants.a4_times_round_constants_b9[round_idx], + &state[0], + &iota_constants.a4_times_round_constants_b9[round_idx], )?; // The resulting state is in Base-9 now. We now convert it to @@ -200,15 +199,13 @@ impl KeccakFConfig { // start of the loop. state = convert_from_b9_to_b13(layouter, &self.from_b9_table, &self.generic, state)?; } - let (f_pos, f_neg) = self.stackable.assign_boolean_flag(layouter, Some(flag))?; - + let (f_mix, f_no_mix) = self.stackable.assign_boolean_flag(layouter, Some(flag))?; state[0] = self.generic.conditional_add_const( layouter, - state[0].clone(), - f_neg.clone(), - iota_constants.a4_times_round_constants_b9[PERMUTATION - 1], + &state[0], + &f_no_mix, + &iota_constants.a4_times_round_constants_b9[PERMUTATION - 1], )?; - let next_input = assign_next_input(layouter, &self.advice, &next_mixing)?; // Convert to base 9 and multiply by A4 @@ -233,37 +230,34 @@ impl KeccakFConfig { )?; let vs = (0..NUM_OF_BINARY_SLICES) .map(|i| { - biguint_to_f( - &BigUint::from(B13) - .pow((NUM_OF_BINARY_CHUNKS_PER_SLICE * i) as u32), - ) + biguint_to_f::( + &BigUint::from(B9).pow((NUM_OF_BINARY_CHUNKS_PER_SLICE * i) as u32), + ) * F::from(A4) }) .rev() .collect_vec(); let output = self.generic .linear_combine_consts(layouter, base9s.clone(), vs, None)?; - self.generic.mul_fixed(layouter, output, F::from(A4)) + Ok(output) }) .collect::>, Error>>()?; let next_input: [AssignedCell; NEXT_INPUTS_LANES] = next_input.try_into().unwrap(); next_input }; + for (i, input) in next_input.iter().enumerate() { - state[i] = self.generic.conditional_add_advice( - layouter, - state[i].clone(), - f_pos.clone(), - input.clone(), - )?; + state[i] = self + .generic + .conditional_add_advice(layouter, &state[i], &f_mix, &input)?; } state = convert_from_b9_to_b13(layouter, &self.from_b9_table, &self.generic, state)?; state[0] = self.generic.conditional_add_const( layouter, - state[0].clone(), - f_pos.clone(), - iota_constants.round_constant_b13, + &state[0], + &f_mix, + &iota_constants.round_constant_b13, )?; Ok(state.try_into().unwrap()) } diff --git a/keccak256/src/permutation/generic.rs b/keccak256/src/permutation/generic.rs index 81df2d14d1..3daeaa5eb4 100644 --- a/keccak256/src/permutation/generic.rs +++ b/keccak256/src/permutation/generic.rs @@ -53,10 +53,10 @@ impl GenericConfig { fn add_generic( &self, layouter: &mut impl Layouter, - input: Option>, - left: Option>, - right: Option>, - value: Option, + input: Option<&AssignedCell>, + left: Option<&AssignedCell>, + right: Option<&AssignedCell>, + value: Option<&F>, ) -> Result, Error> { layouter.assign_region( || "add advice", @@ -100,7 +100,7 @@ impl GenericConfig { }) .unwrap_or_else(|| { value - .map(|value| { + .map(|&value| { region.assign_advice_from_constant( || "fixed value", self.right, @@ -140,9 +140,9 @@ impl GenericConfig { pub fn add_advice_mul_const( &self, layouter: &mut impl Layouter, - input: AssignedCell, - x: AssignedCell, - v: F, + input: &AssignedCell, + x: &AssignedCell, + v: &F, ) -> Result, Error> { self.add_generic(layouter, Some(input), Some(x), None, Some(v)) } @@ -150,17 +150,17 @@ impl GenericConfig { pub fn sub_advice( &self, layouter: &mut impl Layouter, - input: AssignedCell, - x: AssignedCell, + input: &AssignedCell, + x: &AssignedCell, ) -> Result, Error> { - self.add_generic(layouter, Some(input), Some(x), None, Some(-F::one())) + self.add_generic(layouter, Some(input), Some(x), None, Some(&(-F::one()))) } /// input += v pub fn add_fixed( &self, layouter: &mut impl Layouter, - input: AssignedCell, - value: F, + input: &AssignedCell, + value: &F, ) -> Result, Error> { self.add_generic(layouter, Some(input), None, None, Some(value)) } @@ -168,8 +168,8 @@ impl GenericConfig { pub fn mul_fixed( &self, layouter: &mut impl Layouter, - input: AssignedCell, - value: F, + input: &AssignedCell, + value: &F, ) -> Result, Error> { self.add_generic(layouter, None, Some(input), None, Some(value)) } @@ -179,9 +179,9 @@ impl GenericConfig { pub fn conditional_add_const( &self, layouter: &mut impl Layouter, - input: AssignedCell, - flag: AssignedCell, - value: F, + input: &AssignedCell, + flag: &AssignedCell, + value: &F, ) -> Result, Error> { self.add_generic(layouter, Some(input), Some(flag), None, Some(value)) } @@ -191,9 +191,9 @@ impl GenericConfig { pub fn conditional_add_advice( &self, layouter: &mut impl Layouter, - input: AssignedCell, - flag: AssignedCell, - x: AssignedCell, + input: &AssignedCell, + flag: &AssignedCell, + x: &AssignedCell, ) -> Result, Error> { self.add_generic(layouter, Some(input), Some(flag), Some(x), None) } diff --git a/keccak256/src/permutation/mixing.rs b/keccak256/src/permutation/mixing.rs index e473205b1f..7176216646 100644 --- a/keccak256/src/permutation/mixing.rs +++ b/keccak256/src/permutation/mixing.rs @@ -141,9 +141,9 @@ impl MixingConfig { // next cell state[0] = self.generic.conditional_add_const( layouter, - state[0].clone(), - negated_flag.clone(), - self.iota_constants.a4_times_round_constants_b9[PERMUTATION - 1], + &state[0], + &negated_flag, + &self.iota_constants.a4_times_round_constants_b9[PERMUTATION - 1], )?; state }; @@ -184,9 +184,9 @@ impl MixingConfig { // next cell base_conv_cells[0] = self.generic.conditional_add_const( layouter, - base_conv_cells[0].clone(), - flag, - self.iota_constants.round_constant_b13, + &base_conv_cells[0], + &flag, + &self.iota_constants.round_constant_b13, )?; base_conv_cells }; diff --git a/keccak256/src/permutation/rho.rs b/keccak256/src/permutation/rho.rs index 1e72dab6a0..4d0be98321 100644 --- a/keccak256/src/permutation/rho.rs +++ b/keccak256/src/permutation/rho.rs @@ -50,7 +50,7 @@ pub fn assign_rho( let input_from_chunks = generic.linear_combine_consts(layouter, input_coefs, input_pobs, None)?; - let diff = generic.sub_advice(layouter, lane.clone(), input_from_chunks)?; + let last_chunk = generic.sub_advice(layouter, &lane, &input_from_chunks)?; let final_output_coef = stackable.lookup_special_chunks(layouter, &diff)?; output_coefs.push(final_output_coef); diff --git a/keccak256/src/permutation/tables.rs b/keccak256/src/permutation/tables.rs index ca7e8bbf1e..20f5e9ccfd 100644 --- a/keccak256/src/permutation/tables.rs +++ b/keccak256/src/permutation/tables.rs @@ -647,7 +647,7 @@ impl FromBinaryTableConfig { .multi_cartesian_product() .enumerate() { - let input_b2 = f_from_radix_be::(&b2_chunks, B9); + let input_b2 = f_from_radix_be::(&b2_chunks, B2); let output_b9 = f_from_radix_be::(&b2_chunks, B9); let output_b13 = f_from_radix_be::(&b2_chunks, B13); From 177f8065c6b7bf3788cb1452cc8af45191bd9f35 Mon Sep 17 00:00:00 2001 From: ChihChengLiang Date: Thu, 30 Jun 2022 22:34:22 +0100 Subject: [PATCH 06/12] add the conv to b2 part --- keccak256/src/permutation/circuit.rs | 91 +++++++++++++++++++++------- 1 file changed, 68 insertions(+), 23 deletions(-) diff --git a/keccak256/src/permutation/circuit.rs b/keccak256/src/permutation/circuit.rs index 775e4ce81c..91e60eb3ab 100644 --- a/keccak256/src/permutation/circuit.rs +++ b/keccak256/src/permutation/circuit.rs @@ -67,11 +67,12 @@ fn convert_from_b9_to_b13( from_b9_table: &FromBase9TableConfig, generic: &GenericConfig, state: [AssignedCell; 25], -) -> Result<[AssignedCell; 25], Error> { - let state = state + output_b2: bool, +) -> Result<([AssignedCell; 25], Option<[AssignedCell; 25]>), Error> { + let (state_b13, state_b2): (Vec>, Vec>>) = state .iter() .map(|lane| { - let (base9s, base_13s, _) = from_b9_table.assign_region(layouter, lane)?; + let (base9s, base_13s, base_2s) = from_b9_table.assign_region(layouter, lane)?; let vs = (0..NUM_OF_B9_SLICES) .map(|i| { biguint_to_f(&BigUint::from(B9).pow((NUM_OF_B9_CHUNKS_PER_SLICE * i) as u32)) @@ -85,10 +86,33 @@ fn convert_from_b9_to_b13( }) .rev() .collect_vec(); - generic.linear_combine_consts(layouter, base_13s, vs, None) + let lane_b13 = generic.linear_combine_consts(layouter, base_13s, vs, None)?; + let lane_b2 = if output_b2 { + let vs = (0..NUM_OF_B9_SLICES) + .map(|i| { + biguint_to_f( + &BigUint::from(B2).pow((NUM_OF_B9_CHUNKS_PER_SLICE * i) as u32), + ) + }) + .rev() + .collect_vec(); + let lane_b2 = generic.linear_combine_consts(layouter, base_2s, vs, None)?; + Some(lane_b2) + } else { + None + }; + Ok((lane_b13, lane_b2)) }) - .collect::>, Error>>()?; - Ok(state.try_into().unwrap()) + .collect::, Error>>()? + .iter() + .cloned() + .unzip(); + let state_b2: Option<[AssignedCell; 25]> = state_b2 + .into_iter() + .collect::>>() + .map(|v| v.try_into().unwrap()); + + Ok((state_b13.try_into().unwrap(), state_b2)) } #[derive(Clone, Debug)] @@ -158,13 +182,14 @@ impl KeccakFConfig { self.from_b2_table.load(layouter) } + // Result b13 state for next round, b2 state for end result pub fn assign_all( &self, layouter: &mut impl Layouter, in_state: [AssignedCell; 25], flag: bool, next_mixing: Option<[F; NEXT_INPUTS_LANES]>, - ) -> Result<[AssignedCell; 25], Error> { + ) -> Result<([AssignedCell; 25], [AssignedCell; 25]), Error> { let iota_constants = IotaConstants::default(); let mut state = in_state; @@ -197,7 +222,9 @@ impl KeccakFConfig { // The resulting state is in Base-9 now. We now convert it to // base_13 which is what Theta requires again at the // start of the loop. - state = convert_from_b9_to_b13(layouter, &self.from_b9_table, &self.generic, state)?; + state = + convert_from_b9_to_b13(layouter, &self.from_b9_table, &self.generic, state, false)? + .0; } let (f_mix, f_no_mix) = self.stackable.assign_boolean_flag(layouter, Some(flag))?; state[0] = self.generic.conditional_add_const( @@ -252,14 +279,16 @@ impl KeccakFConfig { .generic .conditional_add_advice(layouter, &state[i], &f_mix, &input)?; } - state = convert_from_b9_to_b13(layouter, &self.from_b9_table, &self.generic, state)?; - state[0] = self.generic.conditional_add_const( + let (mut state_b13, state_b2) = + convert_from_b9_to_b13(layouter, &self.from_b9_table, &self.generic, state, true)?; + let state_b2 = state_b2.unwrap(); + state_b13[0] = self.generic.conditional_add_const( layouter, - &state[0], + &state_b13[0], &f_mix, &iota_constants.round_constant_b13, )?; - Ok(state.try_into().unwrap()) + Ok((state_b13.try_into().unwrap(), state_b2)) } } @@ -326,18 +355,29 @@ mod tests { }, )?; - let state = + let (state_b13, state_b2) = config.assign_all(&mut layouter, state, self.is_mixing, self.next_mixing)?; - - layouter.assign_region( - || "check final states", - |mut region| { - for (assigned, value) in state.iter().zip(self.out_state.iter()) { - region.constrain_constant(assigned.cell(), value)?; - } - Ok(()) - }, - ) + if self.is_mixing { + layouter.assign_region( + || "check final states", + |mut region| { + for (assigned, value) in state_b13.iter().zip(self.out_state.iter()) { + region.constrain_constant(assigned.cell(), value)?; + } + Ok(()) + }, + ) + } else { + layouter.assign_region( + || "check final states", + |mut region| { + for (assigned, value) in state_b2.iter().zip(self.out_state.iter()) { + region.constrain_constant(assigned.cell(), value)?; + } + Ok(()) + }, + ) + } } } @@ -374,6 +414,11 @@ mod tests { let mut out_state_non_mix = in_state_biguint.clone(); KeccakFArith::permute_and_absorb(&mut out_state_non_mix, None); + for (x, y) in (0..5).cartesian_product(0..5) { + out_state_non_mix[(x, y)] = + convert_b9_lane_to_b2_biguint(out_state_non_mix[(x, y)].clone()) + } + // Generate out_state as `[Fp;25]` let out_state_mix: [Fp; 25] = state_bigint_to_field(out_state_mix); let out_state_non_mix: [Fp; 25] = state_bigint_to_field(out_state_non_mix); From b92e1ac74a2ee07fed7f9b24f7c4c98ebad6fe8c Mon Sep 17 00:00:00 2001 From: ChihChengLiang Date: Thu, 30 Jun 2022 22:46:49 +0100 Subject: [PATCH 07/12] rm absorb and mixing --- keccak256/src/permutation.rs | 2 - keccak256/src/permutation/absorb.rs | 346 --------------------- keccak256/src/permutation/mixing.rs | 452 ---------------------------- 3 files changed, 800 deletions(-) delete mode 100644 keccak256/src/permutation/absorb.rs delete mode 100644 keccak256/src/permutation/mixing.rs diff --git a/keccak256/src/permutation.rs b/keccak256/src/permutation.rs index 2045343244..749e655432 100644 --- a/keccak256/src/permutation.rs +++ b/keccak256/src/permutation.rs @@ -1,10 +1,8 @@ #![allow(clippy::type_complexity)] #![allow(clippy::too_many_arguments)] -pub(crate) mod absorb; pub mod circuit; pub(crate) mod generic; pub(crate) mod iota; -pub(crate) mod mixing; pub(crate) mod pi; pub(crate) mod rho; pub(crate) mod rho_helpers; diff --git a/keccak256/src/permutation/absorb.rs b/keccak256/src/permutation/absorb.rs deleted file mode 100644 index b81e459e4e..0000000000 --- a/keccak256/src/permutation/absorb.rs +++ /dev/null @@ -1,346 +0,0 @@ -use crate::arith_helpers::*; -use crate::common::*; -use eth_types::Field; -use halo2_proofs::circuit::{AssignedCell, Layouter, Region}; -use halo2_proofs::{ - plonk::{Advice, Column, ConstraintSystem, Error, Expression, Selector}, - poly::Rotation, -}; -use itertools::Itertools; -use std::marker::PhantomData; - -#[derive(Clone, Debug)] -pub struct AbsorbConfig { - q_mixing: Selector, - state: [Column; 25], - _marker: PhantomData, -} - -impl AbsorbConfig { - // We assume state is recieved in base-9. - // Rows are assigned as: - // 1) STATE (25 columns) (offset -1) - // 2) NEXT_INPUTS (17 columns) + is_mixing flag (1 column) (offset +0) - // (current rotation) - // 3) OUT_STATE (25 columns) (offset +1) - pub fn configure( - meta: &mut ConstraintSystem, - state: [Column; 25], - ) -> AbsorbConfig { - // def absorb(state: List[List[int], next_input: List[List[int]): - // for x in range(5): - // for y in range(5): - // # state[x][y] has 2*a + b + 3*c already, now add 2*d to - // make it 2*a + b + 3*c + 2*d # coefficient in 0~8 - // state[x][y] += 2 * next_input[x][y] - // return state - - // Declare the q_mixing. - let q_mixing = meta.selector(); - state - .iter() - .for_each(|column| meta.enable_equality(*column)); - - meta.create_gate("absorb", |meta| { - // We do a trick which consists on multiplying an internal selector - // which is always active by the actual `is_mixing` flag - // which will then enable or disable the gate. - let q_enable = { - // We query the flag value from the `state` `Advice` column at - // rotation curr and position = `NEXT_INPUTS_LANES + 1` - // and multiply to it the active selector so that we avoid the - // `PoisonedConstraints` and each gate equation - // can be satisfied while enforcing the correct gate logic. - // - // This is boolean-constrained outside of `AbsorbConfig` by `MixingConfig`. - let flag = meta.query_advice(state[NEXT_INPUTS_LANES], Rotation::cur()); - // Note also that we want to enable the gate when `is_mixing` is - // true. (flag = 1). See the flag computation above. - meta.query_selector(q_mixing) * flag - }; - - (0..NEXT_INPUTS_LANES) - .map(|idx| { - let val = meta.query_advice(state[idx], Rotation::prev()) - + (Expression::Constant(F::from(A4)) - * meta.query_advice(state[idx], Rotation::cur())); - - let next_lane = meta.query_advice(state[idx], Rotation::next()); - - q_enable.clone() * (val - next_lane) - }) - .collect::>() - }); - - AbsorbConfig { - q_mixing, - state, - _marker: PhantomData, - } - } - - fn assign_next_inp_and_flag( - &self, - region: &mut Region, - offset: usize, - flag: AssignedCell, - next_input: [F; NEXT_INPUTS_LANES], - ) -> Result, Error> { - // Generate next_input in base-9. - let mut next_mixing = state_to_biguint::(next_input); - for (x, y) in (0..5).cartesian_product(0..5) { - // Assign only first 17 values. - if x >= 3 && y >= 1 { - break; - } - next_mixing[(x, y)] = convert_b2_to_b9(next_mixing[(x, y)].clone().try_into().unwrap()) - } - let next_input = state_bigint_to_field::(next_mixing); - - // Assign next_mixing. - for (idx, lane) in next_input.iter().enumerate() { - region.assign_advice( - || format!("assign next_input {}", idx), - self.state[idx], - offset, - || Ok(*lane), - )?; - } - - // Assign flag at last column(17th). - let flag_assig_cell = flag.copy_advice( - || format!("assign next_input {}", NEXT_INPUTS_LANES), - region, - self.state[NEXT_INPUTS_LANES], - offset, - )?; - - Ok(flag_assig_cell) - } - - /// Doc this $ - pub fn copy_state_flag_next_inputs( - &self, - layouter: &mut impl Layouter, - in_state: &[AssignedCell; 25], - out_state: [F; 25], - // Passed in base-2 and converted internally after witnessing it. - next_input: [F; NEXT_INPUTS_LANES], - flag: AssignedCell, - ) -> Result<([AssignedCell; 25], AssignedCell), Error> { - layouter.assign_region( - || "Absorb state assignations", - |mut region| { - let mut offset = 0; - // State at offset + 0 - for (idx, in_state) in in_state.iter().enumerate() { - in_state.copy_advice( - || format!("assign state {}", idx), - &mut region, - self.state[idx], - offset, - )?; - } - - offset += 1; - // Enable `q_mixing` at `offset + 1` - self.q_mixing.enable(&mut region, offset)?; - - // Assign `next_inputs` and flag. - let flag = - self.assign_next_inp_and_flag(&mut region, offset, flag.clone(), next_input)?; - - offset += 1; - // Assign out_state at offset + 2 - let mut state: Vec> = Vec::with_capacity(25); - for (idx, lane) in out_state.iter().enumerate() { - let assig_cell = region.assign_advice( - || format!("assign state {}", idx), - self.state[idx], - offset, - || Ok(*lane), - )?; - state.push(assig_cell); - } - let out_state: [AssignedCell; 25] = state - .try_into() - .expect("Unexpected into_slice conversion err"); - - Ok((out_state, flag)) - }, - ) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::keccak_arith::KeccakFArith; - use halo2_proofs::pairing; - use halo2_proofs::{circuit::SimpleFloorPlanner, dev::MockProver, plonk::Circuit}; - use pairing::bn256::Fr as Fp; - use pairing::group::ff::PrimeField; - use pretty_assertions::assert_eq; - - #[test] - fn test_absorb_gate() { - #[derive(Default)] - struct MyCircuit { - in_state: [F; 25], - out_state: [F; 25], - next_input: [F; NEXT_INPUTS_LANES], - is_mixing: bool, - _marker: PhantomData, - } - impl Circuit for MyCircuit - where - F: PrimeField, - { - type Config = AbsorbConfig; - type FloorPlanner = SimpleFloorPlanner; - - fn without_witnesses(&self) -> Self { - Self::default() - } - - fn configure(meta: &mut ConstraintSystem) -> Self::Config { - let state: [Column; 25] = (0..25) - .map(|_| { - let column = meta.advice_column(); - meta.enable_equality(column); - column - }) - .collect::>() - .try_into() - .unwrap(); - - AbsorbConfig::configure(meta, state) - } - - fn synthesize( - &self, - config: Self::Config, - mut layouter: impl Layouter, - ) -> Result<(), Error> { - let val: F = (self.is_mixing as u64).into(); - let flag: AssignedCell = layouter.assign_region( - || "witness_is_mixing_flag", - |mut region| { - let offset = 1; - region.assign_advice( - || "assign is_mixing", - config.state[NEXT_INPUTS_LANES + 1], - offset, - || Ok(val), - ) - }, - )?; - - // Witness `in_state`. - let in_state: [AssignedCell; 25] = layouter.assign_region( - || "Witness input state", - |mut region| { - let mut state: Vec> = Vec::with_capacity(25); - for (idx, val) in self.in_state.iter().enumerate() { - let cell = region.assign_advice( - || "witness input state", - config.state[idx], - 0, - || Ok(*val), - )?; - state.push(cell) - } - - Ok(state.try_into().unwrap()) - }, - )?; - - config.copy_state_flag_next_inputs( - &mut layouter, - &in_state, - self.out_state, - self.next_input, - flag, - )?; - - Ok(()) - } - } - - let input1: State = [ - [1, 0, 0, 0, 0], - [0, 0, 0, 0, 0], - [0, 0, 0, 0, 0], - [0, 0, 0, 0, 0], - [0, 0, 0, 0, 0], - ]; - - let input2: State = [ - [2, 0, 0, 0, 0], - [0, 0, 0, 0, 0], - [0, 0, 0, 0, 0], - [0, 0, 0, 0, 0], - [0, 0, 0, 0, 0], - ]; - - // Convert the input to base9 as the gadget already expects it like this - // since it's always the output of IotaB9. - let mut in_state = StateBigInt::from(input1); - for (x, y) in (0..5).cartesian_product(0..5) { - in_state[(x, y)] = convert_b2_to_b9(input1[x][y]) - } - - let in_state = state_bigint_to_field(in_state); - let out_state = - state_bigint_to_field(KeccakFArith::absorb(&StateBigInt::from(input1), &input2)); - - let next_input = state_bigint_to_field(StateBigInt::from(input2)); - - // With flag set to true, the gate should trigger. - { - // With the correct input and output witnesses, the proof should - // pass. - let circuit = MyCircuit:: { - in_state, - out_state, - next_input, - is_mixing: true, - _marker: PhantomData, - }; - - let prover = MockProver::::run(9, &circuit, vec![]).unwrap(); - - assert_eq!(prover.verify(), Ok(())); - - // With wrong input and/or output witnesses, the proof should fail - // to be verified. - let circuit = MyCircuit:: { - in_state, - out_state: in_state, - next_input, - is_mixing: true, - _marker: PhantomData, - }; - - let prover = MockProver::::run(9, &circuit, vec![]).unwrap(); - - assert!(prover.verify().is_err()); - } - - // With flag set to `false`, the gate shouldn't trigger. - // And so we can pass any witness data and the proof should pass. - { - let circuit = MyCircuit:: { - in_state, - out_state: in_state, - next_input, - is_mixing: false, - _marker: PhantomData, - }; - - let prover = MockProver::::run(9, &circuit, vec![]).unwrap(); - - assert_eq!(prover.verify(), Ok(())); - } - } -} diff --git a/keccak256/src/permutation/mixing.rs b/keccak256/src/permutation/mixing.rs deleted file mode 100644 index 7176216646..0000000000 --- a/keccak256/src/permutation/mixing.rs +++ /dev/null @@ -1,452 +0,0 @@ -use super::super::arith_helpers::*; -use super::generic::GenericConfig; -use super::tables::{FromBase9TableConfig, StackableTable}; -use super::{absorb::AbsorbConfig, iota::IotaConstants}; -use crate::common::*; -use crate::keccak_arith::KeccakFArith; -use eth_types::Field; -use halo2_proofs::{ - circuit::{AssignedCell, Layouter, Region}, - plonk::{Advice, Column, ConstraintSystem, Error, Selector}, - poly::Rotation, -}; - -#[derive(Clone, Debug)] -pub struct MixingConfig { - iota_constants: IotaConstants, - absorb_config: AbsorbConfig, - pub table: FromBase9TableConfig, - state: [Column; 25], - flag: Column, - q_out_copy: Selector, - generic: GenericConfig, - stackable: StackableTable, -} - -impl MixingConfig { - pub fn configure( - meta: &mut ConstraintSystem, - table: FromBase9TableConfig, - state: [Column; 25], - generic: GenericConfig, - stackable: StackableTable, - ) -> MixingConfig { - let flag = meta.advice_column(); - meta.enable_equality(flag); - // We mix -> Flag = true - let absorb_config = AbsorbConfig::configure(meta, state); - - let q_out_copy = meta.selector(); - - meta.create_gate("Mixing result copies and constraints", |meta| { - let q_enable = meta.query_selector(q_out_copy); - // Add out mixing states together multiplied by the mixing_flag. - let negated_flag = meta.query_advice(flag, Rotation::next()); - let flag = meta.query_advice(flag, Rotation::cur()); - - // Multiply by flag and negated_flag the out mixing results. - let left_side = meta.query_advice(state[0], Rotation::cur()) * negated_flag; - let right_side = meta.query_advice(state[0], Rotation::next()) * flag; - let out_state = meta.query_advice(state[0], Rotation(2)); - - // We add the results of the mixing gate if/else branches multiplied - // by it's corresponding flags so that we always - // copy from the same place on the copy_constraints while enforcing - // the equality with the out_state of the permutation. - [q_enable * ((left_side + right_side) - out_state)] - }); - let iota_constants = IotaConstants::default(); - - MixingConfig { - iota_constants, - absorb_config, - table, - state, - flag, - q_out_copy, - generic, - stackable, - } - } - - /// Enforce flag constraints - pub fn assign_out_mixing_states( - &self, - layouter: &mut impl Layouter, - flag_bool: bool, - negated_flag: AssignedCell, - out_mixing_circ: &[AssignedCell; 25], - out_non_mixing_circ: &[AssignedCell; 25], - out_state: [F; 25], - ) -> Result<[AssignedCell; 25], Error> { - layouter.assign_region( - || "Out Mixing states assignation", - |mut region| { - // Enable selector - self.q_out_copy.enable(&mut region, 0)?; - - // Copy constrain flags. - let _flag_cell = region.assign_advice( - || "witness is_mixing", - self.flag, - 0, - || Ok(F::from(flag_bool as u64)), - )?; - - negated_flag.copy_advice(|| "witness is_mixing", &mut region, self.flag, 1)?; - - // Copy-constrain both out states. - self.copy_state(&mut region, 0, self.state, out_non_mixing_circ)?; - - self.copy_state(&mut region, 1, self.state, out_mixing_circ)?; - - let out_state: [AssignedCell; 25] = { - let mut out_vec: Vec> = vec![]; - for (idx, lane) in out_state.iter().enumerate() { - let out_cell = region.assign_advice( - || format!("assign out_state [{}]", idx), - self.state[idx], - 2, - || Ok(*lane), - )?; - out_vec.push(out_cell); - } - out_vec.try_into().unwrap() - }; - - Ok(out_state) - }, - ) - } - - pub fn assign_state( - &self, - layouter: &mut impl Layouter, - in_state: &[AssignedCell; 25], - out_state: [F; 25], - flag_bool: bool, - next_mixing: Option<[F; NEXT_INPUTS_LANES]>, - ) -> Result<[AssignedCell; 25], Error> { - // Enforce flag constraints and witness them. - let (flag, negated_flag) = self - .stackable - .assign_boolean_flag(layouter, Some(flag_bool))?; - - // If we don't mix: - // IotaB9 - let non_mix_res = { - let mut state = in_state.clone(); - // If `no_mixing` is true: add `A4 * round_constant_b9` - // Otherwise, do nothing and return the orignal lane value in the - // next cell - state[0] = self.generic.conditional_add_const( - layouter, - &state[0], - &negated_flag, - &self.iota_constants.a4_times_round_constants_b9[PERMUTATION - 1], - )?; - state - }; - - // If we mix: - // Absorb - let (out_state_absorb_cells, _) = self.absorb_config.copy_state_flag_next_inputs( - layouter, - in_state, - // Compute out_absorb state. - state_bigint_to_field(KeccakFArith::absorb( - &state_to_biguint(split_state_cells(in_state.clone())), - &state_to_state_bigint::(next_mixing.unwrap_or_default()), - )), - next_mixing.unwrap_or_default(), - flag.clone(), - )?; - - let base_conv_cells = { - let mut state = vec![]; - for lane in out_state_absorb_cells.iter() { - let (input_coefs, output_b13, _) = self.table.assign_region(layouter, &lane)?; - self.generic - .running_sum(layouter, input_coefs, Some(lane.clone()))?; - let out_lane = self.generic.running_sum(layouter, output_b13, None)?; - state.push(out_lane); - } - let state: [AssignedCell; 25] = state.try_into().unwrap(); - state - }; - - // IotaB13 - let mix_res = { - let mut base_conv_cells = base_conv_cells; - - // If `mixing` is true: add round constant in base 13. - // Otherwise, do nothing and return the orignal lane value in the - // next cell - base_conv_cells[0] = self.generic.conditional_add_const( - layouter, - &base_conv_cells[0], - &flag, - &self.iota_constants.round_constant_b13, - )?; - base_conv_cells - }; - - self.assign_out_mixing_states( - layouter, - flag_bool, - negated_flag, - &mix_res, - &non_mix_res, - out_state, - ) - } - - /// Copies the `[(Cell,F);25]` to the passed [Column; 25]. - fn copy_state( - &self, - region: &mut Region<'_, F>, - offset: usize, - columns: [Column; 25], - state: &[AssignedCell; 25], - ) -> Result<(), Error> { - for (idx, state_cell) in state.iter().enumerate() { - state_cell.copy_advice( - || format!("Copy state {}", idx), - region, - columns[idx], - offset, - )?; - } - - Ok(()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::common::{State, ROUND_CONSTANTS}; - use halo2_proofs::{ - circuit::{Layouter, SimpleFloorPlanner}, - dev::MockProver, - pairing::bn256::Fr as Fp, - plonk::{Circuit, ConstraintSystem, Error, TableColumn}, - }; - use itertools::Itertools; - use pretty_assertions::assert_eq; - - #[test] - fn test_mixing_gate() { - #[derive(Default)] - struct MyCircuit { - in_state: [F; 25], - out_state: [F; 25], - next_mixing: Option<[F; NEXT_INPUTS_LANES]>, - // flag - is_mixing: bool, - } - - #[derive(Clone)] - struct MyConfig { - mixing_conf: MixingConfig, - stackable: StackableTable, - } - - impl Circuit for MyCircuit { - type Config = MyConfig; - type FloorPlanner = SimpleFloorPlanner; - - fn without_witnesses(&self) -> Self { - Self::default() - } - - fn configure(meta: &mut ConstraintSystem) -> Self::Config { - let state: [Column; 25] = (0..25) - .map(|_| { - let col = meta.advice_column(); - meta.enable_equality(col); - col - }) - .collect_vec() - .try_into() - .unwrap(); - let fixed = meta.fixed_column(); - let generic = - GenericConfig::configure(meta, state[0..3].try_into().unwrap(), fixed); - let stackable_cols: [TableColumn; 3] = (0..3) - .map(|_| meta.lookup_table_column()) - .collect_vec() - .try_into() - .unwrap(); - let fromb9_cols: [TableColumn; 3] = (0..3) - .map(|_| meta.lookup_table_column()) - .collect_vec() - .try_into() - .unwrap(); - let stackable = StackableTable::configure( - meta, - state[0..3].try_into().unwrap(), - stackable_cols, - ); - let table = FromBase9TableConfig::configure( - meta, - state[0..3].try_into().unwrap(), - fromb9_cols, - ); - let mixing_conf = - MixingConfig::configure(meta, table, state, generic, stackable.clone()); - - MyConfig { - mixing_conf, - stackable, - } - } - - fn synthesize( - &self, - mut config: Self::Config, - mut layouter: impl Layouter, - ) -> Result<(), Error> { - // Load the table - config.mixing_conf.table.load(&mut layouter)?; - config.stackable.load(&mut layouter)?; - let offset: usize = 0; - - let in_state = layouter.assign_region( - || "Mixing Wittnes assignment", - |mut region| { - // Witness `in_state` - let in_state: [AssignedCell; 25] = { - let mut state: Vec> = Vec::with_capacity(25); - for (idx, val) in self.in_state.iter().enumerate() { - let cell = region.assign_advice( - || "witness input state", - config.mixing_conf.state[idx], - offset, - || Ok(*val), - )?; - state.push(cell) - } - state.try_into().unwrap() - }; - - Ok(in_state) - }, - )?; - - config.mixing_conf.assign_state( - &mut layouter, - &in_state, - self.out_state, - self.is_mixing, - self.next_mixing, - )?; - - Ok(()) - } - } - - let input1: State = [ - [1, 0, 0, 0, 0], - [0, 0, 0, 0, 0], - [0, 0, 0, 0, 0], - [0, 0, 0, 0, 0], - [0, 0, 0, 0, 0], - ]; - - let input2: State = [ - [2, 0, 0, 0, 0], - [0, 0, 0, 0, 0], - [0, 0, 0, 0, 0], - [0, 0, 0, 0, 0], - [0, 0, 0, 0, 0], - ]; - - // Convert the input to base9 as the gadget already expects it like this - // since it's always the output of IotaB9. - let mut in_state = StateBigInt::from(input1); - for (x, y) in (0..5).cartesian_product(0..5) { - in_state[(x, y)] = convert_b2_to_b9(input1[x][y]) - } - - // Convert the next_input_b9 to base9 as it needs to be added to the - // state in base9 too. - let next_input = StateBigInt::from(input2); - - // Compute out mixing state (when flag = 1) - let out_mixing_state = state_bigint_to_field(KeccakFArith::mixing( - &in_state, - Some(&input2), - *ROUND_CONSTANTS.last().unwrap(), - )); - - // Compute out non-mixing state (when flag = 0) - let out_non_mixing_state = state_bigint_to_field(KeccakFArith::mixing( - &in_state, - None, - *ROUND_CONSTANTS.last().unwrap(), - )); - - // Add inputs in the correct format. - let in_state = state_bigint_to_field(StateBigInt::from(input1)); - let next_mixing = Some(state_bigint_to_field(next_input)); - - // With flag set to false, we don't mix. And so we should obtain Absorb - // + base_conv + IotaB13 result - { - // With the correct input and output witnesses, the proof should - // pass. - let circuit = MyCircuit:: { - in_state, - out_state: out_mixing_state, - next_mixing, - is_mixing: true, - }; - - let prover = MockProver::::run(17, &circuit, vec![]).unwrap(); - - assert_eq!(prover.verify(), Ok(())); - - // With wrong input and/or output witnesses, the proof should fail - // to be verified. - let circuit = MyCircuit:: { - in_state, - out_state: out_non_mixing_state, - next_mixing, - is_mixing: true, - }; - - let prover = MockProver::::run(17, &circuit, vec![]).unwrap(); - - assert!(prover.verify().is_err()); - } - - // With flag set to `false`, we don't mix. And so we should obtain - // IotaB9 application as result. - { - let circuit = MyCircuit:: { - in_state, - out_state: out_non_mixing_state, - next_mixing: None, - is_mixing: false, - }; - - let prover = MockProver::::run(17, &circuit, vec![]).unwrap(); - - assert_eq!(prover.verify(), Ok(())); - - // With wrong input and/or output witnesses, the proof should fail - // to be verified. - let circuit = MyCircuit:: { - in_state, - out_state: in_state, - next_mixing, - is_mixing: false, - }; - - let prover = MockProver::::run(17, &circuit, vec![]).unwrap(); - - assert!(prover.verify().is_err()); - } - } -} From 251d6c139e56e53046d60002e1fd1148e0934d3e Mon Sep 17 00:00:00 2001 From: ChihChengLiang Date: Thu, 30 Jun 2022 22:47:14 +0100 Subject: [PATCH 08/12] minor --- keccak256/src/permutation/circuit.rs | 23 ++++++++++++----------- keccak256/src/permutation/theta.rs | 12 +++++------- keccak256/src/permutation/xi.rs | 5 ++--- 3 files changed, 19 insertions(+), 21 deletions(-) diff --git a/keccak256/src/permutation/circuit.rs b/keccak256/src/permutation/circuit.rs index 91e60eb3ab..056f812ecf 100644 --- a/keccak256/src/permutation/circuit.rs +++ b/keccak256/src/permutation/circuit.rs @@ -35,7 +35,6 @@ fn assign_next_input( let next_input_b9 = layouter.assign_region( || "next input words", |mut region| { - let mut next_input_b9: Vec> = vec![]; let next_input = next_input.map_or( [None; NEXT_INPUTS_LANES], |v| -> [Option; NEXT_INPUTS_LANES] { @@ -47,16 +46,18 @@ fn assign_next_input( .unwrap() }, ); - for (offset, input) in next_input.iter().enumerate() { - let cell = region.assign_advice( - || "next input words", - *next_input_col, - offset, - || Ok(input.unwrap_or_default()), - )?; - next_input_b9.push(cell); - } - Ok(next_input_b9) + next_input + .iter() + .enumerate() + .map(|(offset, input)| { + region.assign_advice( + || "next input words", + *next_input_col, + offset, + || Ok(input.unwrap_or_default()), + ) + }) + .collect::, Error>>() }, )?; Ok(next_input_b9.try_into().unwrap()) diff --git a/keccak256/src/permutation/theta.rs b/keccak256/src/permutation/theta.rs index d443b70002..3565929dff 100644 --- a/keccak256/src/permutation/theta.rs +++ b/keccak256/src/permutation/theta.rs @@ -14,14 +14,13 @@ pub fn assign_theta( ) -> Result<[AssignedCell; 25], Error> { let theta_col_sums = (0..5) .map(|x| { - let col_sum = generic.running_sum( + generic.running_sum( layouter, (0..5).map(|y| state[5 * x + y].clone()).collect(), None, - )?; - Ok(col_sum) + ) }) - .collect::>, Error>>()?; + .collect::, Error>>()?; let out_state = (0..5) .cartesian_product(0..5) @@ -32,10 +31,9 @@ pub fn assign_theta( theta_col_sums[(x + 1) % 5].clone(), ]; let vs = vec![F::one(), F::one(), F::from(B13 as u64)]; - let new_lane = generic.linear_combine_consts(layouter, cells, vs, None)?; - Ok(new_lane) + generic.linear_combine_consts(layouter, cells, vs, None) }) - .collect::>, Error>>()?; + .collect::, Error>>()?; Ok(out_state.try_into().unwrap()) } diff --git a/keccak256/src/permutation/xi.rs b/keccak256/src/permutation/xi.rs index 6202c2a6e9..989a62fb2f 100644 --- a/keccak256/src/permutation/xi.rs +++ b/keccak256/src/permutation/xi.rs @@ -21,10 +21,9 @@ pub fn assign_xi( state[5 * ((x + 2) % 5) + y].clone(), ]; let vs = vec![F::from(A1), F::from(A2), F::from(A3)]; - let new_lane = generic.linear_combine_consts(layouter, cells, vs, None)?; - Ok(new_lane) + generic.linear_combine_consts(layouter, cells, vs, None) }) - .collect::>, Error>>()?; + .collect::, Error>>()?; Ok(out_state.try_into().unwrap()) } From d2aa68f4f2b46fc5e4cddf8940d418230f9f8b22 Mon Sep 17 00:00:00 2001 From: ChihChengLiang Date: Thu, 30 Jun 2022 23:16:15 +0100 Subject: [PATCH 09/12] migrate all functions to components --- keccak256/src/permutation.rs | 6 +- keccak256/src/permutation/circuit.rs | 165 +----- keccak256/src/permutation/components.rs | 710 ++++++++++++++++++++++++ keccak256/src/permutation/iota.rs | 33 -- keccak256/src/permutation/pi.rs | 18 - keccak256/src/permutation/rho.rs | 225 -------- keccak256/src/permutation/theta.rs | 188 ------- keccak256/src/permutation/xi.rs | 163 ------ 8 files changed, 732 insertions(+), 776 deletions(-) create mode 100644 keccak256/src/permutation/components.rs delete mode 100644 keccak256/src/permutation/iota.rs delete mode 100644 keccak256/src/permutation/pi.rs delete mode 100644 keccak256/src/permutation/rho.rs delete mode 100644 keccak256/src/permutation/theta.rs delete mode 100644 keccak256/src/permutation/xi.rs diff --git a/keccak256/src/permutation.rs b/keccak256/src/permutation.rs index 749e655432..71ccfc205f 100644 --- a/keccak256/src/permutation.rs +++ b/keccak256/src/permutation.rs @@ -1,11 +1,7 @@ #![allow(clippy::type_complexity)] #![allow(clippy::too_many_arguments)] pub mod circuit; +pub(crate) mod components; pub(crate) mod generic; -pub(crate) mod iota; -pub(crate) mod pi; -pub(crate) mod rho; pub(crate) mod rho_helpers; pub(crate) mod tables; -pub(crate) mod theta; -pub(crate) mod xi; diff --git a/keccak256/src/permutation/circuit.rs b/keccak256/src/permutation/circuit.rs index 056f812ecf..2ea717a180 100644 --- a/keccak256/src/permutation/circuit.rs +++ b/keccak256/src/permutation/circuit.rs @@ -1,12 +1,7 @@ use crate::{ - arith_helpers::*, common::{NEXT_INPUTS_LANES, PERMUTATION}, - gate_helpers::biguint_to_f, permutation::{ generic::GenericConfig, - iota::IotaConstants, - pi::pi_gate_permutation, - rho::assign_rho, tables::{Base13toBase9TableConfig, FromBase9TableConfig, StackableTable}, }, }; @@ -16,106 +11,15 @@ use halo2_proofs::{ plonk::{Advice, Column, ConstraintSystem, Error, TableColumn}, }; use itertools::Itertools; -use num_bigint::BigUint; use super::{ - tables::{ - FromBinaryTableConfig, NUM_OF_B9_CHUNKS_PER_SLICE, NUM_OF_B9_SLICES, - NUM_OF_BINARY_CHUNKS_PER_SLICE, NUM_OF_BINARY_SLICES, + components::{ + assign_next_input, assign_rho, assign_theta, assign_xi, convert_from_b9_to_b13, + convert_to_b9_mul_a4, pi_gate_permutation, IotaConstants, }, - theta::assign_theta, - xi::assign_xi, + tables::FromBinaryTableConfig, }; -fn assign_next_input( - layouter: &mut impl Layouter, - next_input_col: &Column, - next_input: &Option<[F; NEXT_INPUTS_LANES]>, -) -> Result<[AssignedCell; NEXT_INPUTS_LANES], Error> { - let next_input_b9 = layouter.assign_region( - || "next input words", - |mut region| { - let next_input = next_input.map_or( - [None; NEXT_INPUTS_LANES], - |v| -> [Option; NEXT_INPUTS_LANES] { - v.map(|vv| Some(vv)) - .iter() - .cloned() - .collect_vec() - .try_into() - .unwrap() - }, - ); - next_input - .iter() - .enumerate() - .map(|(offset, input)| { - region.assign_advice( - || "next input words", - *next_input_col, - offset, - || Ok(input.unwrap_or_default()), - ) - }) - .collect::, Error>>() - }, - )?; - Ok(next_input_b9.try_into().unwrap()) -} - -fn convert_from_b9_to_b13( - layouter: &mut impl Layouter, - from_b9_table: &FromBase9TableConfig, - generic: &GenericConfig, - state: [AssignedCell; 25], - output_b2: bool, -) -> Result<([AssignedCell; 25], Option<[AssignedCell; 25]>), Error> { - let (state_b13, state_b2): (Vec>, Vec>>) = state - .iter() - .map(|lane| { - let (base9s, base_13s, base_2s) = from_b9_table.assign_region(layouter, lane)?; - let vs = (0..NUM_OF_B9_SLICES) - .map(|i| { - biguint_to_f(&BigUint::from(B9).pow((NUM_OF_B9_CHUNKS_PER_SLICE * i) as u32)) - }) - .rev() - .collect_vec(); - generic.linear_combine_consts(layouter, base9s, vs, Some(lane.clone()))?; - let vs = (0..NUM_OF_B9_SLICES) - .map(|i| { - biguint_to_f(&BigUint::from(B13).pow((NUM_OF_B9_CHUNKS_PER_SLICE * i) as u32)) - }) - .rev() - .collect_vec(); - let lane_b13 = generic.linear_combine_consts(layouter, base_13s, vs, None)?; - let lane_b2 = if output_b2 { - let vs = (0..NUM_OF_B9_SLICES) - .map(|i| { - biguint_to_f( - &BigUint::from(B2).pow((NUM_OF_B9_CHUNKS_PER_SLICE * i) as u32), - ) - }) - .rev() - .collect_vec(); - let lane_b2 = generic.linear_combine_consts(layouter, base_2s, vs, None)?; - Some(lane_b2) - } else { - None - }; - Ok((lane_b13, lane_b2)) - }) - .collect::, Error>>()? - .iter() - .cloned() - .unzip(); - let state_b2: Option<[AssignedCell; 25]> = state_b2 - .into_iter() - .collect::>>() - .map(|v| v.try_into().unwrap()); - - Ok((state_b13.try_into().unwrap(), state_b2)) -} - #[derive(Clone, Debug)] pub struct KeccakFConfig { generic: GenericConfig, @@ -237,43 +141,8 @@ impl KeccakFConfig { let next_input = assign_next_input(layouter, &self.advice, &next_mixing)?; // Convert to base 9 and multiply by A4 - let next_input = { - let next_input = next_input - .iter() - .map(|input| { - let (base2s, base9s, _) = self.from_b2_table.assign_region(layouter, input)?; - let vs = (0..NUM_OF_BINARY_SLICES) - .map(|i| { - biguint_to_f( - &BigUint::from(B2).pow((NUM_OF_BINARY_CHUNKS_PER_SLICE * i) as u32), - ) - }) - .rev() - .collect_vec(); - self.generic.linear_combine_consts( - layouter, - base2s, - vs, - Some(input.clone()), - )?; - let vs = (0..NUM_OF_BINARY_SLICES) - .map(|i| { - biguint_to_f::( - &BigUint::from(B9).pow((NUM_OF_BINARY_CHUNKS_PER_SLICE * i) as u32), - ) * F::from(A4) - }) - .rev() - .collect_vec(); - let output = - self.generic - .linear_combine_consts(layouter, base9s.clone(), vs, None)?; - Ok(output) - }) - .collect::>, Error>>()?; - let next_input: [AssignedCell; NEXT_INPUTS_LANES] = - next_input.try_into().unwrap(); - next_input - }; + let next_input = + convert_to_b9_mul_a4(layouter, &self.from_b2_table, &self.generic, &next_input)?; for (i, input) in next_input.iter().enumerate() { state[i] = self @@ -296,13 +165,21 @@ impl KeccakFConfig { #[cfg(test)] mod tests { use super::*; - use crate::common::{State, NEXT_INPUTS_LANES}; - use crate::gate_helpers::biguint_to_f; - use crate::keccak_arith::KeccakFArith; - use halo2_proofs::circuit::Layouter; - use halo2_proofs::pairing::bn256::Fr as Fp; - use halo2_proofs::plonk::{ConstraintSystem, Error}; - use halo2_proofs::{circuit::SimpleFloorPlanner, dev::MockProver, plonk::Circuit}; + use crate::{ + arith_helpers::{ + convert_b2_to_b13, convert_b9_lane_to_b2_biguint, state_bigint_to_field, StateBigInt, + }, + common::{State, NEXT_INPUTS_LANES}, + gate_helpers::biguint_to_f, + keccak_arith::KeccakFArith, + }; + + use halo2_proofs::{ + circuit::{Layouter, SimpleFloorPlanner}, + dev::MockProver, + pairing::bn256::Fr as Fp, + plonk::{Circuit, ConstraintSystem, Error}, + }; #[test] fn test_keccak_round() { diff --git a/keccak256/src/permutation/components.rs b/keccak256/src/permutation/components.rs new file mode 100644 index 0000000000..d2b9b77040 --- /dev/null +++ b/keccak256/src/permutation/components.rs @@ -0,0 +1,710 @@ +use eth_types::Field; +use halo2_proofs::{ + circuit::{AssignedCell, Layouter}, + plonk::{Advice, Column, Error}, +}; +use itertools::Itertools; +use std::convert::TryInto; +use std::vec; + +use super::tables::{FromBinaryTableConfig, NUM_OF_BINARY_CHUNKS_PER_SLICE, NUM_OF_BINARY_SLICES}; + +use crate::{ + arith_helpers::{convert_b2_to_b13, convert_b2_to_b9, A1, A2, A3, A4, B13, B2, B9}, + common::{NEXT_INPUTS_LANES, PERMUTATION, ROTATION_CONSTANTS, ROUND_CONSTANTS}, + gate_helpers::{biguint_to_f, f_to_biguint}, + permutation::{ + generic::GenericConfig, + rho_helpers::{slice_lane, RhoLane}, + tables::{ + Base13toBase9TableConfig, FromBase9TableConfig, StackableTable, + NUM_OF_B9_CHUNKS_PER_SLICE, NUM_OF_B9_SLICES, + }, + }, +}; + +use num_bigint::BigUint; + +pub fn assign_theta( + generic: &GenericConfig, + layouter: &mut impl Layouter, + state: &[AssignedCell; 25], +) -> Result<[AssignedCell; 25], Error> { + let theta_col_sums = (0..5) + .map(|x| { + generic.running_sum( + layouter, + (0..5).map(|y| state[5 * x + y].clone()).collect(), + None, + ) + }) + .collect::, Error>>()?; + + let out_state = (0..5) + .cartesian_product(0..5) + .map(|(x, y)| { + let cells = vec![ + state[5 * x + y].clone(), + theta_col_sums[(x + 4) % 5].clone(), + theta_col_sums[(x + 1) % 5].clone(), + ]; + let vs = vec![F::one(), F::one(), F::from(B13 as u64)]; + generic.linear_combine_consts(layouter, cells, vs, None) + }) + .collect::, Error>>()?; + + Ok(out_state.try_into().unwrap()) +} + +pub fn assign_rho( + layouter: &mut impl Layouter, + base13to9_config: &Base13toBase9TableConfig, + generic: &GenericConfig, + stackable: &StackableTable, + state: &[AssignedCell; 25], +) -> Result<[AssignedCell; 25], Error> { + let mut next_state = vec![]; + let mut step2_od_join = vec![]; + let mut step3_od_join = vec![]; + for (lane_idx, lane) in state.iter().enumerate() { + let rotation = { + let x = lane_idx / 5; + let y = lane_idx % 5; + ROTATION_CONSTANTS[x][y] + }; + let (conversions, special) = + RhoLane::new(f_to_biguint(*lane.value().unwrap_or(&F::zero())), rotation) + .get_full_witness(); + let slices = slice_lane(rotation); + + let (input_coefs, mut output_coefs, step2_od, step3_od) = + base13to9_config.assign_region(layouter, &slices, &conversions)?; + + let input_pobs = conversions + .iter() + .map(|c| biguint_to_f::(&c.input.power_of_base)) + .collect_vec(); + + let mut output_pobs = conversions + .iter() + .map(|c| biguint_to_f::(&c.output.power_of_base)) + .collect_vec(); + // Final output power of base + output_pobs.push(biguint_to_f::(&special.output_pob)); + + let input_from_chunks = + generic.linear_combine_consts(layouter, input_coefs, input_pobs, None)?; + let last_chunk = generic.sub_advice(layouter, &lane, &input_from_chunks)?; + + let final_output_coef = stackable.lookup_special_chunks(layouter, &last_chunk)?; + output_coefs.push(final_output_coef); + + let output_lane = + generic.linear_combine_consts(layouter, output_coefs, output_pobs, None)?; + next_state.push(output_lane); + step2_od_join.extend(step2_od); + step3_od_join.extend(step3_od); + } + let step2_sum = generic.running_sum(layouter, step2_od_join, None)?; + let step3_sum = generic.running_sum(layouter, step3_od_join, None)?; + stackable.lookup_range_12(layouter, &[step2_sum])?; + stackable.lookup_range_169(layouter, &[step3_sum])?; + Ok(next_state.try_into().unwrap()) +} + +/// The Keccak Pi step +/// +/// It has no gates. We just have to permute the previous state into the correct +/// order. The copy constrain in the next gate can then enforce the Pi step +/// permutation. +pub fn pi_gate_permutation(state: &[AssignedCell; 25]) -> [AssignedCell; 25] { + (0..5) + .cartesian_product(0..5) + .map(|(x, y)| state[5 * ((x + 3 * y) % 5) + x].clone()) + .collect::>() + .try_into() + .unwrap() +} + +pub fn assign_xi( + generic: &GenericConfig, + layouter: &mut impl Layouter, + state: &[AssignedCell; 25], +) -> Result<[AssignedCell; 25], Error> { + let out_state = (0..5) + .cartesian_product(0..5) + .map(|(x, y)| { + let cells = vec![ + state[5 * x + y].clone(), + state[5 * ((x + 1) % 5) + y].clone(), + state[5 * ((x + 2) % 5) + y].clone(), + ]; + let vs = vec![F::from(A1), F::from(A2), F::from(A3)]; + generic.linear_combine_consts(layouter, cells, vs, None) + }) + .collect::, Error>>()?; + Ok(out_state.try_into().unwrap()) +} + +#[derive(Clone, Debug)] +pub struct IotaConstants { + pub round_constant_b13: F, + pub a4_times_round_constants_b9: [F; PERMUTATION], +} + +impl Default for IotaConstants { + fn default() -> Self { + let round_constant_b13 = + biguint_to_f::(&convert_b2_to_b13(ROUND_CONSTANTS[PERMUTATION - 1])); + + let a4_times_round_constants_b9: [F; 24] = ROUND_CONSTANTS + .iter() + .map(|&x| { + let constant = A4 * convert_b2_to_b9(x); + biguint_to_f::(&constant) + }) + .collect_vec() + .try_into() + .unwrap(); + + Self { + round_constant_b13, + a4_times_round_constants_b9, + } + } +} + +pub fn assign_next_input( + layouter: &mut impl Layouter, + next_input_col: &Column, + next_input: &Option<[F; NEXT_INPUTS_LANES]>, +) -> Result<[AssignedCell; NEXT_INPUTS_LANES], Error> { + let next_input_b9 = layouter.assign_region( + || "next input words", + |mut region| { + let next_input = next_input.map_or( + [None; NEXT_INPUTS_LANES], + |v| -> [Option; NEXT_INPUTS_LANES] { + v.map(|vv| Some(vv)) + .iter() + .cloned() + .collect_vec() + .try_into() + .unwrap() + }, + ); + next_input + .iter() + .enumerate() + .map(|(offset, input)| { + region.assign_advice( + || "next input words", + *next_input_col, + offset, + || Ok(input.unwrap_or_default()), + ) + }) + .collect::, Error>>() + }, + )?; + Ok(next_input_b9.try_into().unwrap()) +} + +pub fn convert_to_b9_mul_a4( + layouter: &mut impl Layouter, + from_b2_table: &FromBinaryTableConfig, + generic: &GenericConfig, + next_input: &[AssignedCell; NEXT_INPUTS_LANES], +) -> Result<[AssignedCell; NEXT_INPUTS_LANES], Error> { + let next_input = next_input + .iter() + .map(|input| { + let (base2s, base9s, _) = from_b2_table.assign_region(layouter, input)?; + let vs = (0..NUM_OF_BINARY_SLICES) + .map(|i| { + biguint_to_f( + &BigUint::from(B2).pow((NUM_OF_BINARY_CHUNKS_PER_SLICE * i) as u32), + ) + }) + .rev() + .collect_vec(); + generic.linear_combine_consts(layouter, base2s, vs, Some(input.clone()))?; + let vs = (0..NUM_OF_BINARY_SLICES) + .map(|i| { + biguint_to_f::( + &BigUint::from(B9).pow((NUM_OF_BINARY_CHUNKS_PER_SLICE * i) as u32), + ) * F::from(A4) + }) + .rev() + .collect_vec(); + let output = generic.linear_combine_consts(layouter, base9s.clone(), vs, None)?; + Ok(output) + }) + .collect::>, Error>>()?; + let next_input: [AssignedCell; NEXT_INPUTS_LANES] = next_input.try_into().unwrap(); + Ok(next_input) +} + +pub fn convert_from_b9_to_b13( + layouter: &mut impl Layouter, + from_b9_table: &FromBase9TableConfig, + generic: &GenericConfig, + state: [AssignedCell; 25], + output_b2: bool, +) -> Result<([AssignedCell; 25], Option<[AssignedCell; 25]>), Error> { + let (state_b13, state_b2): (Vec>, Vec>>) = state + .iter() + .map(|lane| { + let (base9s, base_13s, base_2s) = from_b9_table.assign_region(layouter, lane)?; + let vs = (0..NUM_OF_B9_SLICES) + .map(|i| { + biguint_to_f(&BigUint::from(B9).pow((NUM_OF_B9_CHUNKS_PER_SLICE * i) as u32)) + }) + .rev() + .collect_vec(); + generic.linear_combine_consts(layouter, base9s, vs, Some(lane.clone()))?; + let vs = (0..NUM_OF_B9_SLICES) + .map(|i| { + biguint_to_f(&BigUint::from(B13).pow((NUM_OF_B9_CHUNKS_PER_SLICE * i) as u32)) + }) + .rev() + .collect_vec(); + let lane_b13 = generic.linear_combine_consts(layouter, base_13s, vs, None)?; + let lane_b2 = if output_b2 { + let vs = (0..NUM_OF_B9_SLICES) + .map(|i| { + biguint_to_f( + &BigUint::from(B2).pow((NUM_OF_B9_CHUNKS_PER_SLICE * i) as u32), + ) + }) + .rev() + .collect_vec(); + let lane_b2 = generic.linear_combine_consts(layouter, base_2s, vs, None)?; + Some(lane_b2) + } else { + None + }; + Ok((lane_b13, lane_b2)) + }) + .collect::, Error>>()? + .iter() + .cloned() + .unzip(); + let state_b2: Option<[AssignedCell; 25]> = state_b2 + .into_iter() + .collect::>>() + .map(|v| v.try_into().unwrap()); + + Ok((state_b13.try_into().unwrap(), state_b2)) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::arith_helpers::StateBigInt; + use crate::common::*; + use crate::gate_helpers::biguint_to_f; + use crate::keccak_arith::*; + use halo2_proofs::{ + circuit::{Layouter, SimpleFloorPlanner}, + dev::MockProver, + pairing::bn256::Fr as Fp, + plonk::{Advice, Circuit, Column, ConstraintSystem, Error, TableColumn}, + }; + use itertools::Itertools; + use std::convert::TryInto; + use std::marker::PhantomData; + + #[test] + fn test_theta_gates() { + #[derive(Clone, Debug)] + struct MyConfig { + lane: Column, + generic: GenericConfig, + } + + impl MyConfig { + pub fn configure(meta: &mut ConstraintSystem) -> Self { + let advices: [Column; 3] = (0..3) + .map(|_| { + let column = meta.advice_column(); + meta.enable_equality(column); + column + }) + .collect::>() + .try_into() + .unwrap(); + let fixed = meta.fixed_column(); + + let lane = advices[0]; + let generic = GenericConfig::configure(meta, advices, fixed); + Self { lane, generic } + } + } + #[derive(Default)] + struct MyCircuit { + in_state: [F; 25], + out_state: [F; 25], + _marker: PhantomData, + } + impl Circuit for MyCircuit { + type Config = MyConfig; + type FloorPlanner = SimpleFloorPlanner; + + fn without_witnesses(&self) -> Self { + Self::default() + } + + fn configure(meta: &mut ConstraintSystem) -> Self::Config { + // this column is required by `constrain_constant` + let constant = meta.fixed_column(); + meta.enable_constant(constant); + Self::Config::configure(meta) + } + + fn synthesize( + &self, + config: Self::Config, + mut layouter: impl Layouter, + ) -> Result<(), Error> { + let in_state = layouter.assign_region( + || "Wittnes & assignation", + |mut region| { + // Witness `state` + let in_state: [AssignedCell; 25] = { + let mut state: Vec> = Vec::with_capacity(25); + for (offset, val) in self.in_state.iter().enumerate() { + let cell = region.assign_advice( + || "witness input state", + config.lane, + offset, + || Ok(*val), + )?; + state.push(cell) + } + state.try_into().unwrap() + }; + Ok(in_state) + }, + )?; + + let out_state = assign_theta(&config.generic, &mut layouter, &in_state)?; + + layouter.assign_region( + || "Check outstate", + |mut region| { + for (assigned, value) in out_state.iter().zip(self.out_state.iter()) { + region.constrain_constant(assigned.cell(), value)?; + } + Ok(()) + }, + )?; + Ok(()) + } + } + + let input1: State = [ + [1, 0, 0, 0, 0], + [0, 0, 0, 9223372036854775808, 0], + [0, 0, 0, 0, 0], + [0, 0, 0, 0, 0], + [0, 0, 0, 0, 0], + ]; + let mut in_biguint = StateBigInt::default(); + let mut in_state: [Fp; 25] = [Fp::zero(); 25]; + + for (x, y) in (0..5).cartesian_product(0..5) { + in_biguint[(x, y)] = convert_b2_to_b13(input1[x][y]); + in_state[5 * x + y] = biguint_to_f(&in_biguint[(x, y)]); + } + let s1_arith = KeccakFArith::theta(&in_biguint); + let mut out_state: [Fp; 25] = [Fp::zero(); 25]; + for (x, y) in (0..5).cartesian_product(0..5) { + out_state[5 * x + y] = biguint_to_f(&s1_arith[(x, y)]); + } + + let circuit = MyCircuit:: { + in_state, + out_state, + _marker: PhantomData, + }; + + // Test without public inputs + let prover = MockProver::::run(9, &circuit, vec![]).unwrap(); + + assert_eq!(prover.verify(), Ok(())); + + let mut out_state2 = out_state; + out_state2[0] = Fp::from(5566u64); + + let circuit2 = MyCircuit:: { + in_state, + out_state: out_state2, + _marker: PhantomData, + }; + + let prover = MockProver::::run(9, &circuit2, vec![]).unwrap(); + assert!(prover.verify().is_err()); + } + + #[test] + fn test_rho_gate() { + #[derive(Default)] + struct MyCircuit { + in_state: [F; 25], + out_state: [F; 25], + } + + #[derive(Clone)] + struct MyConfig { + advice: Column, + generic: GenericConfig, + stackable: StackableTable, + base13to9_config: Base13toBase9TableConfig, + } + impl Circuit for MyCircuit { + type Config = MyConfig; + type FloorPlanner = SimpleFloorPlanner; + + fn without_witnesses(&self) -> Self { + Self::default() + } + + fn configure(meta: &mut ConstraintSystem) -> Self::Config { + let advices: [Column; 3] = (0..3) + .map(|_| meta.advice_column()) + .collect::>() + .try_into() + .unwrap(); + + let fixed = meta.fixed_column(); + let stackable_cols: [TableColumn; 3] = (0..3) + .map(|_| meta.lookup_table_column()) + .collect_vec() + .try_into() + .unwrap(); + let base13to9_cols: [TableColumn; 3] = (0..3) + .map(|_| meta.lookup_table_column()) + .collect_vec() + .try_into() + .unwrap(); + let stackable = StackableTable::configure(meta, advices, stackable_cols); + let generic = GenericConfig::configure(meta, advices, fixed); + let base13to9_config = + Base13toBase9TableConfig::configure(meta, advices, base13to9_cols); + + Self::Config { + advice: advices[0], + generic, + stackable, + base13to9_config, + } + } + + fn synthesize( + &self, + mut config: Self::Config, + mut layouter: impl Layouter, + ) -> Result<(), Error> { + config.base13to9_config.load(&mut layouter)?; + config.stackable.load(&mut layouter)?; + let state = layouter.assign_region( + || "assign input state", + |mut region| { + let state = self + .in_state + .iter() + .enumerate() + .map(|(offset, &value)| { + region.assign_advice(|| "lane", config.advice, offset, || Ok(value)) + }) + .collect::>, Error>>()?; + + Ok(state.try_into().unwrap()) + }, + )?; + let out_state = assign_rho( + &mut layouter, + &config.base13to9_config, + &config.generic, + &config.stackable, + &state, + )?; + layouter.assign_region( + || "check final states", + |mut region| { + for (assigned, value) in out_state.iter().zip(self.out_state.iter()) { + region.constrain_constant(assigned.cell(), value)?; + } + Ok(()) + }, + )?; + + Ok(()) + } + } + + let input1: State = [ + [102, 111, 111, 98, 97], + [114, 0, 5, 0, 0], + [0, 0, 0, 0, 0], + [0, 0, 0, 5, 0], + [0, 0, 0, 0, 0], + ]; + let mut in_biguint = StateBigInt::default(); + let mut in_state: [Fp; 25] = [Fp::zero(); 25]; + + for (x, y) in (0..5).cartesian_product(0..5) { + in_biguint[(x, y)] = convert_b2_to_b13(input1[x][y]); + } + let s0_arith = KeccakFArith::theta(&in_biguint); + for (x, y) in (0..5).cartesian_product(0..5) { + in_state[5 * x + y] = biguint_to_f(&s0_arith[(x, y)]); + } + let s1_arith = KeccakFArith::rho(&s0_arith); + let mut out_state: [Fp; 25] = [Fp::zero(); 25]; + for (x, y) in (0..5).cartesian_product(0..5) { + out_state[5 * x + y] = biguint_to_f(&s1_arith[(x, y)]); + } + let circuit = MyCircuit:: { + in_state, + out_state, + }; + let k = 15; + #[cfg(feature = "dev-graph")] + { + use plotters::prelude::*; + let root = + BitMapBackend::new("rho-test-circuit.png", (1024, 16384)).into_drawing_area(); + root.fill(&WHITE).unwrap(); + let root = root.titled("Rho", ("sans-serif", 60)).unwrap(); + halo2_proofs::dev::CircuitLayout::default() + .render(k, &circuit, &root) + .unwrap(); + } + // Test without public inputs + let prover = MockProver::::run(k, &circuit, vec![]).unwrap(); + + assert_eq!(prover.verify(), Ok(())); + } + + #[test] + fn test_xi_gate() { + #[derive(Clone, Debug)] + struct MyConfig { + lane: Column, + generic: GenericConfig, + } + + impl MyConfig { + pub fn configure(meta: &mut ConstraintSystem) -> Self { + let advices: [Column; 3] = (0..3) + .map(|_| { + let column = meta.advice_column(); + meta.enable_equality(column); + column + }) + .collect::>() + .try_into() + .unwrap(); + let fixed = meta.fixed_column(); + + let lane = advices[0]; + let generic = GenericConfig::configure(meta, advices, fixed); + Self { lane, generic } + } + } + #[derive(Default)] + struct MyCircuit { + in_state: [F; 25], + out_state: [F; 25], + _marker: PhantomData, + } + + impl Circuit for MyCircuit { + type Config = MyConfig; + type FloorPlanner = SimpleFloorPlanner; + + fn without_witnesses(&self) -> Self { + Self::default() + } + + fn configure(meta: &mut ConstraintSystem) -> Self::Config { + // this column is required by `constrain_constant` + let constant = meta.fixed_column(); + meta.enable_constant(constant); + Self::Config::configure(meta) + } + + fn synthesize( + &self, + config: Self::Config, + mut layouter: impl Layouter, + ) -> Result<(), Error> { + let in_state = layouter.assign_region( + || "Wittnes & assignation", + |mut region| { + // Witness `state` + let in_state: [AssignedCell; 25] = { + let mut state: Vec> = Vec::with_capacity(25); + for (offset, val) in self.in_state.iter().enumerate() { + let cell = region.assign_advice( + || "witness input state", + config.lane, + offset, + || Ok(*val), + )?; + state.push(cell) + } + state.try_into().unwrap() + }; + Ok(in_state) + }, + )?; + + let out_state = assign_xi(&config.generic, &mut layouter, &in_state)?; + + layouter.assign_region( + || "Check outstate", + |mut region| { + for (assigned, value) in out_state.iter().zip(self.out_state.iter()) { + region.constrain_constant(assigned.cell(), value)?; + } + Ok(()) + }, + )?; + Ok(()) + } + } + + let input1: State = [ + [1, 0, 0, 0, 0], + [0, 0, 0, 0, 0], + [0, 0, 0, 0, 0], + [0, 0, 0, 0, 0], + [0, 0, 0, 0, 0], + ]; + let mut in_biguint = StateBigInt::default(); + let mut in_state: [Fp; 25] = [Fp::zero(); 25]; + + for (x, y) in (0..5).cartesian_product(0..5) { + in_biguint[(x, y)] = convert_b2_to_b9(input1[x][y]); + in_state[5 * x + y] = biguint_to_f(&in_biguint[(x, y)]); + } + let s1_arith = KeccakFArith::xi(&in_biguint); + let mut out_state: [Fp; 25] = [Fp::zero(); 25]; + for (x, y) in (0..5).cartesian_product(0..5) { + out_state[5 * x + y] = biguint_to_f(&s1_arith[(x, y)]); + } + let circuit = MyCircuit:: { + in_state, + out_state, + _marker: PhantomData, + }; + + // Test without public inputs + let prover = MockProver::::run(9, &circuit, vec![]).unwrap(); + + assert_eq!(prover.verify(), Ok(())); + } +} diff --git a/keccak256/src/permutation/iota.rs b/keccak256/src/permutation/iota.rs deleted file mode 100644 index 2cfb6f9bd5..0000000000 --- a/keccak256/src/permutation/iota.rs +++ /dev/null @@ -1,33 +0,0 @@ -use crate::arith_helpers::{convert_b2_to_b13, convert_b2_to_b9, A4}; -use crate::common::{PERMUTATION, ROUND_CONSTANTS}; -use crate::gate_helpers::biguint_to_f; -use eth_types::Field; -use itertools::Itertools; - -#[derive(Clone, Debug)] -pub struct IotaConstants { - pub round_constant_b13: F, - pub a4_times_round_constants_b9: [F; PERMUTATION], -} - -impl Default for IotaConstants { - fn default() -> Self { - let round_constant_b13 = - biguint_to_f::(&convert_b2_to_b13(ROUND_CONSTANTS[PERMUTATION - 1])); - - let a4_times_round_constants_b9: [F; 24] = ROUND_CONSTANTS - .iter() - .map(|&x| { - let constant = A4 * convert_b2_to_b9(x); - biguint_to_f::(&constant) - }) - .collect_vec() - .try_into() - .unwrap(); - - Self { - round_constant_b13, - a4_times_round_constants_b9, - } - } -} diff --git a/keccak256/src/permutation/pi.rs b/keccak256/src/permutation/pi.rs deleted file mode 100644 index 41c815ed59..0000000000 --- a/keccak256/src/permutation/pi.rs +++ /dev/null @@ -1,18 +0,0 @@ -use eth_types::Field; -use halo2_proofs::circuit::AssignedCell; -use itertools::Itertools; - -/// The Keccak Pi step -/// -/// It has no gates. We just have to permute the previous state into the correct -/// order. The copy constrain in the next gate can then enforce the Pi step -/// permutation. -pub fn pi_gate_permutation(state: &[AssignedCell; 25]) -> [AssignedCell; 25] { - let state: [AssignedCell; 25] = (0..5) - .cartesian_product(0..5) - .map(|(x, y)| state[5 * ((x + 3 * y) % 5) + x].clone()) - .collect::>() - .try_into() - .unwrap(); - state -} diff --git a/keccak256/src/permutation/rho.rs b/keccak256/src/permutation/rho.rs deleted file mode 100644 index 4d0be98321..0000000000 --- a/keccak256/src/permutation/rho.rs +++ /dev/null @@ -1,225 +0,0 @@ -use crate::common::ROTATION_CONSTANTS; -use crate::gate_helpers::{biguint_to_f, f_to_biguint}; -use crate::permutation::{ - generic::GenericConfig, - rho_helpers::{slice_lane, RhoLane}, - tables::{Base13toBase9TableConfig, StackableTable}, -}; -use eth_types::Field; -use halo2_proofs::{ - circuit::{AssignedCell, Layouter}, - plonk::Error, -}; -use itertools::Itertools; - -pub fn assign_rho( - layouter: &mut impl Layouter, - base13to9_config: &Base13toBase9TableConfig, - generic: &GenericConfig, - stackable: &StackableTable, - state: &[AssignedCell; 25], -) -> Result<[AssignedCell; 25], Error> { - let mut next_state = vec![]; - let mut step2_od_join = vec![]; - let mut step3_od_join = vec![]; - for (lane_idx, lane) in state.iter().enumerate() { - let rotation = { - let x = lane_idx / 5; - let y = lane_idx % 5; - ROTATION_CONSTANTS[x][y] - }; - let (conversions, special) = - RhoLane::new(f_to_biguint(*lane.value().unwrap_or(&F::zero())), rotation) - .get_full_witness(); - let slices = slice_lane(rotation); - - let (input_coefs, mut output_coefs, step2_od, step3_od) = - base13to9_config.assign_region(layouter, &slices, &conversions)?; - - let input_pobs = conversions - .iter() - .map(|c| biguint_to_f::(&c.input.power_of_base)) - .collect_vec(); - - let mut output_pobs = conversions - .iter() - .map(|c| biguint_to_f::(&c.output.power_of_base)) - .collect_vec(); - // Final output power of base - output_pobs.push(biguint_to_f::(&special.output_pob)); - - let input_from_chunks = - generic.linear_combine_consts(layouter, input_coefs, input_pobs, None)?; - let last_chunk = generic.sub_advice(layouter, &lane, &input_from_chunks)?; - - let final_output_coef = stackable.lookup_special_chunks(layouter, &diff)?; - output_coefs.push(final_output_coef); - - let output_lane = - generic.linear_combine_consts(layouter, output_coefs, output_pobs, None)?; - next_state.push(output_lane); - step2_od_join.extend(step2_od); - step3_od_join.extend(step3_od); - } - let step2_sum = generic.running_sum(layouter, step2_od_join, None)?; - let step3_sum = generic.running_sum(layouter, step3_od_join, None)?; - stackable.lookup_range_12(layouter, &[step2_sum])?; - stackable.lookup_range_169(layouter, &[step3_sum])?; - Ok(next_state.try_into().unwrap()) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::arith_helpers::{convert_b2_to_b13, StateBigInt}; - use crate::common::*; - use crate::gate_helpers::biguint_to_f; - use crate::keccak_arith::*; - use halo2_proofs::{ - circuit::{Layouter, SimpleFloorPlanner}, - dev::MockProver, - pairing::bn256::Fr as Fp, - plonk::{Advice, Circuit, Column, ConstraintSystem, Error, TableColumn}, - }; - use itertools::Itertools; - #[test] - fn test_rho_gate() { - #[derive(Default)] - struct MyCircuit { - in_state: [F; 25], - out_state: [F; 25], - } - - #[derive(Clone)] - struct MyConfig { - advice: Column, - generic: GenericConfig, - stackable: StackableTable, - base13to9_config: Base13toBase9TableConfig, - } - impl Circuit for MyCircuit { - type Config = MyConfig; - type FloorPlanner = SimpleFloorPlanner; - - fn without_witnesses(&self) -> Self { - Self::default() - } - - fn configure(meta: &mut ConstraintSystem) -> Self::Config { - let advices: [Column; 3] = (0..3) - .map(|_| meta.advice_column()) - .collect::>() - .try_into() - .unwrap(); - - let fixed = meta.fixed_column(); - let stackable_cols: [TableColumn; 3] = (0..3) - .map(|_| meta.lookup_table_column()) - .collect_vec() - .try_into() - .unwrap(); - let base13to9_cols: [TableColumn; 3] = (0..3) - .map(|_| meta.lookup_table_column()) - .collect_vec() - .try_into() - .unwrap(); - let stackable = StackableTable::configure(meta, advices, stackable_cols); - let generic = GenericConfig::configure(meta, advices, fixed); - let base13to9_config = - Base13toBase9TableConfig::configure(meta, advices, base13to9_cols); - - Self::Config { - advice: advices[0], - generic, - stackable, - base13to9_config, - } - } - - fn synthesize( - &self, - mut config: Self::Config, - mut layouter: impl Layouter, - ) -> Result<(), Error> { - config.base13to9_config.load(&mut layouter)?; - config.stackable.load(&mut layouter)?; - let state = layouter.assign_region( - || "assign input state", - |mut region| { - let state = self - .in_state - .iter() - .enumerate() - .map(|(offset, &value)| { - region.assign_advice(|| "lane", config.advice, offset, || Ok(value)) - }) - .collect::>, Error>>()?; - - Ok(state.try_into().unwrap()) - }, - )?; - let out_state = assign_rho( - &mut layouter, - &config.base13to9_config, - &config.generic, - &config.stackable, - &state, - )?; - layouter.assign_region( - || "check final states", - |mut region| { - for (assigned, value) in out_state.iter().zip(self.out_state.iter()) { - region.constrain_constant(assigned.cell(), value)?; - } - Ok(()) - }, - )?; - - Ok(()) - } - } - - let input1: State = [ - [102, 111, 111, 98, 97], - [114, 0, 5, 0, 0], - [0, 0, 0, 0, 0], - [0, 0, 0, 5, 0], - [0, 0, 0, 0, 0], - ]; - let mut in_biguint = StateBigInt::default(); - let mut in_state: [Fp; 25] = [Fp::zero(); 25]; - - for (x, y) in (0..5).cartesian_product(0..5) { - in_biguint[(x, y)] = convert_b2_to_b13(input1[x][y]); - } - let s0_arith = KeccakFArith::theta(&in_biguint); - for (x, y) in (0..5).cartesian_product(0..5) { - in_state[5 * x + y] = biguint_to_f(&s0_arith[(x, y)]); - } - let s1_arith = KeccakFArith::rho(&s0_arith); - let mut out_state: [Fp; 25] = [Fp::zero(); 25]; - for (x, y) in (0..5).cartesian_product(0..5) { - out_state[5 * x + y] = biguint_to_f(&s1_arith[(x, y)]); - } - let circuit = MyCircuit:: { - in_state, - out_state, - }; - let k = 15; - #[cfg(feature = "dev-graph")] - { - use plotters::prelude::*; - let root = - BitMapBackend::new("rho-test-circuit.png", (1024, 16384)).into_drawing_area(); - root.fill(&WHITE).unwrap(); - let root = root.titled("Rho", ("sans-serif", 60)).unwrap(); - halo2_proofs::dev::CircuitLayout::default() - .render(k, &circuit, &root) - .unwrap(); - } - // Test without public inputs - let prover = MockProver::::run(k, &circuit, vec![]).unwrap(); - - assert_eq!(prover.verify(), Ok(())); - } -} diff --git a/keccak256/src/permutation/theta.rs b/keccak256/src/permutation/theta.rs deleted file mode 100644 index 3565929dff..0000000000 --- a/keccak256/src/permutation/theta.rs +++ /dev/null @@ -1,188 +0,0 @@ -use crate::arith_helpers::*; -use crate::permutation::generic::GenericConfig; -use eth_types::Field; -use halo2_proofs::{ - circuit::{AssignedCell, Layouter}, - plonk::Error, -}; -use itertools::Itertools; - -pub fn assign_theta( - generic: &GenericConfig, - layouter: &mut impl Layouter, - state: &[AssignedCell; 25], -) -> Result<[AssignedCell; 25], Error> { - let theta_col_sums = (0..5) - .map(|x| { - generic.running_sum( - layouter, - (0..5).map(|y| state[5 * x + y].clone()).collect(), - None, - ) - }) - .collect::, Error>>()?; - - let out_state = (0..5) - .cartesian_product(0..5) - .map(|(x, y)| { - let cells = vec![ - state[5 * x + y].clone(), - theta_col_sums[(x + 4) % 5].clone(), - theta_col_sums[(x + 1) % 5].clone(), - ]; - let vs = vec![F::one(), F::one(), F::from(B13 as u64)]; - generic.linear_combine_consts(layouter, cells, vs, None) - }) - .collect::, Error>>()?; - - Ok(out_state.try_into().unwrap()) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::common::*; - use crate::gate_helpers::biguint_to_f; - use crate::keccak_arith::*; - use eth_types::Field; - use halo2_proofs::pairing::bn256::Fr as Fp; - use halo2_proofs::{ - circuit::{Layouter, SimpleFloorPlanner}, - dev::MockProver, - plonk::{Advice, Circuit, Column, ConstraintSystem, Error}, - }; - use itertools::Itertools; - use std::marker::PhantomData; - - #[test] - fn test_theta_gates() { - #[derive(Clone, Debug)] - struct MyConfig { - lane: Column, - generic: GenericConfig, - } - - impl MyConfig { - pub fn configure(meta: &mut ConstraintSystem) -> Self { - let advices: [Column; 3] = (0..3) - .map(|_| { - let column = meta.advice_column(); - meta.enable_equality(column); - column - }) - .collect::>() - .try_into() - .unwrap(); - let fixed = meta.fixed_column(); - - let lane = advices[0]; - let generic = GenericConfig::configure(meta, advices, fixed); - Self { lane, generic } - } - } - #[derive(Default)] - struct MyCircuit { - in_state: [F; 25], - out_state: [F; 25], - _marker: PhantomData, - } - impl Circuit for MyCircuit { - type Config = MyConfig; - type FloorPlanner = SimpleFloorPlanner; - - fn without_witnesses(&self) -> Self { - Self::default() - } - - fn configure(meta: &mut ConstraintSystem) -> Self::Config { - // this column is required by `constrain_constant` - let constant = meta.fixed_column(); - meta.enable_constant(constant); - Self::Config::configure(meta) - } - - fn synthesize( - &self, - config: Self::Config, - mut layouter: impl Layouter, - ) -> Result<(), Error> { - let in_state = layouter.assign_region( - || "Wittnes & assignation", - |mut region| { - // Witness `state` - let in_state: [AssignedCell; 25] = { - let mut state: Vec> = Vec::with_capacity(25); - for (offset, val) in self.in_state.iter().enumerate() { - let cell = region.assign_advice( - || "witness input state", - config.lane, - offset, - || Ok(*val), - )?; - state.push(cell) - } - state.try_into().unwrap() - }; - Ok(in_state) - }, - )?; - - let out_state = assign_theta(&config.generic, &mut layouter, &in_state)?; - - layouter.assign_region( - || "Check outstate", - |mut region| { - for (assigned, value) in out_state.iter().zip(self.out_state.iter()) { - region.constrain_constant(assigned.cell(), value)?; - } - Ok(()) - }, - )?; - Ok(()) - } - } - - let input1: State = [ - [1, 0, 0, 0, 0], - [0, 0, 0, 9223372036854775808, 0], - [0, 0, 0, 0, 0], - [0, 0, 0, 0, 0], - [0, 0, 0, 0, 0], - ]; - let mut in_biguint = StateBigInt::default(); - let mut in_state: [Fp; 25] = [Fp::zero(); 25]; - - for (x, y) in (0..5).cartesian_product(0..5) { - in_biguint[(x, y)] = convert_b2_to_b13(input1[x][y]); - in_state[5 * x + y] = biguint_to_f(&in_biguint[(x, y)]); - } - let s1_arith = KeccakFArith::theta(&in_biguint); - let mut out_state: [Fp; 25] = [Fp::zero(); 25]; - for (x, y) in (0..5).cartesian_product(0..5) { - out_state[5 * x + y] = biguint_to_f(&s1_arith[(x, y)]); - } - - let circuit = MyCircuit:: { - in_state, - out_state, - _marker: PhantomData, - }; - - // Test without public inputs - let prover = MockProver::::run(9, &circuit, vec![]).unwrap(); - - assert_eq!(prover.verify(), Ok(())); - - let mut out_state2 = out_state; - out_state2[0] = Fp::from(5566u64); - - let circuit2 = MyCircuit:: { - in_state, - out_state: out_state2, - _marker: PhantomData, - }; - - let prover = MockProver::::run(9, &circuit2, vec![]).unwrap(); - assert!(prover.verify().is_err()); - } -} diff --git a/keccak256/src/permutation/xi.rs b/keccak256/src/permutation/xi.rs deleted file mode 100644 index 989a62fb2f..0000000000 --- a/keccak256/src/permutation/xi.rs +++ /dev/null @@ -1,163 +0,0 @@ -use crate::arith_helpers::{A1, A2, A3}; -use crate::permutation::generic::GenericConfig; -use eth_types::Field; -use halo2_proofs::{ - circuit::{AssignedCell, Layouter}, - plonk::Error, -}; -use itertools::Itertools; - -pub fn assign_xi( - generic: &GenericConfig, - layouter: &mut impl Layouter, - state: &[AssignedCell; 25], -) -> Result<[AssignedCell; 25], Error> { - let out_state = (0..5) - .cartesian_product(0..5) - .map(|(x, y)| { - let cells = vec![ - state[5 * x + y].clone(), - state[5 * ((x + 1) % 5) + y].clone(), - state[5 * ((x + 2) % 5) + y].clone(), - ]; - let vs = vec![F::from(A1), F::from(A2), F::from(A3)]; - generic.linear_combine_consts(layouter, cells, vs, None) - }) - .collect::, Error>>()?; - Ok(out_state.try_into().unwrap()) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::arith_helpers::*; - use crate::common::*; - use crate::gate_helpers::biguint_to_f; - use crate::keccak_arith::*; - use halo2_proofs::circuit::Layouter; - use halo2_proofs::pairing::bn256::Fr as Fp; - use halo2_proofs::plonk::{Advice, Column, ConstraintSystem, Error}; - use halo2_proofs::{circuit::SimpleFloorPlanner, dev::MockProver, plonk::Circuit}; - use itertools::Itertools; - use std::marker::PhantomData; - - #[test] - fn test_xi_gate() { - #[derive(Clone, Debug)] - struct MyConfig { - lane: Column, - generic: GenericConfig, - } - - impl MyConfig { - pub fn configure(meta: &mut ConstraintSystem) -> Self { - let advices: [Column; 3] = (0..3) - .map(|_| { - let column = meta.advice_column(); - meta.enable_equality(column); - column - }) - .collect::>() - .try_into() - .unwrap(); - let fixed = meta.fixed_column(); - - let lane = advices[0]; - let generic = GenericConfig::configure(meta, advices, fixed); - Self { lane, generic } - } - } - #[derive(Default)] - struct MyCircuit { - in_state: [F; 25], - out_state: [F; 25], - _marker: PhantomData, - } - - impl Circuit for MyCircuit { - type Config = MyConfig; - type FloorPlanner = SimpleFloorPlanner; - - fn without_witnesses(&self) -> Self { - Self::default() - } - - fn configure(meta: &mut ConstraintSystem) -> Self::Config { - // this column is required by `constrain_constant` - let constant = meta.fixed_column(); - meta.enable_constant(constant); - Self::Config::configure(meta) - } - - fn synthesize( - &self, - config: Self::Config, - mut layouter: impl Layouter, - ) -> Result<(), Error> { - let in_state = layouter.assign_region( - || "Wittnes & assignation", - |mut region| { - // Witness `state` - let in_state: [AssignedCell; 25] = { - let mut state: Vec> = Vec::with_capacity(25); - for (offset, val) in self.in_state.iter().enumerate() { - let cell = region.assign_advice( - || "witness input state", - config.lane, - offset, - || Ok(*val), - )?; - state.push(cell) - } - state.try_into().unwrap() - }; - Ok(in_state) - }, - )?; - - let out_state = assign_xi(&config.generic, &mut layouter, &in_state)?; - - layouter.assign_region( - || "Check outstate", - |mut region| { - for (assigned, value) in out_state.iter().zip(self.out_state.iter()) { - region.constrain_constant(assigned.cell(), value)?; - } - Ok(()) - }, - )?; - Ok(()) - } - } - - let input1: State = [ - [1, 0, 0, 0, 0], - [0, 0, 0, 0, 0], - [0, 0, 0, 0, 0], - [0, 0, 0, 0, 0], - [0, 0, 0, 0, 0], - ]; - let mut in_biguint = StateBigInt::default(); - let mut in_state: [Fp; 25] = [Fp::zero(); 25]; - - for (x, y) in (0..5).cartesian_product(0..5) { - in_biguint[(x, y)] = convert_b2_to_b9(input1[x][y]); - in_state[5 * x + y] = biguint_to_f(&in_biguint[(x, y)]); - } - let s1_arith = KeccakFArith::xi(&in_biguint); - let mut out_state: [Fp; 25] = [Fp::zero(); 25]; - for (x, y) in (0..5).cartesian_product(0..5) { - out_state[5 * x + y] = biguint_to_f(&s1_arith[(x, y)]); - } - let circuit = MyCircuit:: { - in_state, - out_state, - _marker: PhantomData, - }; - - // Test without public inputs - let prover = MockProver::::run(9, &circuit, vec![]).unwrap(); - - assert_eq!(prover.verify(), Ok(())); - } -} From 8483ce4f1683c9ef5d3150bf8325e650bb191289 Mon Sep 17 00:00:00 2001 From: ChihChengLiang Date: Tue, 9 Aug 2022 15:52:45 +0800 Subject: [PATCH 10/12] clippy round --- circuit-benchmarks/src/keccak_permutation.rs | 41 +++----------------- keccak256/src/permutation/circuit.rs | 21 ++++------ keccak256/src/permutation/components.rs | 4 +- keccak256/src/permutation/generic.rs | 6 +-- keccak256/src/permutation/tables.rs | 6 +-- 5 files changed, 20 insertions(+), 58 deletions(-) diff --git a/circuit-benchmarks/src/keccak_permutation.rs b/circuit-benchmarks/src/keccak_permutation.rs index 587cf9afd4..657e299054 100644 --- a/circuit-benchmarks/src/keccak_permutation.rs +++ b/circuit-benchmarks/src/keccak_permutation.rs @@ -5,16 +5,12 @@ use halo2_proofs::{ circuit::{AssignedCell, Layouter, SimpleFloorPlanner}, plonk::{Circuit, ConstraintSystem, Error}, }; -use keccak256::{ - common::NEXT_INPUTS_LANES, keccak_arith::KeccakFArith, permutation::circuit::KeccakFConfig, -}; +use keccak256::{common::NEXT_INPUTS_LANES, permutation::circuit::KeccakFConfig}; #[derive(Default, Clone)] struct KeccakRoundTestCircuit { in_state: [F; 25], - out_state: [F; 25], next_mixing: Option<[F; NEXT_INPUTS_LANES]>, - is_mixing: bool, } impl Circuit for KeccakRoundTestCircuit { @@ -44,12 +40,12 @@ impl Circuit for KeccakRoundTestCircuit { // Witness `state` let in_state: [AssignedCell; 25] = { let mut state: Vec> = Vec::with_capacity(25); - for (idx, val) in self.in_state.iter().enumerate() { + for &val in self.in_state.iter() { let cell = region.assign_advice( || "witness input state", - config.state[idx], + config.advice, offset, - || Ok(*val), + || Ok(val), )?; state.push(cell) } @@ -59,13 +55,7 @@ impl Circuit for KeccakRoundTestCircuit { }, )?; - config.assign_all( - &mut layouter, - in_state, - self.out_state, - self.is_mixing, - self.next_mixing, - )?; + config.assign_all(&mut layouter, in_state, self.next_mixing)?; Ok(()) } } @@ -97,14 +87,6 @@ mod tests { [0, 0, 0, 0, 0], ]; - let next_input: State = [ - [2, 0, 0, 0, 0], - [0, 0, 0, 0, 0], - [0, 0, 0, 0, 0], - [0, 0, 0, 0, 0], - [0, 0, 0, 0, 0], - ]; - let mut in_state_biguint = StateBigInt::default(); // Generate in_state as `[Fr;25]` @@ -114,23 +96,10 @@ mod tests { in_state_biguint[(x, y)] = convert_b2_to_b13(in_state[x][y]); } - // Compute out_state_mix - let mut out_state_mix = in_state_biguint.clone(); - KeccakFArith::permute_and_absorb(&mut out_state_mix, Some(&next_input)); - - // Compute out_state_non_mix - let mut out_state_non_mix = in_state_biguint.clone(); - KeccakFArith::permute_and_absorb(&mut out_state_non_mix, None); - - // Generate out_state as `[Fr;25]` - let out_state_non_mix: [Fr; 25] = state_bigint_to_field(out_state_non_mix); - // Build the circuit let circuit = KeccakRoundTestCircuit:: { in_state: in_state_fp, - out_state: out_state_non_mix, next_mixing: None, - is_mixing: false, }; let degree: u32 = var("DEGREE") diff --git a/keccak256/src/permutation/circuit.rs b/keccak256/src/permutation/circuit.rs index 2ea717a180..e36320e418 100644 --- a/keccak256/src/permutation/circuit.rs +++ b/keccak256/src/permutation/circuit.rs @@ -27,7 +27,7 @@ pub struct KeccakFConfig { base13to9_config: Base13toBase9TableConfig, from_b9_table: FromBase9TableConfig, from_b2_table: FromBinaryTableConfig, - advice: Column, + pub advice: Column, } impl KeccakFConfig { @@ -92,7 +92,6 @@ impl KeccakFConfig { &self, layouter: &mut impl Layouter, in_state: [AssignedCell; 25], - flag: bool, next_mixing: Option<[F; NEXT_INPUTS_LANES]>, ) -> Result<([AssignedCell; 25], [AssignedCell; 25]), Error> { let iota_constants = IotaConstants::default(); @@ -131,7 +130,9 @@ impl KeccakFConfig { convert_from_b9_to_b13(layouter, &self.from_b9_table, &self.generic, state, false)? .0; } - let (f_mix, f_no_mix) = self.stackable.assign_boolean_flag(layouter, Some(flag))?; + let (f_mix, f_no_mix) = self + .stackable + .assign_boolean_flag(layouter, next_mixing.is_some())?; state[0] = self.generic.conditional_add_const( layouter, &state[0], @@ -147,7 +148,7 @@ impl KeccakFConfig { for (i, input) in next_input.iter().enumerate() { state[i] = self .generic - .conditional_add_advice(layouter, &state[i], &f_mix, &input)?; + .conditional_add_advice(layouter, &state[i], &f_mix, input)?; } let (mut state_b13, state_b2) = convert_from_b9_to_b13(layouter, &self.from_b9_table, &self.generic, state, true)?; @@ -158,7 +159,7 @@ impl KeccakFConfig { &f_mix, &iota_constants.round_constant_b13, )?; - Ok((state_b13.try_into().unwrap(), state_b2)) + Ok((state_b13, state_b2)) } } @@ -188,8 +189,6 @@ mod tests { in_state: [F; 25], out_state: [F; 25], next_mixing: Option<[F; NEXT_INPUTS_LANES]>, - // flag - is_mixing: bool, } impl Circuit for MyCircuit { @@ -234,8 +233,8 @@ mod tests { )?; let (state_b13, state_b2) = - config.assign_all(&mut layouter, state, self.is_mixing, self.next_mixing)?; - if self.is_mixing { + config.assign_all(&mut layouter, state, self.next_mixing)?; + if self.next_mixing.is_some() { layouter.assign_region( || "check final states", |mut region| { @@ -315,7 +314,6 @@ mod tests { in_state: in_state_fp, out_state: out_state_non_mix, next_mixing: None, - is_mixing: false, }; let prover = MockProver::::run(17, &circuit, vec![]).unwrap(); @@ -328,7 +326,6 @@ mod tests { in_state: out_state_non_mix, out_state: out_state_non_mix, next_mixing: None, - is_mixing: true, }; let k = 17; let prover = MockProver::::run(k, &circuit, vec![]).unwrap(); @@ -355,7 +352,6 @@ mod tests { in_state: in_state_fp, out_state: out_state_mix, next_mixing: Some(next_input_fp), - is_mixing: true, }; let prover = MockProver::::run(17, &circuit, vec![]).unwrap(); @@ -368,7 +364,6 @@ mod tests { in_state: out_state_non_mix, out_state: out_state_non_mix, next_mixing: Some(next_input_fp), - is_mixing: true, }; let prover = MockProver::::run(17, &circuit, vec![]).unwrap(); diff --git a/keccak256/src/permutation/components.rs b/keccak256/src/permutation/components.rs index d2b9b77040..7529681974 100644 --- a/keccak256/src/permutation/components.rs +++ b/keccak256/src/permutation/components.rs @@ -94,7 +94,7 @@ pub fn assign_rho( let input_from_chunks = generic.linear_combine_consts(layouter, input_coefs, input_pobs, None)?; - let last_chunk = generic.sub_advice(layouter, &lane, &input_from_chunks)?; + let last_chunk = generic.sub_advice(layouter, lane, &input_from_chunks)?; let final_output_coef = stackable.lookup_special_chunks(layouter, &last_chunk)?; output_coefs.push(final_output_coef); @@ -237,7 +237,7 @@ pub fn convert_to_b9_mul_a4( }) .rev() .collect_vec(); - let output = generic.linear_combine_consts(layouter, base9s.clone(), vs, None)?; + let output = generic.linear_combine_consts(layouter, base9s, vs, None)?; Ok(output) }) .collect::>, Error>>()?; diff --git a/keccak256/src/permutation/generic.rs b/keccak256/src/permutation/generic.rs index 3daeaa5eb4..8a2d264b4b 100644 --- a/keccak256/src/permutation/generic.rs +++ b/keccak256/src/permutation/generic.rs @@ -268,10 +268,8 @@ impl GenericConfig { if let Some(outcome) = &outcome { region.constrain_equal(outcome.cell(), acc.cell())?; } - if let Some((outcome, acc)) = outcome - .as_ref() - .map(|oc| oc.value().zip(acc.value())) - .flatten() + if let Some((outcome, acc)) = + outcome.as_ref().and_then(|oc| oc.value().zip(acc.value())) { debug_assert_eq!(outcome, acc); } diff --git a/keccak256/src/permutation/tables.rs b/keccak256/src/permutation/tables.rs index 20f5e9ccfd..483cd67440 100644 --- a/keccak256/src/permutation/tables.rs +++ b/keccak256/src/permutation/tables.rs @@ -295,7 +295,7 @@ impl StackableTable { pub(crate) fn assign_boolean_flag( &self, layouter: &mut impl Layouter, - is_left: Option, + is_left: bool, ) -> Result<(AssignedCell, AssignedCell), Error> { layouter.assign_region( || "lookup for boolean flag", @@ -312,13 +312,13 @@ impl StackableTable { || "left", self.lookup_config.col1.0, offset, - || is_left.map(|flag| F::from(flag)).ok_or(Error::Synthesis), + || Ok(F::from(is_left)), )?; let right = region.assign_advice( || "right", self.lookup_config.col2.0, offset, - || is_left.map(|flag| F::from(!flag)).ok_or(Error::Synthesis), + || Ok(F::from(!is_left)), )?; Ok((left, right)) }, From 756fcd7ea74793672252a8a374f2e96eb7164116 Mon Sep 17 00:00:00 2001 From: ChihChengLiang Date: Fri, 12 Aug 2022 12:01:04 +0800 Subject: [PATCH 11/12] feedback: fix doc --- keccak256/src/permutation/tables.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/keccak256/src/permutation/tables.rs b/keccak256/src/permutation/tables.rs index 483cd67440..9f7c307c48 100644 --- a/keccak256/src/permutation/tables.rs +++ b/keccak256/src/permutation/tables.rs @@ -633,7 +633,7 @@ impl FromBase9TableConfig { #[derive(Debug, Clone)] pub struct FromBinaryTableConfig { lookup_config: ThreeColumnsLookup, - // mapping from base9 input to base13 and base2 output + /// mapping from base2 input to base9 and base13 output map: HashMap<[u8; 32], (F, F)>, } From 6b5de294a7c76b82351bd93a9c5a669c13c36d38 Mon Sep 17 00:00:00 2001 From: ChihChengLiang Date: Fri, 12 Aug 2022 12:13:24 +0800 Subject: [PATCH 12/12] feedback: rename three columns config cols --- keccak256/src/permutation/tables.rs | 107 +++++++++++++++------------- 1 file changed, 56 insertions(+), 51 deletions(-) diff --git a/keccak256/src/permutation/tables.rs b/keccak256/src/permutation/tables.rs index 9f7c307c48..7fffe593e8 100644 --- a/keccak256/src/permutation/tables.rs +++ b/keccak256/src/permutation/tables.rs @@ -25,9 +25,7 @@ pub const NUM_OF_B9_SLICES: usize = 13; #[derive(Debug, Clone)] struct ThreeColumnsLookup { q_enable: Selector, - pub(crate) col0: (Column, TableColumn), - pub(crate) col1: (Column, TableColumn), - pub(crate) col2: (Column, TableColumn), + pub(crate) cols: [(Column, TableColumn); 3], _marker: PhantomData, } impl ThreeColumnsLookup { @@ -37,27 +35,29 @@ impl ThreeColumnsLookup { table_cols: [TableColumn; 3], name: &'static str, ) -> Self { - let col0 = (adv_cols[0], table_cols[0]); - let col1 = (adv_cols[1], table_cols[1]); - let col2 = (adv_cols[2], table_cols[2]); + let cols: [(Column, TableColumn); 3] = adv_cols + .iter() + .cloned() + .zip(table_cols.iter().cloned()) + .collect_vec() + .try_into() + .unwrap(); let q_enable = meta.complex_selector(); meta.lookup(name, |meta| { let q_enable = meta.query_selector(q_enable); - let col0_adv = meta.query_advice(col0.0, Rotation::cur()); - let col1_adv = meta.query_advice(col1.0, Rotation::cur()); - let col2_adv = meta.query_advice(col2.0, Rotation::cur()); + let col0_adv = meta.query_advice(cols[0].0, Rotation::cur()); + let col1_adv = meta.query_advice(cols[1].0, Rotation::cur()); + let col2_adv = meta.query_advice(cols[2].0, Rotation::cur()); vec![ - (q_enable.clone() * col0_adv, col0.1), - (q_enable.clone() * col1_adv, col1.1), - (q_enable * col2_adv, col2.1), + (q_enable.clone() * col0_adv, cols[0].1), + (q_enable.clone() * col1_adv, cols[1].1), + (q_enable * col2_adv, cols[2].1), ] }); Self { q_enable, - col0, - col1, - col2, + cols, _marker: PhantomData, } } @@ -105,19 +105,19 @@ impl StackableTable { for i in 0..=k { table.assign_cell( || format!("tag range{}", tag), - self.lookup_config.col0.1, + self.lookup_config.cols[0].1, offset, || Ok(F::from(tag as u64)), )?; table.assign_cell( || format!("range{}", tag), - self.lookup_config.col1.1, + self.lookup_config.cols[1].1, offset, || Ok(F::from(i)), )?; table.assign_cell( || format!("dummy col range{}", tag), - self.lookup_config.col2.1, + self.lookup_config.cols[2].1, offset, || Ok(F::zero()), )?; @@ -140,19 +140,19 @@ impl StackableTable { .insert(last_chunk.to_repr(), output_coef); table.assign_cell( || "tag special chunks", - self.lookup_config.col0.1, + self.lookup_config.cols[0].1, offset, || Ok(F::from(TableTags::SpecialChunk as u64)), )?; table.assign_cell( || "last chunk", - self.lookup_config.col1.1, + self.lookup_config.cols[1].1, offset, || Ok(last_chunk), )?; table.assign_cell( || "output coef", - self.lookup_config.col2.1, + self.lookup_config.cols[2].1, offset, || Ok(output_coef), )?; @@ -167,19 +167,19 @@ impl StackableTable { for (left, right) in [(true, false), (false, true)] { table.assign_cell( || "tag boolean flag", - self.lookup_config.col0.1, + self.lookup_config.cols[0].1, offset, || Ok(F::from(TableTags::BooleanFlag as u64)), )?; table.assign_cell( || "left", - self.lookup_config.col1.1, + self.lookup_config.cols[1].1, offset, || Ok(F::from(left)), )?; table.assign_cell( || "right", - self.lookup_config.col2.1, + self.lookup_config.cols[2].1, offset, || Ok(F::from(right)), )?; @@ -221,14 +221,19 @@ impl StackableTable { self.lookup_config.q_enable.enable(&mut region, offset)?; region.assign_advice_from_constant( || "tag", - self.lookup_config.col0.0, + self.lookup_config.cols[0].0, offset, tag, )?; - v.copy_advice(|| "value", &mut region, self.lookup_config.col1.0, offset)?; + v.copy_advice( + || "value", + &mut region, + self.lookup_config.cols[1].0, + offset, + )?; region.assign_advice_from_constant( || "dummy", - self.lookup_config.col2.0, + self.lookup_config.cols[2].0, offset, F::zero(), )?; @@ -265,19 +270,19 @@ impl StackableTable { self.lookup_config.q_enable.enable(&mut region, offset)?; region.assign_advice_from_constant( || "tag", - self.lookup_config.col0.0, + self.lookup_config.cols[0].0, offset, tag, )?; last_chunk.copy_advice( || "last chunk", &mut region, - self.lookup_config.col1.0, + self.lookup_config.cols[1].0, offset, )?; region.assign_advice( || "output coef", - self.lookup_config.col2.0, + self.lookup_config.cols[2].0, offset, || { last_chunk @@ -304,19 +309,19 @@ impl StackableTable { self.lookup_config.q_enable.enable(&mut region, offset)?; region.assign_advice_from_constant( || "tag", - self.lookup_config.col0.0, + self.lookup_config.cols[0].0, offset, F::from(TableTags::BooleanFlag as u64), )?; let left = region.assign_advice( || "left", - self.lookup_config.col1.0, + self.lookup_config.cols[1].0, offset, || Ok(F::from(is_left)), )?; let right = region.assign_advice( || "right", - self.lookup_config.col2.0, + self.lookup_config.cols[2].0, offset, || Ok(F::from(!is_left)), )?; @@ -389,20 +394,20 @@ impl Base13toBase9TableConfig { .insert(input_b13.to_repr(), (output_b9, overflow_detector)); table.assign_cell( || "base 13", - self.lookup_config.col0.1, + self.lookup_config.cols[0].1, i, || Ok(input_b13), )?; table.assign_cell( || "base 9", - self.lookup_config.col1.1, + self.lookup_config.cols[1].1, i, || Ok(output_b9), )?; table.assign_cell( || "overflow_detector", - self.lookup_config.col2.1, + self.lookup_config.cols[2].1, i, || Ok(overflow_detector), )?; @@ -454,7 +459,7 @@ impl Base13toBase9TableConfig { let outputs = self.map.get(&input.to_repr()); let input_coef = region.assign_advice( || "Input Coef", - self.lookup_config.col0.0, + self.lookup_config.cols[0].0, offset, || Ok(input), )?; @@ -462,7 +467,7 @@ impl Base13toBase9TableConfig { let output_coef = region.assign_advice( || "Output Coef", - self.lookup_config.col1.0, + self.lookup_config.cols[1].0, offset, || outputs.map(|o| o.0).ok_or(Error::Synthesis), )?; @@ -470,7 +475,7 @@ impl Base13toBase9TableConfig { let od = region.assign_advice( || "Overflow detector", - self.lookup_config.col2.0, + self.lookup_config.cols[2].0, offset, || outputs.map(|o| o.1).ok_or(Error::Synthesis), )?; @@ -543,20 +548,20 @@ impl FromBase9TableConfig { self.map.insert(input_b9.to_repr(), (output_b13, output_b2)); table.assign_cell( || "base 9", - self.lookup_config.col0.1, + self.lookup_config.cols[0].1, i, || Ok(input_b9), )?; table.assign_cell( || "base 13", - self.lookup_config.col1.1, + self.lookup_config.cols[1].1, i, || Ok(output_b13), )?; table.assign_cell( || "base 2", - self.lookup_config.col2.1, + self.lookup_config.cols[2].1, i, || Ok(output_b2), )?; @@ -602,7 +607,7 @@ impl FromBase9TableConfig { self.lookup_config.q_enable.enable(&mut region, offset)?; let input = region.assign_advice( || "base 9", - self.lookup_config.col0.0, + self.lookup_config.cols[0].0, offset, || input_coef.ok_or(Error::Synthesis), )?; @@ -611,14 +616,14 @@ impl FromBase9TableConfig { let output_b13 = region.assign_advice( || "base 13", - self.lookup_config.col1.0, + self.lookup_config.cols[1].0, offset, || output.map(|v| v.0).ok_or(Error::Synthesis), )?; output_b13_cells.push(output_b13); let output_b2 = region.assign_advice( || "base 2", - self.lookup_config.col2.0, + self.lookup_config.cols[2].0, offset, || output.map(|v| v.1).ok_or(Error::Synthesis), )?; @@ -656,20 +661,20 @@ impl FromBinaryTableConfig { table.assign_cell( || "base 2", - self.lookup_config.col0.1, + self.lookup_config.cols[0].1, i, || Ok(input_b2), )?; table.assign_cell( || "base 9", - self.lookup_config.col1.1, + self.lookup_config.cols[1].1, i, || Ok(output_b9), )?; table.assign_cell( || "base 13", - self.lookup_config.col2.1, + self.lookup_config.cols[2].1, i, || Ok(output_b13), )?; @@ -715,7 +720,7 @@ impl FromBinaryTableConfig { self.lookup_config.q_enable.enable(&mut region, offset)?; let input = region.assign_advice( || "base 2", - self.lookup_config.col0.0, + self.lookup_config.cols[0].0, offset, || input_coef.ok_or(Error::Synthesis), )?; @@ -725,14 +730,14 @@ impl FromBinaryTableConfig { let output_b9 = region.assign_advice( || "base 9", - self.lookup_config.col1.0, + self.lookup_config.cols[1].0, offset, || output.map(|v| v.0).ok_or(Error::Synthesis), )?; output_b9_cells.push(output_b9); let output_b13 = region.assign_advice( || "base 13", - self.lookup_config.col2.0, + self.lookup_config.cols[2].0, offset, || output.map(|v| v.1).ok_or(Error::Synthesis), )?;