Skip to content

Commit

Permalink
Merge branch 'dynamic_array' into dev
Browse files Browse the repository at this point in the history
  • Loading branch information
siq1 committed Oct 12, 2024
2 parents 8662d11 + c622281 commit 488d5c2
Show file tree
Hide file tree
Showing 2 changed files with 309 additions and 0 deletions.
22 changes: 22 additions & 0 deletions expander_compiler/src/frontend/circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ macro_rules! declare_circuit_field_type {
[$crate::frontend::internal::declare_circuit_field_type!(@type $elem); $n]
};

(@type [$elem:tt]) => {
Vec<$crate::frontend::internal::declare_circuit_field_type!(@type $elem)>
};

(@type $other:ty) => {
$other
};
Expand All @@ -33,6 +37,12 @@ macro_rules! declare_circuit_dump_into {
}
};

($field_value:expr, @type [$elem:tt], $vars:expr, $public_vars:expr) => {
for _x in $field_value.iter() {
$crate::frontend::internal::declare_circuit_dump_into!(_x, @type $elem, $vars, $public_vars);
}
};

($field_value:expr, @type $other:ty, $vars:expr, $public_vars:expr) => {
};
}
Expand All @@ -53,6 +63,12 @@ macro_rules! declare_circuit_load_from {
}
};

($field_value:expr, @type [$elem:tt], $vars:expr, $public_vars:expr) => {
for _x in $field_value.iter_mut() {
$crate::frontend::internal::declare_circuit_load_from!(_x, @type $elem, $vars, $public_vars);
}
};

($field_value:expr, @type $other:ty, $vars:expr, $public_vars:expr) => {
};
}
Expand All @@ -71,6 +87,12 @@ macro_rules! declare_circuit_num_vars {
$crate::frontend::internal::declare_circuit_num_vars!($field_value[0], @type $elem, $cnt_sec, $cnt_pub, $array_cnt * $n);
};

($field_value:expr, @type [$elem:tt], $cnt_sec:expr, $cnt_pub:expr, $array_cnt:expr) => {
for _x in $field_value.iter() {
$crate::frontend::internal::declare_circuit_num_vars!(_x, @type $elem, $cnt_sec, $cnt_pub, $array_cnt);
}
};

($field_value:expr, @type $other:ty, $cnt_sec:expr, $cnt_pub:expr, $array_cnt:expr) => {
};
}
Expand Down
287 changes: 287 additions & 0 deletions expander_compiler/tests/keccak_gf2_vec.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,287 @@
use expander_compiler::frontend::*;
use rand::{thread_rng, Rng};
use tiny_keccak::Hasher;

const N_HASHES: usize = 4;

fn rc() -> Vec<u64> {
vec![
0x0000000000000001,
0x0000000000008082,
0x800000000000808A,
0x8000000080008000,
0x000000000000808B,
0x0000000080000001,
0x8000000080008081,
0x8000000000008009,
0x000000000000008A,
0x0000000000000088,
0x0000000080008009,
0x000000008000000A,
0x000000008000808B,
0x800000000000008B,
0x8000000000008089,
0x8000000000008003,
0x8000000000008002,
0x8000000000000080,
0x000000000000800A,
0x800000008000000A,
0x8000000080008081,
0x8000000000008080,
0x0000000080000001,
0x8000000080008008,
]
}

fn xor_in<C: Config>(
api: &mut API<C>,
mut s: Vec<Vec<Variable>>,
buf: Vec<Vec<Variable>>,
) -> Vec<Vec<Variable>> {
for y in 0..5 {
for x in 0..5 {
if x + 5 * y < buf.len() {
s[5 * x + y] = xor(api, s[5 * x + y].clone(), buf[x + 5 * y].clone())
}
}
}
s
}

