Skip to content
This repository was archived by the owner on Jul 5, 2024. It is now read-only.

Commit 1ce3f7f

Browse files
Build keccak all toghether (#144)
* Add KeccakFConfig & allocation structure def The KeccakFConfig contains all of the gadget configurations of the gadgets plus the logic for the allocations of each of the keccak steps on each of the regions. This is the first design guideline that seems can fit in with the infra we have. Works with #105 * Remove biguint_to_pallas duplicity * Add aux functions to switch state repr We need to move from `FieldExt` to `BigUint` Repr in order to execute KeccaK intermediate steps so that we can allocate all the intermediate states of the keccak algorithm inside of the circuit. Therefore we need functions that allow us to swap between both representations. * Add `assign_state` placeholders for Pi and Rho Configs * Add 24-loop state allocation phase in KeccakConfig * Add state_assign minus mixing stage * Add configure initial impl for `KeccakConfig` * Add basic b9 & b13 ROUND_CTANTS allocation * Change gadgets state allocation to add out_state We now also allocate the out_state of the gadget when we allocate the entire witness for the gadget in keccak. * Merge `next_input` and state assigment to single fn We can simply do the assigment of the `out_state`, `state` and `next_input` in a single function reducing the overhead and the verbosity. * Change `q_enable` activations to happen in `assign_state` * Add missing offset increments in KeccakConfig allocation * Set IotaB9Config Selector as generic Expression * Set IotaB13 Selector as Expression * Change AbsorbConfig design and allocation We now allocate the Absorb as: - State Row - Next Mixing Row - Out State Row * Move state transformation fns to arith_helpers mod * Add MixingConfig preliminary design * Externalize state conversion functions * Add out_state computation during `assign_state` runtime for B13 & B9 * Add `State` creation function in arith_helpers * Change AbsorbConfig assigment to compute out_state internally * Add assign_state_and_mixing_flag_and_rc for IotaB9Config * Finalize first MixingConfig configure fn * Change AbsorbConfig to copy_cell strategy * Add IotaB13Config Cell copy constrains strategy & modify tests * Update IotaB9Config assigment functions * Change KeccakF circuit calls to IotaB9 and Mixing configs * Fix `state_bigint_to_pallas` slice copy lengths * Add mixing step to KeccakFArith * test_absorb_gate: Witness input state to get (Cell, Value) tuples. * Fix range of `state_to_state_bigint` * IotaB9:_Fix test_flag wrong assignation_err * iota_b9: Introduce q_last, q_not_last selectors. These are used to differentiate between gates for the steady state, and gates for the final round (where an is_mixing flag is witnessed by the prover). In the final round, q_last * flag is used as a composite selector. * Add IotaB9 missing test cases * IotaB13: Add internal selector + flag setup With the previous setup, the gate was producing `ConstraintPoisoned` due to the usage of `round_ctant_b13` at rotation:next to store the `is_mixing` flag inside. It also was activated/deactivated following the same bool logic as IotaB9, and has been changed. - IotaB13 now activates when `is_mixing = false` so no matter the inputs the verification will pass as the gate is not active. - IotaB13 contains now an internal selector `q_mixing` which is always active and prevents the gate equations to fail due to queriyng `round_ctant_b13` cells that they shouldn't. This completes all the development needed for IotaB9 and IotaB13 in order to add them inside the `MixingConfig` and so work towards closing issue #105 * Absorb: Add internal selector + flag setup With the previous setup, the gate was producing `ConstraintPoisoned` due to the usage of `absorb_next_inputs` at rotation:next to store the `is_mixing` flag inside. It also was activated/deactivated following the same bool logic as IotaB9, and has been changed. - Absorb now activates when `is_mixing = false` so no matter the inputs the verification will pass as the gate is not active. - Absorb contains now an internal selector `q_mixing` which is always active and prevents the gate equations to fail due to queriyng `absorb_next_inputs` cells that they shouldn't. ASSIGNATION MAP: - STATE (25 columns) (offset -1) - NEXT_INPUTS (17 columns) + is_mixing flag (1 column) (offset +0) (current rotation) - OUT_STATE (25 columns) (offset +1) This completes all the development needed for `AbsorbConfig` in order to add them inside the `MixingConfig` and so work towards closing issue #105 * Add state computation fn's for configs It's much easier, clean and less verbose to compute `in_state`, `out_state` and `next_inputs` with an associated function for the MixingConfig sub-configs. And also makes the tests much less verbose. * Update StateBigint in compute_states signatures * Mixing: Add `MixingConfig` impl + tests lacking base conversion * mixing: Witness flag in state assignation * Rho: Derive `Debug` for all configs * xi: Apply copy_constraints for xi inputs It is critical for the correctness of the keccak circuit to apply copy constraints between the gates while executing the rounds. Works towards solving: #219 * Add OFFSET associated consts * Ignore failing Mixing tests * Clippy fixes * Replace pallas by field * Add zeroed_bytes assertion Co-authored-by: ying tong <yingtong@z.cash>
1 parent bb6c220 commit 1ce3f7f

File tree

15 files changed

+1637
-333
lines changed

15 files changed

+1637
-333
lines changed

keccak256/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,6 @@ num-bigint = "0.4.2"
1313
num-traits = "0.2.14"
1414
pairing = { git = 'https://github.com/appliedzkp/pairing', package = "pairing_bn256" }
1515
plotters = { version = "0.3.0", optional = true }
16+
17+
[dev-dependencies]
18+
pretty_assertions = "1.0"

keccak256/src/arith_helpers.rs

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1+
use crate::common::State;
12
use itertools::Itertools;
23
use num_bigint::BigUint;
34
use num_traits::{One, Zero};
5+
use pairing::arithmetic::FieldExt;
6+
use pairing::bn256::Fr as Fp;
47
use std::ops::{Index, IndexMut};
58

69
pub const B13: u64 = 13;
@@ -21,7 +24,7 @@ pub type Lane13 = BigUint;
2124
pub type Lane9 = BigUint;
2225

2326
pub struct StateBigInt {
24-
xy: Vec<BigUint>,
27+
pub(crate) xy: Vec<BigUint>,
2528
}
2629
impl Default for StateBigInt {
2730
fn default() -> Self {
@@ -33,6 +36,17 @@ impl Default for StateBigInt {
3336
}
3437
}
3538

39+
impl From<State> for StateBigInt {
40+
fn from(state: State) -> Self {
41+
let xy = state
42+
.iter()
43+
.flatten()
44+
.map(|num| BigUint::from(*num))
45+
.collect();
46+
Self { xy }
47+
}
48+
}
49+
3650
impl StateBigInt {
3751
pub fn from_state_big_int<F>(a: &StateBigInt, lane_transform: F) -> Self
3852
where
@@ -183,6 +197,21 @@ pub fn convert_b9_lane_to_b2_normal(x: Lane9) -> u64 {
183197
.unwrap_or(0)
184198
}
185199

200+
pub fn big_uint_to_field<F: FieldExt>(a: &BigUint) -> F {
201+
let mut b: [u64; 4] = [0; 4];
202+
let mut iter = a.iter_u64_digits();
203+
204+
for i in &mut b {
205+
*i = match &iter.next() {
206+
Some(x) => *x,
207+
None => 0u64,
208+
};
209+
}
210+
211+
// Workarround since `FieldExt` does not impl `from_raw`.
212+
F::from_bytes(&Fp::from_raw(b).to_bytes()).unwrap()
213+
}
214+
186215
/// This function allows us to inpect coefficients of big-numbers in different
187216
/// bases.
188217
pub fn inspect(x: BigUint, name: &str, base: u64) {
@@ -198,3 +227,58 @@ pub fn inspect(x: BigUint, name: &str, base: u64) {
198227
}
199228
println!("inspect {} {} info {:?}", name, x, info);
200229
}
230+
231+
pub fn state_to_biguint<F: FieldExt>(state: [F; 25]) -> StateBigInt {
232+
StateBigInt {
233+
xy: state
234+
.iter()
235+
.map(|elem| elem.to_bytes())
236+
.map(|bytes| BigUint::from_bytes_le(&bytes))
237+
.collect(),
238+
}
239+
}
240+
241+
pub fn state_to_state_bigint<F: FieldExt, const N: usize>(
242+
state: [F; N],
243+
) -> State {
244+
let mut matrix = [[0u64; 5]; 5];
245+
246+
let mut elems: Vec<u64> = state
247+
.iter()
248+
.map(|elem| elem.to_bytes())
249+
// This is horrible. But FieldExt does not give much better alternatives
250+
// and refactoring `State` will be done once the
251+
// keccak_all_togheter is done.
252+
.map(|bytes| {
253+
assert!(bytes[8..32] == vec![0u8; 24]);
254+
let mut arr = [0u8; 8];
255+
arr.copy_from_slice(&bytes[0..8]);
256+
u64::from_le_bytes(arr)
257+
})
258+
.collect();
259+
elems.extend(vec![0u64; 25 - N]);
260+
(0..5).into_iter().for_each(|idx| {
261+
matrix[idx].copy_from_slice(&elems[5 * idx..(5 * idx + 5)])
262+
});
263+
264+
matrix
265+
}
266+
267+
pub fn state_bigint_to_field<F: FieldExt, const N: usize>(
268+
state: StateBigInt,
269+
) -> [F; N] {
270+
let mut arr = [F::zero(); N];
271+
let vector: Vec<F> = state
272+
.xy
273+
.iter()
274+
.map(|elem| {
275+
let mut array = [0u8; 32];
276+
let bytes = elem.to_bytes_le();
277+
array[0..bytes.len()].copy_from_slice(&bytes[0..bytes.len()]);
278+
array
279+
})
280+
.map(|bytes| F::from_bytes(&bytes).unwrap())
281+
.collect();
282+
arr[0..N].copy_from_slice(&vector[0..N]);
283+
arr
284+
}

keccak256/src/circuit.rs

Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
use super::gates::{
2+
absorb::{AbsorbConfig, ABSORB_NEXT_INPUTS},
3+
iota_b13::IotaB13Config,
4+
iota_b9::IotaB9Config,
5+
pi::PiConfig,
6+
rho::RhoConfig,
7+
theta::ThetaConfig,
8+
xi::XiConfig,
9+
};
10+
use crate::{
11+
arith_helpers::*, common::{ROUND_CONSTANTS, PERMUTATION, ROTATION_CONSTANTS}, gates::rho_checks::RhoAdvices,
12+
};
13+
use crate::{gates::mixing::MixingConfig, keccak_arith::*};
14+
use halo2::{
15+
circuit::Region,
16+
plonk::{Advice, Column, ConstraintSystem, Error, Selector},
17+
};
18+
use itertools::Itertools;
19+
use num_bigint::BigUint;
20+
use pasta_curves::arithmetic::FieldExt;
21+
use std::{convert::TryInto, marker::PhantomData};
22+
23+
#[derive(Clone, Debug)]
24+
pub struct KeccakFConfig<F: FieldExt> {
25+
theta_config: ThetaConfig<F>,
26+
rho_config: RhoConfig<F>,
27+
pi_config: PiConfig<F>,
28+
xi_config: XiConfig<F>,
29+
iota_b9_config: IotaB9Config<F>,
30+
mixing_config: MixingConfig<F>,
31+
state: [Column<Advice>; 25],
32+
_marker: PhantomData<F>,
33+
}
34+
35+
impl<F: FieldExt> KeccakFConfig<F> {
36+
const B9_ROW: usize = 0;
37+
const B13_ROW: usize = 1;
38+
39+
// We assume state is recieved in base-9.
40+
pub fn configure(meta: &mut ConstraintSystem<F>) -> KeccakFConfig<F> {
41+
let state = (0..25)
42+
.map(|_| {
43+
let column = meta.advice_column();
44+
meta.enable_equality(column.into());
45+
column
46+
})
47+
.collect_vec()
48+
.try_into()
49+
.unwrap();
50+
51+
// theta
52+
let theta_config = ThetaConfig::configure(meta.selector(), meta, state);
53+
// rho
54+
let rho_config = {
55+
let cols: [Column<Advice>; 7] = state[0..7].try_into().unwrap();
56+
let adv = RhoAdvices::from(cols);
57+
let axiliary = [state[8], state[9]];
58+
59+
let base13_to_9 = [
60+
meta.fixed_column(),
61+
meta.fixed_column(),
62+
meta.fixed_column(),
63+
];
64+
let special = [meta.fixed_column(), meta.fixed_column()];
65+
RhoConfig::configure(
66+
meta,
67+
state,
68+
&adv,
69+
axiliary,
70+
base13_to_9,
71+
special,
72+
)
73+
};
74+
// Pi
75+
let pi_config = PiConfig::configure(meta.selector(), meta, state);
76+
// xi
77+
let xi_config = XiConfig::configure(meta.selector(), meta, state);
78+
79+
// Iotab9
80+
// Generate advice and instance column for Round constants in base9
81+
let round_ctant_b9 = meta.advice_column();
82+
let round_constants_b9 = meta.instance_column();
83+
let iota_b9_config = IotaB9Config::configure(
84+
meta,
85+
state,
86+
round_ctant_b9,
87+
round_constants_b9,
88+
);
89+
let not_mixing_b9_to_13 = StateConversion::configure(meta);
90+
91+
92+
93+
let mixing_config = MixingConfig::configure(meta, state);
94+
// in side mixing let b9_to_13 = StateConversion::configure(meta);
95+
96+
97+
KeccakFConfig {
98+
theta_config,
99+
rho_config,
100+
pi_config,
101+
xi_config,
102+
iota_b9_config,
103+
mixing_config,
104+
state,
105+
_marker: PhantomData,
106+
}
107+
}
108+
109+
pub fn assign_all(
110+
&self,
111+
region: &mut Region<'_, F>,
112+
mut offset: usize,
113+
state: [F; 25],
114+
out_state: [F;25],
115+
flag: bool,
116+
next_mixing: Option<[F; ABSORB_NEXT_INPUTS]>,
117+
absolute_row_b9: usize,
118+
absolute_row_b13: usize,
119+
) -> Result<[F; 25], Error> {
120+
// In case is needed
121+
let mut state = state;
122+
123+
// First 23 rounds
124+
for round in 0..PERMUTATION {
125+
// State in base-13
126+
// theta
127+
state = {
128+
// Apply theta outside circuit
129+
let out_state = KeccakFArith::theta(&state_to_biguint(state));
130+
let out_state = state_bigint_to_pallas(out_state);
131+
// assignment
132+
self.theta_config
133+
.assign_state(region, offset, state, out_state)?
134+
};
135+
136+
offset += ThetaConfig::OFFSET;
137+
138+
// rho
139+
state = {
140+
// Apply rho outside circuit
141+
let out_state = KeccakFArith::rho(&state_to_biguint(state));
142+
let out_state = state_bigint_to_pallas(out_state);
143+
// assignment
144+
self.rho_config
145+
.assign_region(region, offset, out_state)?;
146+
out_state
147+
};
148+
// Outputs in base-9 which is what Pi requires.
149+
offset += RhoConfig::OFFSET;
150+
151+
// pi
152+
state = {
153+
// Apply pi outside circuit
154+
let out_state = KeccakFArith::pi(&state_to_biguint(state));
155+
let out_state = state_bigint_to_pallas(out_state);
156+
// assignment
157+
self.pi_config
158+
.assign_state(region, offset, state, out_state)?
159+
};
160+
161+
offset += PiConfig::OFFSET;
162+
163+
// xi
164+
state = {
165+
// Apply xi outside circuit
166+
let out_state = KeccakFArith::xi(&state_to_biguint(state));
167+
let out_state = state_bigint_to_pallas(out_state);
168+
// assignment
169+
self.xi_config
170+
.assign_state(region, offset, state, out_state)?
171+
};
172+
173+
offset += XiConfig::OFFSET;
174+
175+
// iota_b9
176+
state = {
177+
let out_state = KeccakFArith::iota_b9(&state_to_biguint(state), ROUND_CONSTANTS[round]);
178+
let out_state = state_bigint_to_pallas(out_state);
179+
self
180+
.iota_b9_config
181+
.not_last_round(region, offset, state, out_state, round)?;
182+
out_state
183+
};
184+
offset += IotaB9Config::OFFSET;
185+
// The resulting state is in Base-13 now. Which is what Theta
186+
// requires again at the start of the loop.
187+
188+
self.not_mixing_b9_to_13.not_last_round();
189+
}
190+
191+
// Final round.
192+
let round = PERMUTATION;
193+
// PERMUTATION'th round
194+
// State in base-13
195+
// theta
196+
state = {
197+
// Apply theta outside circuit
198+
let out_state = KeccakFArith::theta(&state_to_biguint(state));
199+
let out_state = state_bigint_to_pallas(out_state);
200+
// assignment
201+
self.theta_config
202+
.assign_state(region, offset, state, out_state)?
203+
};
204+
205+
offset += 1;
206+
207+
// rho
208+
state = {
209+
// Apply rho outside circuit
210+
let out_state = KeccakFArith::rho(&state_to_biguint(state));
211+
let out_state = state_bigint_to_pallas(out_state);
212+
// assignment
213+
self.rho_config
214+
.assign_state(region, offset, state, out_state)?
215+
};
216+
// Outputs in base-9 which is what Pi requires.
217+
offset += 1;
218+
219+
// pi
220+
state = {
221+
// Apply pi outside circuit
222+
let out_state = KeccakFArith::pi(&state_to_biguint(state));
223+
let out_state = state_bigint_to_pallas(out_state);
224+
// assignment
225+
self.pi_config
226+
.assign_state(region, offset, state, out_state)?
227+
};
228+
229+
offset += PiConfig::OFFSET;
230+
231+
// xi
232+
state = {
233+
// Apply xi outside circuit
234+
let out_state = KeccakFArith::xi(&state_to_biguint(state));
235+
let out_state = state_bigint_to_pallas(out_state);
236+
// assignment
237+
self.xi_config
238+
.assign_state(region, offset, state, out_state)?
239+
};
240+
241+
offset += XiConfig::OFFSET;
242+
243+
// Mixing step
244+
state = {
245+
let out_state = KeccakFArith::mixing(&state_to_biguint(state), next_mixing, ROUND_CONSTANTS[round]);
246+
let out_state = state_bigint_to_pallas(out_state);
247+
self.mixing_config.assign_state(region, offset, state, out_state, flag, next_mixing, round, round)?;
248+
out_state
249+
};
250+
251+
Ok(state)
252+
}
253+
}

keccak256/src/gates.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
1+
#![allow(dead_code)]
2+
#![allow(clippy::type_complexity)]
3+
#![allow(clippy::too_many_arguments)]
14
pub mod absorb;
25
pub mod gate_helpers;
36
pub mod iota_b13;
47
pub mod iota_b9;
8+
pub mod mixing;
59
pub mod pi;
610
pub mod rho;
711
pub mod rho_checks;

0 commit comments

Comments
 (0)