Skip to content

Instantly share code, notes, and snippets.

@ChihChengLiang
Created September 2, 2021 10:38
Show Gist options
  • Save ChihChengLiang/b3f94f5b32c35679db1c154651ee8d68 to your computer and use it in GitHub Desktop.
Save ChihChengLiang/b3f94f5b32c35679db1c154651ee8d68 to your computer and use it in GitHub Desktop.
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