fn keccak_f<C: Config>(api: &mut API<C>, mut a: Vec<Vec<Variable>>) -> Vec<Vec<Variable>> {
let mut b = vec![vec![api.constant(0); 64]; 25];
let mut c = vec![vec![api.constant(0); 64]; 5];
let mut d = vec![vec![api.constant(0); 64]; 5];
let mut da = vec![vec![api.constant(0); 64]; 5];
let rc = rc();

for i in 0..24 {
for j in 0..5 {
let t1 = xor(api, a[j * 5 + 1].clone(), a[j * 5 + 2].clone());
let t2 = xor(api, a[j * 5 + 3].clone(), a[j * 5 + 4].clone());
c[j] = xor(api, t1, t2);
}

for j in 0..5 {
d[j] = xor(
api,
c[(j + 4) % 5].clone(),
rotate_left::<C>(&c[(j + 1) % 5], 1),
);
da[j] = xor(
api,
a[((j + 4) % 5) * 5].clone(),
rotate_left::<C>(&a[((j + 1) % 5) * 5], 1),
);
}

for j in 0..25 {
let tmp = xor(api, da[j / 5].clone(), a[j].clone());
a[j] = xor(api, tmp, d[j / 5].clone());
}

/*Rho and pi steps*/
b[0] = a[0].clone();

b[8] = rotate_left::<C>(&a[1], 36);
b[11] = rotate_left::<C>(&a[2], 3);
b[19] = rotate_left::<C>(&a[3], 41);
b[22] = rotate_left::<C>(&a[4], 18);

b[2] = rotate_left::<C>(&a[5], 1);
b[5] = rotate_left::<C>(&a[6], 44);
b[13] = rotate_left::<C>(&a[7], 10);
b[16] = rotate_left::<C>(&a[8], 45);
b[24] = rotate_left::<C>(&a[9], 2);

b[4] = rotate_left::<C>(&a[10], 62);
b[7] = rotate_left::<C>(&a[11], 6);
b[10] = rotate_left::<C>(&a[12], 43);
b[18] = rotate_left::<C>(&a[13], 15);
b[21] = rotate_left::<C>(&a[14], 61);

b[1] = rotate_left::<C>(&a[15], 28);
b[9] = rotate_left::<C>(&a[16], 55);
b[12] = rotate_left::<C>(&a[17], 25);
b[15] = rotate_left::<C>(&a[18], 21);
b[23] = rotate_left::<C>(&a[19], 56);

b[3] = rotate_left::<C>(&a[20], 27);
b[6] = rotate_left::<C>(&a[21], 20);
b[14] = rotate_left::<C>(&a[22], 39);
b[17] = rotate_left::<C>(&a[23], 8);
b[20] = rotate_left::<C>(&a[24], 14);

/*Xi state*/

for j in 0..25 {
let t = not(api, b[(j + 5) % 25].clone());
let t = and(api, t, b[(j + 10) % 25].clone());
a[j] = xor(api, b[j].clone(), t);
}

/*Last step*/

for j in 0..64 {
if rc[i] >> j & 1 == 1 {
a[0][j] = api.sub(1, a[0][j]);
}
}
}

a
}

fn xor<C: Config>(api: &mut API<C>, a: Vec<Variable>, b: Vec<Variable>) -> Vec<Variable> {
let nbits = a.len();
let mut bits_res = vec![api.constant(0); nbits];
for i in 0..nbits {
bits_res[i] = api.add(a[i].clone(), b[i].clone());
}
bits_res
}

fn and<C: Config>(api: &mut API<C>, a: Vec<Variable>, b: Vec<Variable>) -> Vec<Variable> {
let nbits = a.len();
let mut bits_res = vec![api.constant(0); nbits];
for i in 0..nbits {
bits_res[i] = api.mul(a[i].clone(), b[i].clone());
}
bits_res
}

fn not<C: Config>(api: &mut API<C>, a: Vec<Variable>) -> Vec<Variable> {
let mut bits_res = vec![api.constant(0); a.len()];
for i in 0..a.len() {
bits_res[i] = api.sub(1, a[i].clone());
}
bits_res
}

fn rotate_left<C: Config>(bits: &Vec<Variable>, k: usize) -> Vec<Variable> {
let n = bits.len();
let s = k & (n - 1);
let mut new_bits = bits[(n - s) as usize..].to_vec();
new_bits.append(&mut bits[0..(n - s) as usize].to_vec());
new_bits
}

