Created
September 2, 2021 10:38
-
-
Save ChihChengLiang/b3f94f5b32c35679db1c154651ee8d68 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use halo2::{ | |
arithmetic::FieldExt, | |
circuit::{Chip, Layouter, SimpleFloorPlanner}, | |
pasta::Fp, | |
plonk::{Circuit, ConstraintSystem, Error}, | |
}; | |
use pasta_curves::pallas; | |
pub const KECCAK_NUM_ROUNDS: usize = 24; | |
pub struct KeccakConfig<F> { | |
// Each of these 25 lanes contains a 64-bit word. | |
// The first 17 lanes (1088 bits) are used to absorb inputs. | |
state: [Column<Advice>; 25], | |
theta_config: ThetaConfig<F>, | |
rho_config: RhoConfig<F>, | |
pi_config: PiConfig<F>, | |
xi_iota_config: XiIotaConfig<F>, | |
from_binary_config: [Column<Fixed>; 2] | |
} | |
impl<F: FieldExt> KeccakConfig<F> { | |
pub(crate) fn load( | |
config: Self, | |
layouter: &mut impl Layouter<F>, | |
) -> Result<(), Error> { | |
from_binary_converter(config, layouter); | |
} | |
pub(crate) fn configure( | |
meta: &mut ConstraintSystem<F>, | |
advices: [Column<Advice>; 25], | |
) -> Self { | |
let from_binary_config = [meta.fixed_column(), meta.fixed_column()]; | |
for lane in 0..25 { | |
// Lookup for binary to base-13 conversion | |
meta.lookup(|meta| { | |
let word = advices[lane]; | |
let binary_word = meta.query_advice(word, Rotation::cur()); | |
let base13_word = meta.query_advice(word, Rotation::next()); | |
let key = meta.query_fixed(from_binary_config[0], Rotation::cur()); | |
let value = meta.query_fixed(from_binary_config[1], Rotation::cur()); | |
vec![ | |
(binary_word, key), | |
(base13_word, value), | |
] | |
}); | |
} | |
} | |
// Fixed table converting binary to base-13 | |
fn from_binary_converter( | |
config: Self, | |
layouter: &mut impl Layouter<F>, | |
) -> Result<(), Error> { | |
let [config] = config.from_binary_config; | |
layouter.assign_region(|| "from binary", |mut region| { | |
// Iterate over all possible 16-bit values | |
for (i, coefs) in (0..16).map(|_| 0..1).multi_cartesian_product().enumerate() { | |
let key = coefs.iter().fold(zero_fr.clone(), |acc, x| { | |
let mut tmp = acc; | |
tmp.mul_assign(&base_b_fr); | |
tmp.add_assign(&F::from_u64(*x)); | |
tmp | |
}); | |
region.assign_fixed( | |
|| "key", | |
config[0], | |
i, | |
|| Ok(key) | |
)?; | |
let value = coefs.iter().fold(zero_fr.clone(), |acc, x| { | |
let mut tmp = acc; | |
tmp.mul_assign(&base_c_fr); | |
tmp.add_assign(&u64_to_ff(transform_f(*x))); | |
tmp | |
}); | |
region.assign_fixed( | |
|| "value", | |
config[1], | |
i, | |
|| Ok(value) | |
)?; | |
} | |
Ok(()) | |
}) | |
} | |
} | |
let table_size = pow(base_b as usize, num_chunks); | |
let mut keys_vec = Vec::with_capacity(table_size); | |
let mut chunk_count_vec = Vec::with_capacity(table_size); | |
let mut values_vec = Vec::with_capacity(table_size); | |
let mut map = std::collections::HashMap::with_capacity(table_size); | |
let base_b_fr = u64_to_ff::<E::Fr>(base_b); | |
let base_c_fr = u64_to_ff::<E::Fr>(base_c); | |
let zero_fr = E::Fr::zero(); | |
for coefs in (0..num_chunks).map(|_| 0..base_b).multi_cartesian_product() { | |
let key = coefs.iter().fold(zero_fr.clone(), |acc, x| { | |
let mut tmp = acc; | |
tmp.mul_assign(&base_b_fr); | |
tmp.add_assign(&u64_to_ff(*x)); | |
tmp | |
}); | |
let value = coefs.iter().fold(zero_fr.clone(), |acc, x| { | |
let mut tmp = acc; | |
tmp.mul_assign(&base_c_fr); | |
tmp.add_assign(&u64_to_ff(transform_f(*x))); | |
tmp | |
}); | |
let chunk_count = (num_chunks - coefs.iter().take_while(|x| **x == 0).count()) as u64; | |
let chunk_count_fr = u64_to_ff(transform_counter(chunk_count)); | |
keys_vec.push(key); | |
chunk_count_vec.push(chunk_count_fr); | |
values_vec.push(value); | |
map.insert(key, (chunk_count_fr, value)); | |
} | |
/* | |
class FromBinaryConverterTable: | |
def __init__(self): | |
axies = [[0, 1] for i in range(16)] | |
for coefs in itertools.product(*axies): | |
assert len(coefs) == 16 | |
# key is the binary evaluation of coefs | |
key = sum([coef*(2**i) for i, coef in enumerate(coefs)]) | |
value0 = sum([coef*(13**i) for i, coef in enumerate(coefs)]) | |
value1 = sum([coef*(9**i) for i, coef in enumerate(coefs)]) | |
self.add_row(key, value0, value1) | |
*/ | |
/* | |
25 advice columns for the state | |
fixed columns for XOR lookup | |
tables: | |
- binary to 13 conversion | |
- 13 to 9 conversion | |
- special chunk conversion | |
- 9 to binary and 9 to 13 conversion | |
*/ | |
// sponge | |
// absorb 1088 bits | |
// state 1600 bits | |
// 25 lanes of 64 bits word | |
// keccak-f(25 lanes of 64-bit words) | |
// keccak-f | |
// state[x][y] = is a 64 bit word | |
// - x goes from 0..=4 | |
// - y goes from 0..=4 | |
// convert word from binary to 13 base | |
// state = theta(state) | |
// enter with 13 base | |
// state = rho(state, rot[x][y]) | |
// exit with 9 base | |
// state = pi(state) | |
// enter with 9 base | |
// xi_and_iota() | |
// conversion() | |
// exit with 13 base (if continuing absorb phase) (taking new inputs) | |
// exit with binary (if leaving absorb phase) | |
/* | |
state_array is initialised as ? | |
// sponge | |
// absorb arbitrary-length input to | |
1088 bit at a time | |
convert from 1600 bits state_array state 5*5 lanes of * 64 bits | |
state = keccak-f(state) | |
convert back to state_array | |
absorb next 1088 bit and xor 136 bytes | |
state[i] ^= input_bytes | |
// squezee | |
state_array[:256] | |
*/ | |
keccak(abi.encode("adjllsakjd", 13123, )) | |
30 gas + 6 gas * for every extra 256 bits | |
pub struct KeccakFChip<F: FieldExt> { | |
config: KeccakConfig<F>, | |
} | |
impl<F: FieldExt> KeccakFChip<F> { | |
pub fn configure( | |
meta: &mut ConstraintSystem<pallas::Base>, | |
) -> KeccakConfig<F> { | |
state = self.theta(cs, state)? ; | |
state = self.rho(cs, state)?; | |
state = self.pi(cs, state)?; | |
state = new_state; | |
} | |
state = self.theta(cs, state)?; | |
state = self.rho(cs, state)?; | |
state = self.pi(cs, state)?; | |
let (mut new_state, out) = self.xi_i(cs, state, KECCAK_NUM_ROUNDS-1, elems_to_squeeze, elems_to_mix, is_final)?; | |
if elems_to_mix.is_some() { | |
, | |
new_state[(0, 0)] = new_state[(0, 0)].add(cs, &Num::Constant(self.round_cnsts_in_first_base[KECCAK_NUM_ROUNDS-1]))?; | |
} | |
, | |
, | |
let squeezed = if elems_to_squeeze > 0 { Some(out) } else { None }; | |
Ok((new_state, squeezed)) | |
} | |
pub fn construct(config: KeccakConfig<F>) -> Self { | |
KeccakFChip { config } | |
} | |
pub fn theta(&self,){} | |
pub fn rho(&self) {} | |
pub fn rho(&self,){} | |
pi | |
pub fn pi(&self,){} | |
x | |
impl<F: FieldExt> Chip<F> for KeccakFChip<F> { | |
type Config = KeccakConfig<F>; | |
type Loaded = (); | |
fn config(&self) -> &Self::Config { | |
&self.config | |
} | |
fn loaded(&self) -> &Self::Loaded { | |
&() | |
} | |
} | |
#[derive(Default)] | |
struct HashCircuit { | |
message: Option<[Fp; 2]>, | |
output: Option<Fp>, | |
} | |
impl Circuit<Fp> for HashCircuit { | |
type Config = KeccakConfig<Fp>; | |
type FloorPlanner = SimpleFloorPlanner; | |
fn without_witnesses(&self) -> Self { | |
Self::default() | |
} | |
fn configure(meta: &mut ConstraintSystem<Fp>) -> KeccakConfig<Fp> {} | |
fn synthesize( | |
&self, | |
config: KeccakConfig<Fp>, | |
mut layouter: impl Layouter<Fp>, | |
) -> Result<(), Error> { | |
let chip = KeccakChip::construct(config.clone()); | |
} | |
} | |
#[test] | |
fn keccak_hash() { | |
use tiny_keccak::{Hasher, Keccak}; | |
let mut keccak = Keccak::v256(); | |
let mut output = [0u8; 32]; | |
keccak.update(b"foo"); | |
keccak.update(b"bar"); | |
keccak.finalize(&mut output); | |
let expected = b"\x38\xd1\x8a\xcbg\xd2\\\x8b\xb9\x94'd\xb6/\x18\xe1pT\xf6j\x81{\xd4)T#\xad\xf9\xed\x98\x87>"; | |
assert_eq!(expected, &output); | |
// let message = [Fp::rand(), Fp::rand()]; | |
// let output = poseidon::Hash::init(OrchardNullifier, ConstantLength::<2>) | |
// .hash(message); | |
// let k = 6; | |
// let circuit = HashCircuit { | |
// message: Some(message), | |
// output: Some(output), | |
// }; | |
// let prover = MockProver::run(k, &circuit, vec![]).unwrap(); | |
// assert_eq!(prover.verify(), Ok(())) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment