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

Keccak Challenge-API integration #925

Merged
merged 13 commits into from
Nov 30, 2022
Merged
9 changes: 4 additions & 5 deletions circuit-benchmarks/src/bit_keccak.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,11 @@ mod tests {
.parse()
.expect("Cannot parse DEGREE env var as u32");

// Create the circuit. Leave last dozens of rows for blinding.
let mut circuit = KeccakBitCircuit::new(2usize.pow(degree) - 64);

// Use the complete circuit
let inputs = vec![(0u8..135).collect::<Vec<_>>(); circuit.capacity()];
circuit.generate_witness(&inputs);
let inputs = vec![(0u8..135).collect::<Vec<_>>(); 3];

// Create the circuit. Leave last dozens of rows for blinding.
let circuit = KeccakBitCircuit::new(2usize.pow(degree) - 64, inputs.to_vec());

// Initialize the polynomial commitment parameters
let mut rng = XorShiftRng::from_seed([
Expand Down
9 changes: 4 additions & 5 deletions circuit-benchmarks/src/packed_keccak.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,11 @@ mod tests {
.parse()
.expect("Cannot parse DEGREE env var as u32");

// Create the circuit. Leave last dozens of rows for blinding.
let mut circuit = KeccakPackedCircuit::new(2usize.pow(degree) - 64);

// Use the complete circuit
let inputs = vec![(0u8..135).collect::<Vec<_>>(); circuit.capacity()];
circuit.generate_witness(&inputs);
let inputs = vec![(0u8..135).collect::<Vec<_>>(); 3];

// Create the circuit. Leave last dozens of rows for blinding.
let circuit = KeccakPackedCircuit::new(2usize.pow(degree) - 64, inputs.as_slice());

// Initialize the polynomial commitment parameters
let mut rng = XorShiftRng::from_seed([
Expand Down
9 changes: 4 additions & 5 deletions circuit-benchmarks/src/packed_multi_keccak.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,11 @@ mod tests {
.parse()
.expect("Cannot parse DEGREE env var as u32");

// Create the circuit. Leave last dozens of rows for blinding.
let mut circuit = KeccakPackedCircuit::new(2usize.pow(degree) - 64);

// Use the complete circuit
let inputs = vec![(0u8..135).collect::<Vec<_>>(); circuit.capacity()];
circuit.generate_witness(&inputs);
let inputs = vec![(0u8..135).collect::<Vec<_>>(); 3];

// Create the circuit. Leave last dozens of rows for blinding.
let circuit = KeccakPackedCircuit::new(2usize.pow(degree) - 64, inputs.to_vec());

// Initialize the polynomial commitment parameters
let mut rng = XorShiftRng::from_seed([
Expand Down
1 change: 0 additions & 1 deletion zkevm-circuits/src/evm_circuit/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,6 @@ pub(crate) struct RandomLinearCombination<F, const N: usize> {
impl<F: FieldExt, const N: usize> RandomLinearCombination<F, N> {
const N_BYTES: usize = N;

// TODO: replace `bytes` type by a reference
pub(crate) fn random_linear_combine(bytes: [u8; N], randomness: F) -> F {
rlc::value(&bytes, randomness)
}
Expand Down
102 changes: 53 additions & 49 deletions zkevm-circuits/src/keccak_circuit/keccak_bit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ use crate::{
NUM_WORDS_TO_ABSORB, RATE, RATE_IN_BITS, RHO_MATRIX,
},
table::KeccakTable,
util::Expr,
util::{Challenges, Expr},
};
use eth_types::Field;
use gadgets::util::{and, select, sum, xor};
use halo2_proofs::{
circuit::{Layouter, Region, SimpleFloorPlanner, Value},
plonk::{
Advice, Circuit, Column, ConstraintSystem, Error, Expression, Fixed, TableColumn,
VirtualCells,
Advice, Challenge, Circuit, Column, ConstraintSystem, Error, Expression, Fixed,
SecondPhase, TableColumn, VirtualCells,
},
poly::Rotation,
};
Expand All @@ -44,19 +44,19 @@ fn get_num_bits_per_theta_lookup() -> usize {
num_bits as usize
}

#[derive(Clone, Debug, PartialEq)]
struct KeccakRow<F> {
#[derive(Clone, Debug)]
pub(crate) struct KeccakRow<F> {
q_padding: bool,
q_padding_last: bool,
state: [u8; KECCAK_WIDTH_IN_BITS],
theta_c: [u8; THETA_C_WIDTH],
input: [u8; ABSORB_WIDTH_PER_ROW],
is_paddings: [bool; ABSORB_WIDTH_PER_ROW_BYTES],
data_rlcs: [F; ABSORB_WIDTH_PER_ROW_BYTES],
data_rlcs: [Value<F>; ABSORB_WIDTH_PER_ROW_BYTES],
is_final: bool,
length: usize,
data_rlc: F,
hash_rlc: F,
data_rlc: Value<F>,
hash_rlc: Value<F>,
}

/// KeccakBitConfig
Expand All @@ -83,45 +83,45 @@ pub struct KeccakBitConfig<F> {
/// KeccakBitCircuit
#[derive(Default)]
pub struct KeccakBitCircuit<F: Field> {
witness: Vec<KeccakRow<F>>,
witness_inputs: Vec<Vec<u8>>,
num_rows: usize,
_marker: PhantomData<F>,
}

impl<F: Field> KeccakBitCircuit<F> {
fn r() -> F {
F::from(123456)
}
}

impl<F: Field> Circuit<F> for KeccakBitCircuit<F> {
type Config = KeccakBitConfig<F>;
type Config = (KeccakBitConfig<F>, Challenges<Challenge>);
type FloorPlanner = SimpleFloorPlanner;

fn without_witnesses(&self) -> Self {
Self::default()
}

fn configure(meta: &mut ConstraintSystem<F>) -> Self::Config {
KeccakBitConfig::configure(meta, Expression::Constant(KeccakBitCircuit::r()))
let challenges = Challenges::construct(meta);
let challenge_exprs = challenges.exprs(meta);
(
KeccakBitConfig::configure(meta, challenge_exprs),
challenges,
)
}

fn synthesize(
&self,
config: Self::Config,
mut layouter: impl Layouter<F>,
) -> Result<(), Error> {
config.load(&mut layouter)?;
config.assign(&mut layouter, &self.witness)?;
config.0.load(&mut layouter)?;
let witness = self.generate_witness(config.1.values(&mut layouter));
config.0.assign(&mut layouter, witness)?;
Ok(())
}
}

impl<F: Field> KeccakBitCircuit<F> {
/// Creates a new circuit instance
pub fn new(num_rows: usize) -> Self {
pub fn new(num_rows: usize, witness_inputs: Vec<Vec<u8>>) -> Self {
KeccakBitCircuit {
witness: Vec::new(),
witness_inputs,
num_rows,
_marker: PhantomData,
}
Expand All @@ -134,14 +134,17 @@ impl<F: Field> KeccakBitCircuit<F> {
}

/// Sets the witness using the data to be hashed
pub fn generate_witness(&mut self, inputs: &[Vec<u8>]) {
self.witness = multi_keccak(inputs, KeccakBitCircuit::r(), Some(self.capacity()))
.expect("Too many inputs for given capacity");
pub(crate) fn generate_witness(&self, challenges: Challenges<Value<F>>) -> Vec<KeccakRow<F>> {
multi_keccak(&self.witness_inputs, challenges, Some(self.capacity()))
.expect("Too many inputs for given capacity")
}
}

impl<F: Field> KeccakBitConfig<F> {
pub(crate) fn configure(meta: &mut ConstraintSystem<F>, r: Expression<F>) -> Self {
pub(crate) fn configure(
meta: &mut ConstraintSystem<F>,
challenges: Challenges<Expression<F>>,
) -> Self {
let num_bits_per_theta_lookup = get_num_bits_per_theta_lookup();
info!("num_bits_per_theta_lookup: {}", num_bits_per_theta_lookup);

Expand All @@ -162,7 +165,7 @@ impl<F: Field> KeccakBitConfig<F> {
let theta_c = array_init::array_init(|_| meta.advice_column());
let input = array_init::array_init(|_| meta.advice_column());
let is_paddings = array_init::array_init(|_| meta.advice_column());
let data_rlcs = array_init::array_init(|_| meta.advice_column());
let data_rlcs = array_init::array_init(|_| meta.advice_column_in(SecondPhase));
let round_cst = array_init::array_init(|_| meta.fixed_column());

let mut theta_c_table = Vec::new();
Expand Down Expand Up @@ -379,7 +382,7 @@ impl<F: Field> KeccakBitConfig<F> {
.flat_map(|a| to_bytes::expr(&a[0]))
.rev()
.collect::<Vec<_>>();
let rlc = compose_rlc::expr(&hash_bytes_le, r.clone());
let rlc = compose_rlc::expr(&hash_bytes_le, challenges.evm_word());
cb.condition(start_new_hash(meta, Rotation::cur()), |cb| {
cb.require_equal(
"hash rlc check",
Expand Down Expand Up @@ -547,7 +550,7 @@ impl<F: Field> KeccakBitConfig<F> {
new_data_rlc = select::expr(
meta.query_advice(*is_padding, Rotation::cur()),
new_data_rlc.clone(),
new_data_rlc.clone() * r.clone() + byte.clone(),
new_data_rlc.clone() * challenges.keccak_input() + byte.clone(),
);
if idx < data_rlcs.len() - 1 {
let next_data_rlc = meta.query_advice(data_rlcs[idx + 1], Rotation::cur());
Expand Down Expand Up @@ -607,17 +610,17 @@ impl<F: Field> KeccakBitConfig<F> {
&self,
layouter: &mut impl Layouter<F>,
inputs: &[Vec<u8>],
r: F,
challenges: Challenges<Value<F>>,
capacity: Option<usize>,
) -> Result<(), Error> {
let witness = multi_keccak(inputs, r, capacity)?;
self.assign(layouter, &witness)
let witness = multi_keccak(inputs, challenges, capacity)?;
self.assign(layouter, witness)
}

fn assign(
&self,
layouter: &mut impl Layouter<F>,
witness: &[KeccakRow<F>],
witness: Vec<KeccakRow<F>>,
) -> Result<(), Error> {
layouter.assign_region(
|| "assign keccak rounds",
Expand Down Expand Up @@ -663,9 +666,9 @@ impl<F: Field> KeccakBitConfig<F> {
region,
offset,
[
F::from(row.is_final),
Value::known(F::from(row.is_final)),
row.data_rlc,
F::from(row.length as u64),
Value::known(F::from(row.length as u64)),
row.hash_rlc,
],
)?;
Expand Down Expand Up @@ -722,7 +725,7 @@ impl<F: Field> KeccakBitConfig<F> {
|| format!("assign padding selector {} {}", idx, offset),
*column,
offset,
|| Value::known(*data_rlc),
|| *data_rlc,
)?;
}

Expand Down Expand Up @@ -776,7 +779,7 @@ impl<F: Field> KeccakBitConfig<F> {
}
}

fn keccak<F: Field>(rows: &mut Vec<KeccakRow<F>>, bytes: &[u8], r: F) {
fn keccak<F: Field>(rows: &mut Vec<KeccakRow<F>>, bytes: &[u8], challenges: Challenges<Value<F>>) {
let mut bits = into_bits(bytes);
let mut s = [[[0u8; NUM_BITS_PER_WORD]; 5]; 5];
let absorb_positions = get_absorb_positions();
Expand All @@ -790,7 +793,7 @@ fn keccak<F: Field>(rows: &mut Vec<KeccakRow<F>>, bytes: &[u8], r: F) {
bits.push(1);

let mut length = 0usize;
let mut data_rlc = F::zero();
let mut data_rlc = Value::known(F::zero());
let chunks = bits.chunks(RATE_IN_BITS);
let num_chunks = chunks.len();
for (idx, chunk) in chunks.enumerate() {
Expand Down Expand Up @@ -860,7 +863,7 @@ fn keccak<F: Field>(rows: &mut Vec<KeccakRow<F>>, bytes: &[u8], r: F) {

// Padding/Length/Data rlc
let mut is_paddings = [false; ABSORB_WIDTH_PER_ROW_BYTES];
let mut data_rlcs = [F::zero(); ABSORB_WIDTH_PER_ROW_BYTES];
let mut data_rlcs = [Value::known(F::zero()); ABSORB_WIDTH_PER_ROW_BYTES];
if round < NUM_WORDS_TO_ABSORB {
for (padding_idx, is_padding) in is_paddings.iter_mut().enumerate() {
let byte_idx = round * 8 + padding_idx;
Expand All @@ -878,7 +881,7 @@ fn keccak<F: Field>(rows: &mut Vec<KeccakRow<F>>, bytes: &[u8], r: F) {
{
if !*padding {
let byte_value: F = pack_with_base(byte_bits, 2);
data_rlc = data_rlc * r + byte_value;
data_rlc = data_rlc * challenges.keccak_input() + Value::known(byte_value);
}
if idx < data_rlcs.len() - 1 {
data_rlcs[idx + 1] = data_rlc;
Expand All @@ -897,9 +900,11 @@ fn keccak<F: Field>(rows: &mut Vec<KeccakRow<F>>, bytes: &[u8], r: F) {
.flat_map(|a| to_bytes::value(&a[0]))
.rev()
.collect::<Vec<_>>();
rlc::value(&hash_bytes_le, r)
challenges
.evm_word()
.map(|challenge_value| rlc::value(&hash_bytes_le, challenge_value))
} else {
F::zero()
Value::known(F::zero())
};

rows.push(KeccakRow {
Expand Down Expand Up @@ -969,7 +974,7 @@ fn keccak<F: Field>(rows: &mut Vec<KeccakRow<F>>, bytes: &[u8], r: F) {

fn multi_keccak<F: Field>(
bytes: &[Vec<u8>],
r: F,
challenges: Challenges<Value<F>>,
capacity: Option<usize>,
) -> Result<Vec<KeccakRow<F>>, Error> {
// Dummy first row so that the initial data can be absorbed
Expand All @@ -981,20 +986,20 @@ fn multi_keccak<F: Field>(
theta_c: [0u8; THETA_C_WIDTH],
input: [0u8; ABSORB_WIDTH_PER_ROW],
is_paddings: [false; ABSORB_WIDTH_PER_ROW_BYTES],
data_rlcs: [F::zero(); ABSORB_WIDTH_PER_ROW_BYTES],
data_rlcs: [Value::known(F::zero()); ABSORB_WIDTH_PER_ROW_BYTES],
is_final: false,
length: 0usize,
data_rlc: F::zero(),
hash_rlc: F::zero(),
data_rlc: Value::known(F::zero()),
hash_rlc: Value::known(F::zero()),
}];
// Actual keccaks
for bytes in bytes {
keccak(&mut rows, bytes, r);
keccak(&mut rows, bytes, challenges);
}
if let Some(capacity) = capacity {
// Pad with no data hashes to the expected capacity
while rows.len() < (1 + capacity * (NUM_ROUNDS + 1)) {
keccak(&mut rows, &[], r);
keccak(&mut rows, &[], challenges);
}
// Check that we are not over capacity
if rows.len() > (1 + capacity * (NUM_ROUNDS + 1)) {
Expand All @@ -1010,8 +1015,7 @@ mod tests {
use halo2_proofs::{dev::MockProver, halo2curves::bn256::Fr};

fn verify<F: Field>(k: u32, inputs: Vec<Vec<u8>>, success: bool) {
let mut circuit = KeccakBitCircuit::new(2usize.pow(k));
circuit.generate_witness(&inputs);
let circuit = KeccakBitCircuit::new(2usize.pow(k), inputs);

let prover = MockProver::<F>::run(k, &circuit, vec![]).unwrap();
let verify_result = prover.verify();
Expand Down
Loading