fn copy_out_unaligned(s: Vec<Vec<Variable>>, rate: usize, output_len: usize) -> Vec<Variable> {
let mut out = vec![];
let w = 8;
let mut b = 0;
while b < output_len {
for y in 0..5 {
for x in 0..5 {
if x + 5 * y < rate / w && b < output_len {
out.append(&mut s[5 * x + y].clone());
b += 8;
}
}
}
}
out
}

declare_circuit!(Keccak256Circuit {
p: [[Variable]],
out: [[PublicVariable]],
});

fn compute_keccak<C: Config>(api: &mut API<C>, p: &Vec<Variable>) -> Vec<Variable> {
let mut ss = vec![vec![api.constant(0); 64]; 25];
let mut new_p = p.clone();
let mut append_data = vec![0; 136 - 64];
append_data[0] = 1;
append_data[135 - 64] = 0x80;
for i in 0..136 - 64 {
for j in 0..8 {
new_p.push(api.constant(((append_data[i] >> j) & 1) as u32));
}
}
let mut p = vec![vec![api.constant(0); 64]; 17];
for i in 0..17 {
for j in 0..64 {
p[i][j] = new_p[i * 64 + j].clone();
}
}
ss = xor_in(api, ss, p);
ss = keccak_f(api, ss);
copy_out_unaligned(ss, 136, 32)
}

impl Define<GF2Config> for Keccak256Circuit<Variable> {
fn define(&self, api: &mut API<GF2Config>) {
for i in 0..N_HASHES {
let out = api.memorized_simple_call(compute_keccak, &self.p[i].to_vec());
for j in 0..256 {
api.assert_is_equal(out[j].clone(), self.out[i][j].clone());
}
}
}
}

#[test]
fn keccak_gf2_vec() {
let mut circuit = Keccak256Circuit::<Variable>::default();
circuit.p = vec![vec![Variable::default(); 64 * 8]; N_HASHES];
circuit.out = vec![vec![Variable::default(); 32 * 8]; N_HASHES];

let compile_result = compile(&circuit).unwrap();
let CompileResult {
witness_solver,
layered_circuit,
} = compile_result;

let mut assignment = Keccak256Circuit::<GF2>::default();
assignment.p = vec![vec![GF2::from(0); 64 * 8]; N_HASHES];
assignment.out = vec![vec![GF2::from(0); 32 * 8]; N_HASHES];
for k in 0..N_HASHES {
let mut data = vec![0u8; 64];
for i in 0..64 {
data[i] = thread_rng().gen();
}
let mut hash = tiny_keccak::Keccak::v256();
hash.update(&data);
let mut output = [0u8; 32];
hash.finalize(&mut output);
for i in 0..64 {
for j in 0..8 {
assignment.p[k][i * 8 + j] = ((data[i] >> j) as u32 & 1).into();
}
}
for i in 0..32 {
for j in 0..8 {
assignment.out[k][i * 8 + j] = ((output[i] >> j) as u32 & 1).into();
}
}
}
let witness = witness_solver.solve_witness(&assignment).unwrap();
let res = layered_circuit.run(&witness);
assert_eq!(res, vec![true]);
println!("test 1 passed");

for k in 0..N_HASHES {
assignment.p[k][0] = assignment.p[k][0] - GF2::from(1);
}
let witness = witness_solver.solve_witness(&assignment).unwrap();
let res = layered_circuit.run(&witness);
assert_eq!(res, vec![false]);
println!("test 2 passed");

let mut assignments = Vec::new();
for _ in 0..16 {
for k in 0..N_HASHES {
assignment.p[k][0] = assignment.p[k][0] - GF2::from(1);
}
assignments.push(assignment.clone());
}
let witness = witness_solver.solve_witnesses(&assignments).unwrap();
let res = layered_circuit.run(&witness);
let mut expected_res = vec![false; 16];
for i in 0..8 {
expected_res[i * 2] = true;
}
assert_eq!(res, expected_res);
println!("test 3 passed");
}

0 comments on commit 488d5c2

Please sign in to comment.