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

Commit 1954c31

Browse files
[keccak] Theta gate circuit (#97)
* add theta gate * move to arith helpers * remove allow deadcode * nitpicks
1 parent 9d02fde commit 1954c31

File tree

5 files changed

+224
-1
lines changed

5 files changed

+224
-1
lines changed

keccak256/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@ halo2 = "0.0"
88
itertools = "0.10.1"
99
num-bigint = "0.4.2"
1010
num-traits = "0.2.14"
11+
pasta_curves = "0.1"

keccak256/src/gates.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pub mod theta;

keccak256/src/gates/theta.rs

Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
use crate::arith_helpers::*;
2+
use halo2::{
3+
circuit::{Layouter, Region},
4+
plonk::{Advice, Column, ConstraintSystem, Error, Expression, Selector},
5+
poly::Rotation,
6+
};
7+
use itertools::Itertools;
8+
use pasta_curves::arithmetic::FieldExt;
9+
use std::marker::PhantomData;
10+
11+
#[derive(Clone, Debug)]
12+
pub struct ThetaConfig<F> {
13+
q_enable: Selector,
14+
state: [Column<Advice>; 25],
15+
_marker: PhantomData<F>,
16+
}
17+
18+
impl<F: FieldExt> ThetaConfig<F> {
19+
pub fn configure(
20+
q_enable: Selector,
21+
meta: &mut ConstraintSystem<F>,
22+
state: [Column<Advice>; 25],
23+
) -> ThetaConfig<F> {
24+
meta.create_gate("theta", |meta| {
25+
let q_enable = meta.query_selector(q_enable);
26+
let column_sum: Vec<Expression<F>> = (0..5)
27+
.map(|x| {
28+
let state_x0 =
29+
meta.query_advice(state[5 * x], Rotation::cur());
30+
let state_x1 =
31+
meta.query_advice(state[5 * x + 1], Rotation::cur());
32+
let state_x2 =
33+
meta.query_advice(state[5 * x + 2], Rotation::cur());
34+
let state_x3 =
35+
meta.query_advice(state[5 * x + 3], Rotation::cur());
36+
let state_x4 =
37+
meta.query_advice(state[5 * x + 4], Rotation::cur());
38+
state_x0 + state_x1 + state_x2 + state_x3 + state_x4
39+
})
40+
.collect::<Vec<_>>();
41+
42+
(0..5)
43+
.cartesian_product(0..5)
44+
.map(|(x, y)| {
45+
let new_state =
46+
meta.query_advice(state[5 * x + y], Rotation::next());
47+
let old_state =
48+
meta.query_advice(state[5 * x + y], Rotation::cur());
49+
let right = old_state
50+
+ column_sum[(x + 4) % 5].clone()
51+
+ Expression::Constant(F::from(B13))
52+
* column_sum[(x + 1) % 5].clone();
53+
q_enable.clone() * (new_state - right)
54+
})
55+
.collect::<Vec<_>>()
56+
});
57+
58+
ThetaConfig {
59+
q_enable,
60+
state,
61+
_marker: PhantomData,
62+
}
63+
}
64+
pub fn load(&self, _layouter: &mut impl Layouter<F>) -> Result<(), Error> {
65+
Ok(())
66+
}
67+
68+
pub fn assign_state(
69+
&self,
70+
region: &mut Region<'_, F>,
71+
offset: usize,
72+
state: [F; 25],
73+
) -> Result<[F; 25], Error> {
74+
for (idx, lane) in state.iter().enumerate() {
75+
region.assign_advice(
76+
|| format!("assign state {}", idx),
77+
self.state[idx],
78+
offset,
79+
|| Ok(*lane),
80+
)?;
81+
}
82+
Ok(state)
83+
}
84+
}
85+
86+
#[cfg(test)]
87+
mod tests {
88+
use super::*;
89+
use crate::common::*;
90+
use crate::keccak_arith::*;
91+
use halo2::{
92+
circuit::{Layouter, SimpleFloorPlanner},
93+
dev::{MockProver, VerifyFailure},
94+
plonk::{Advice, Circuit, Column, ConstraintSystem, Error},
95+
};
96+
use itertools::Itertools;
97+
use num_bigint::BigUint;
98+
use pasta_curves::{arithmetic::FieldExt, pallas};
99+
use std::convert::TryInto;
100+
use std::marker::PhantomData;
101+
102+
#[test]
103+
fn test_theta_gates() {
104+
#[derive(Default)]
105+
struct MyCircuit<F> {
106+
in_state: [F; 25],
107+
out_state: [F; 25],
108+
_marker: PhantomData<F>,
109+
}
110+
impl<F: FieldExt> Circuit<F> for MyCircuit<F> {
111+
type Config = ThetaConfig<F>;
112+
type FloorPlanner = SimpleFloorPlanner;
113+
114+
fn without_witnesses(&self) -> Self {
115+
Self::default()
116+
}
117+
118+
fn configure(meta: &mut ConstraintSystem<F>) -> Self::Config {
119+
let q_enable = meta.selector();
120+
121+
let state: [Column<Advice>; 25] = (0..25)
122+
.map(|_| meta.advice_column())
123+
.collect::<Vec<_>>()
124+
.try_into()
125+
.unwrap();
126+
127+
ThetaConfig::configure(q_enable, meta, state)
128+
}
129+
130+
fn synthesize(
131+
&self,
132+
config: Self::Config,
133+
mut layouter: impl Layouter<F>,
134+
) -> Result<(), Error> {
135+
config.load(&mut layouter)?;
136+
137+
layouter.assign_region(
138+
|| "assign input state",
139+
|mut region| {
140+
let offset = 0;
141+
config.q_enable.enable(&mut region, offset)?;
142+
config.assign_state(
143+
&mut region,
144+
offset,
145+
self.in_state,
146+
)?;
147+
let offset = 1;
148+
config.assign_state(&mut region, offset, self.out_state)
149+
},
150+
)?;
151+
152+
Ok(())
153+
}
154+
}
155+
fn big_uint_to_pallas(a: &BigUint) -> pallas::Base {
156+
let mut b: [u64; 4] = [0; 4];
157+
let mut iter = a.iter_u64_digits();
158+
159+
for i in &mut b {
160+
*i = match &iter.next() {
161+
Some(x) => *x,
162+
None => 0u64,
163+
};
164+
}
165+
166+
pallas::Base::from_raw(b)
167+
}
168+
169+
let input1: State = [
170+
[1, 0, 0, 0, 0],
171+
[0, 0, 0, 9223372036854775808, 0],
172+
[0, 0, 0, 0, 0],
173+
[0, 0, 0, 0, 0],
174+
[0, 0, 0, 0, 0],
175+
];
176+
let mut in_biguint = StateBigInt::default();
177+
let mut in_state: [pallas::Base; 25] = [pallas::Base::zero(); 25];
178+
179+
for (x, y) in (0..5).cartesian_product(0..5) {
180+
in_biguint[(x, y)] = convert_b2_to_b13(input1[x][y]);
181+
in_state[5 * x + y] = big_uint_to_pallas(&in_biguint[(x, y)]);
182+
}
183+
let s1_arith = KeccakFArith::theta(&in_biguint);
184+
let mut out_state: [pallas::Base; 25] = [pallas::Base::zero(); 25];
185+
for (x, y) in (0..5).cartesian_product(0..5) {
186+
out_state[5 * x + y] = big_uint_to_pallas(&s1_arith[(x, y)]);
187+
}
188+
189+
let circuit = MyCircuit::<pallas::Base> {
190+
in_state,
191+
out_state,
192+
_marker: PhantomData,
193+
};
194+
195+
// Test without public inputs
196+
let prover =
197+
MockProver::<pallas::Base>::run(9, &circuit, vec![]).unwrap();
198+
199+
assert_eq!(prover.verify(), Ok(()));
200+
201+
let mut out_state2 = out_state;
202+
out_state2[0] = pallas::Base::from(5566u64);
203+
204+
let circuit2 = MyCircuit::<pallas::Base> {
205+
in_state,
206+
out_state: out_state2,
207+
_marker: PhantomData,
208+
};
209+
210+
let prover =
211+
MockProver::<pallas::Base>::run(9, &circuit2, vec![]).unwrap();
212+
assert_eq!(
213+
prover.verify(),
214+
Err(vec![VerifyFailure::ConstraintNotSatisfied {
215+
constraint: ((0, "theta").into(), 0, "").into(),
216+
row: 0
217+
}])
218+
);
219+
}
220+
}

keccak256/src/keccak_arith.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ impl KeccakFArith {
3333
}
3434
}
3535

36-
fn theta(a: &StateBigInt) -> StateBigInt {
36+
pub fn theta(a: &StateBigInt) -> StateBigInt {
3737
let mut c: Vec<Lane13> = Vec::with_capacity(5);
3838
let mut out = StateBigInt::default();
3939

keccak256/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
pub mod arith_helpers;
22
pub mod common;
3+
pub mod gates;
34
// We build arith module to get test cases for the circuit
45
pub mod keccak_arith;
56
// We build plain module for the purpose of reviewing the circuit

0 commit comments

Comments
 (0)