pub mod message;
pub mod program;
pub mod trace;
use alloc::vec::Vec;
use core::{array, ops::Range};
pub use message::KeccakSpongeMsg;
use miden_core::{
Felt,
field::{PrimeCharacteristicRing, QuadFelt},
utils::RowMajorMatrix,
};
use miden_lifted_air::{AirBuilder, BaseAir, LiftedAir, LiftedAirBuilder};
pub use program::{NUM_PERIODIC_COLS, SPONGE_PERIOD, sponge_program};
use crate::{
hash::memory64::{CHUNK_ADDR_BASE, Memory64Msg},
logup::{
CyclicConstraintLookupBuilder, Deg, LookupAir, LookupBatch, LookupBuilder, LookupColumn,
LookupGroup, NUM_PUBLIC_VALUES, NUM_RANDOMNESS, NUM_SIGMA_VALUES, frac_col,
},
primitives::byte_pair_lut::{BytePairLutMsg, BytePairOp},
relations::{MAX_MESSAGE_WIDTH, NUM_BUS_IDS},
utils::{current_main, halves_le, next_main},
};
pub const COL_SPONGE_SEQ_ID: usize = 0;
pub const COL_ACT: usize = 1;
pub const COL_BYTES_LEFT: usize = 2;
pub const COL_IS_FIRST_BLOCK_OF_INVOCATION: usize = 3;
pub const COL_CHUNK_PTR: usize = 4;
pub const COL_IS_ZERO: usize = 5;
pub const COL_IS_CHUNK_AVAIL: usize = 6;
pub const COL_B_BEGIN: usize = 7;
pub const NUM_B_SELECTORS: usize = 8;
pub const COL_B_RANGE: Range<usize> = COL_B_BEGIN..(COL_B_BEGIN + NUM_B_SELECTORS);
pub const COL_CHUNK_LO: usize = 15;
pub const COL_CHUNK_HI: usize = 16;
pub const COL_STATE_PREV_LO: usize = 17;
pub const COL_STATE_PREV_HI: usize = 18;
pub const COL_STATE_NEW_LO: usize = 19;
pub const COL_STATE_NEW_HI: usize = 20;
pub const COL_STATE_OUT_LO: usize = 21;
pub const COL_STATE_OUT_HI: usize = 22;
pub const COL_CLEARED_LO: usize = 23;
pub const COL_CLEARED_HI: usize = 24;
pub const COL_PADDED_LO: usize = 25;
pub const COL_PADDED_HI: usize = 26;
pub const CHUNK_BYTES_RANGE: Range<usize> = 27..35;
pub const STATE_PREV_BYTES_RANGE: Range<usize> = 35..43;
pub const STATE_NEW_BYTES_RANGE: Range<usize> = 43..51;
pub const CLEARED_BYTES_RANGE: Range<usize> = 51..59;
pub const PADDED_BYTES_RANGE: Range<usize> = 59..67;
pub const NUM_MAIN_COLS: usize = 67;
pub const NUM_AUX_COLS: usize = 24;
pub use program::{
COL_CAPACITY as PCOL_CAPACITY, COL_EXTRA as PCOL_EXTRA, COL_FIRST as PCOL_FIRST,
COL_IDX as PCOL_IDX, COL_LAST as PCOL_LAST, COL_PAD_0X80 as PCOL_PAD_0X80,
COL_RATE_BLOCK as PCOL_RATE_BLOCK, COL_RC_ACTIVE as PCOL_RC_ACTIVE, COL_RC_HI as PCOL_RC_HI,
COL_RC_LO as PCOL_RC_LO, COL_SQUEEZE_ACTIVE as PCOL_SQUEEZE_ACTIVE,
};
#[derive(Debug, Default, Clone, Copy)]
pub struct KeccakSpongeAir;
impl BaseAir<Felt> for KeccakSpongeAir {
fn width(&self) -> usize {
NUM_MAIN_COLS
}
fn num_public_values(&self) -> usize {
NUM_PUBLIC_VALUES
}
fn periodic_columns(&self) -> Vec<Vec<Felt>> {
sponge_program().to_vec()
}
}
pub const ANDNOT_MASK_LO: [u32; 8] = [
0xffff_ffff,
0xffff_ff00,
0xffff_0000,
0xff00_0000,
0x0000_0000,
0x0000_0000,
0x0000_0000,
0x0000_0000,
];
pub const ANDNOT_MASK_HI: [u32; 8] = [
0xffff_ffff,
0xffff_ffff,
0xffff_ffff,
0xffff_ffff,
0xffff_ffff,
0xffff_ff00,
0xffff_0000,
0xff00_0000,
];
pub const PADDING_MASK_LO: [u32; 8] = [
0x0000_0001,
0x0000_0100,
0x0001_0000,
0x0100_0000,
0x0000_0000,
0x0000_0000,
0x0000_0000,
0x0000_0000,
];
pub const PADDING_MASK_HI: [u32; 8] = [
0x0000_0000,
0x0000_0000,
0x0000_0000,
0x0000_0000,
0x0000_0001,
0x0000_0100,
0x0001_0000,
0x0100_0000,
];
pub const PAD_CONST_LO: u32 = 0;
pub const PAD_CONST_HI: u32 = 0x8000_0000;
pub const PAD_CONST_BYTES: [u8; 8] = [0, 0, 0, 0, 0, 0, 0, 0x80];
const fn mask_byte(lo: u32, hi: u32, byte_idx: usize) -> u8 {
let word = if byte_idx < 4 { lo } else { hi };
((word >> (8 * (byte_idx % 4))) & 0xff) as u8
}
impl LiftedAir<Felt, QuadFelt> for KeccakSpongeAir {
fn num_randomness(&self) -> usize {
NUM_RANDOMNESS
}
fn aux_width(&self) -> usize {
NUM_AUX_COLS
}
fn num_aux_values(&self) -> usize {
NUM_SIGMA_VALUES
}
fn build_aux_trace(
&self,
main: &RowMajorMatrix<Felt>,
_air_inputs: &[Felt],
_aux_inputs: &[Felt],
challenges: &[QuadFelt],
) -> (RowMajorMatrix<QuadFelt>, Vec<QuadFelt>) {
trace::build_aux(main, challenges)
}
fn eval<AB: LiftedAirBuilder<F = Felt>>(&self, builder: &mut AB) {
let local: [AB::Var; NUM_MAIN_COLS] = current_main(builder.main(), 0);
let next: [AB::Var; NUM_MAIN_COLS] = next_main(builder.main(), 0);
let periodic = builder.periodic_values();
let p_first: AB::Expr = periodic[PCOL_FIRST].into();
let p_last: AB::Expr = periodic[PCOL_LAST].into();
let p_rate_block: AB::Expr = periodic[PCOL_RATE_BLOCK].into();
let p_capacity: AB::Expr = periodic[PCOL_CAPACITY].into();
let p_extra: AB::Expr = periodic[PCOL_EXTRA].into();
let p_state_lane: AB::Expr = p_rate_block.clone() + p_capacity.clone();
let act: AB::Expr = local[COL_ACT].into();
let act_next: AB::Expr = next[COL_ACT].into();
let sponge_seq_id: AB::Expr = local[COL_SPONGE_SEQ_ID].into();
let sponge_seq_id_next: AB::Expr = next[COL_SPONGE_SEQ_ID].into();
let bytes_left: AB::Expr = local[COL_BYTES_LEFT].into();
let bytes_left_next: AB::Expr = next[COL_BYTES_LEFT].into();
let chunk_ptr: AB::Expr = local[COL_CHUNK_PTR].into();
let chunk_ptr_next: AB::Expr = next[COL_CHUNK_PTR].into();
let is_first_block: AB::Expr = local[COL_IS_FIRST_BLOCK_OF_INVOCATION].into();
let is_first_block_next: AB::Expr = next[COL_IS_FIRST_BLOCK_OF_INVOCATION].into();
let is_zero: AB::Expr = local[COL_IS_ZERO].into();
let is_zero_next: AB::Expr = next[COL_IS_ZERO].into();
let is_chunk_avail: AB::Expr = local[COL_IS_CHUNK_AVAIL].into();
let is_chunk_avail_next: AB::Expr = next[COL_IS_CHUNK_AVAIL].into();
let state_prev_lo: AB::Expr = local[COL_STATE_PREV_LO].into();
let state_prev_hi: AB::Expr = local[COL_STATE_PREV_HI].into();
let state_new_lo: AB::Expr = local[COL_STATE_NEW_LO].into();
let state_new_hi: AB::Expr = local[COL_STATE_NEW_HI].into();
let mut b_sum = AB::Expr::ZERO;
let mut b_weighted = AB::Expr::ZERO;
for (j, col) in COL_B_RANGE.enumerate() {
let b_j: AB::Expr = local[col].into();
b_sum += b_j.clone();
b_weighted += AB::Expr::from(Felt::from(j as u32)) * b_j;
}
builder.when_first_row().assert_zero(sponge_seq_id.clone());
builder.assert_bool(local[COL_ACT]);
builder
.when_transition()
.assert_zero((AB::Expr::ONE - act.clone()) * act_next.clone());
builder.when_transition().assert_zero(
(act.clone() - act_next) * (AB::Expr::ONE - p_last.clone() * b_sum.clone()),
);
builder
.when_transition()
.assert_zero(sponge_seq_id_next - sponge_seq_id - AB::Expr::ONE);
builder.assert_bool(local[COL_IS_FIRST_BLOCK_OF_INVOCATION]);
builder.assert_zero(
(AB::Expr::ONE - p_last.clone())
* (is_first_block_next.clone() - is_first_block.clone()),
);
builder.assert_zero(
act.clone()
* p_rate_block.clone()
* (bytes_left_next.clone() - bytes_left.clone() + AB::Expr::from(Felt::from(8u8))),
);
let enters_new_invocation = p_last.clone() * is_first_block_next.clone();
builder.assert_zero(
act.clone()
* (AB::Expr::ONE - enters_new_invocation.clone())
* (AB::Expr::ONE - p_rate_block.clone())
* (bytes_left_next - bytes_left.clone()),
);
builder.when_transition().assert_zero(
(AB::Expr::ONE - enters_new_invocation)
* (chunk_ptr_next
- chunk_ptr
- (p_rate_block.clone() + p_extra * b_sum.clone()) * is_chunk_avail.clone()),
);
let chunk_lo_local: AB::Expr = local[COL_CHUNK_LO].into();
let chunk_hi_local: AB::Expr = local[COL_CHUNK_HI].into();
builder.assert_zero((AB::Expr::ONE - is_chunk_avail.clone()) * chunk_lo_local);
builder.assert_zero((AB::Expr::ONE - is_chunk_avail.clone()) * chunk_hi_local);
builder.assert_bool(local[COL_IS_ZERO]);
builder.assert_bool(local[COL_IS_CHUNK_AVAIL]);
for col in COL_B_RANGE {
builder.assert_bool(local[col]);
}
builder.assert_zero(
(AB::Expr::ONE - p_last.clone())
* is_zero.clone()
* (AB::Expr::ONE - is_zero_next.clone()),
);
builder.assert_zero(
(AB::Expr::ONE - p_last.clone())
* (AB::Expr::ONE - is_chunk_avail)
* is_chunk_avail_next,
);
builder.assert_zero(p_first * is_zero.clone());
for col in COL_B_RANGE {
let b_j: AB::Expr = local[col].into();
let b_j_next: AB::Expr = next[col].into();
builder.assert_zero((AB::Expr::ONE - p_last.clone()) * (b_j_next - b_j));
}
builder.assert_zero(
(AB::Expr::ONE - p_rate_block.clone()) * (b_sum.clone() - is_zero.clone()),
);
builder.assert_zero(act * p_last * is_first_block_next * (AB::Expr::ONE - is_zero.clone()));
let is_pad: AB::Expr = is_zero_next - is_zero.clone();
builder.assert_zero(p_rate_block.clone() * is_pad * (b_weighted - bytes_left));
builder.assert_zero(p_state_lane.clone() * is_first_block.clone() * state_prev_lo.clone());
builder.assert_zero(p_state_lane * is_first_block * state_prev_hi.clone());
builder.assert_zero(
p_rate_block.clone() * is_zero.clone() * (state_new_lo.clone() - state_prev_lo.clone()),
);
builder
.assert_zero(p_rate_block * is_zero * (state_new_hi.clone() - state_prev_hi.clone()));
builder.assert_zero(p_capacity.clone() * (state_new_lo.clone() - state_prev_lo.clone()));
builder.assert_zero(p_capacity * (state_new_hi.clone() - state_prev_hi.clone()));
let chunk_lo: AB::Expr = local[COL_CHUNK_LO].into();
let chunk_hi: AB::Expr = local[COL_CHUNK_HI].into();
let cleared_lo: AB::Expr = local[COL_CLEARED_LO].into();
let cleared_hi: AB::Expr = local[COL_CLEARED_HI].into();
let padded_lo: AB::Expr = local[COL_PADDED_LO].into();
let padded_hi: AB::Expr = local[COL_PADDED_HI].into();
let link = |builder: &mut AB, range: Range<usize>, lo: AB::Expr, hi: AB::Expr| {
let bytes: [AB::Var; 8] = array::from_fn(|i| local[range.start + i]);
let [lo_from_bytes, hi_from_bytes]: [AB::Expr; 2] = halves_le(&bytes, 256);
builder.assert_zero(lo_from_bytes - lo);
builder.assert_zero(hi_from_bytes - hi);
};
link(builder, CHUNK_BYTES_RANGE, chunk_lo, chunk_hi);
link(builder, STATE_PREV_BYTES_RANGE, state_prev_lo, state_prev_hi);
link(builder, STATE_NEW_BYTES_RANGE, state_new_lo, state_new_hi);
link(builder, CLEARED_BYTES_RANGE, cleared_lo, cleared_hi);
link(builder, PADDED_BYTES_RANGE, padded_lo, padded_hi);
let mut lb =
CyclicConstraintLookupBuilder::new(builder, self, self.preprocessed_width() > 0);
<Self as LookupAir<_>>::eval(self, &mut lb);
}
}
const COLUMN_SHAPE: [usize; NUM_AUX_COLS] = build_column_shape();
const fn build_column_shape() -> [usize; NUM_AUX_COLS] {
let mut shape = [2usize; NUM_AUX_COLS];
shape[0] = 2;
shape[1] = 3;
shape[2] = 1;
shape[NUM_AUX_COLS - 1] = 2;
shape
}
impl<LB> LookupAir<LB> for KeccakSpongeAir
where
LB: LookupBuilder<F = Felt>,
{
fn num_columns(&self) -> usize {
NUM_AUX_COLS
}
fn column_shape(&self) -> &[usize] {
&COLUMN_SHAPE
}
fn max_message_width(&self) -> usize {
MAX_MESSAGE_WIDTH
}
fn num_bus_ids(&self) -> usize {
NUM_BUS_IDS
}
fn eval(&self, builder: &mut LB) {
let local: [LB::Var; NUM_MAIN_COLS] = current_main(builder.main(), 0);
let next: [LB::Var; NUM_MAIN_COLS] = next_main(builder.main(), 0);
let periodic = builder.periodic_values();
let p_first: LB::Expr = periodic[PCOL_FIRST].into();
let p_rate_block: LB::Expr = periodic[PCOL_RATE_BLOCK].into();
let p_capacity: LB::Expr = periodic[PCOL_CAPACITY].into();
let p_rc_active: LB::Expr = periodic[PCOL_RC_ACTIVE].into();
let p_squeeze_active: LB::Expr = periodic[PCOL_SQUEEZE_ACTIVE].into();
let p_pad_0x80: LB::Expr = periodic[PCOL_PAD_0X80].into();
let p_extra: LB::Expr = periodic[PCOL_EXTRA].into();
let p_idx: LB::Expr = periodic[PCOL_IDX].into();
let rc_lo: LB::Expr = periodic[PCOL_RC_LO].into();
let rc_hi: LB::Expr = periodic[PCOL_RC_HI].into();
let p_state_lane: LB::Expr = p_rate_block.clone() + p_capacity;
let act: LB::Expr = local[COL_ACT].into();
let sponge_seq_id: LB::Expr = local[COL_SPONGE_SEQ_ID].into();
let chunk_ptr: LB::Expr = local[COL_CHUNK_PTR].into();
let bytes_left: LB::Expr = local[COL_BYTES_LEFT].into();
let is_first_block: LB::Expr = local[COL_IS_FIRST_BLOCK_OF_INVOCATION].into();
let is_chunk_avail: LB::Expr = local[COL_IS_CHUNK_AVAIL].into();
let is_zero: LB::Expr = local[COL_IS_ZERO].into();
let is_zero_next: LB::Expr = next[COL_IS_ZERO].into();
let chunk_lo: LB::Expr = local[COL_CHUNK_LO].into();
let chunk_hi: LB::Expr = local[COL_CHUNK_HI].into();
let state_prev_lo: LB::Expr = local[COL_STATE_PREV_LO].into();
let state_prev_hi: LB::Expr = local[COL_STATE_PREV_HI].into();
let state_new_lo: LB::Expr = local[COL_STATE_NEW_LO].into();
let state_new_hi: LB::Expr = local[COL_STATE_NEW_HI].into();
let state_out_lo: LB::Expr = local[COL_STATE_OUT_LO].into();
let state_out_hi: LB::Expr = local[COL_STATE_OUT_HI].into();
let cleared_bytes: [LB::Var; 8] = array::from_fn(|i| local[CLEARED_BYTES_RANGE.start + i]);
let padded_bytes: [LB::Var; 8] = array::from_fn(|i| local[PADDED_BYTES_RANGE.start + i]);
let chunk_bytes: [LB::Var; 8] = array::from_fn(|i| local[CHUNK_BYTES_RANGE.start + i]);
let state_prev_bytes: [LB::Var; 8] =
array::from_fn(|i| local[STATE_PREV_BYTES_RANGE.start + i]);
let state_new_bytes: [LB::Var; 8] =
array::from_fn(|i| local[STATE_NEW_BYTES_RANGE.start + i]);
let mut b_sum = LB::Expr::ZERO;
let mut andnot_mask_bytes: [LB::Expr; 8] = array::from_fn(|_| LB::Expr::ZERO);
let mut padding_mask_bytes: [LB::Expr; 8] = array::from_fn(|_| LB::Expr::ZERO);
for (j, col) in COL_B_RANGE.enumerate() {
let b_j: LB::Expr = local[col].into();
b_sum += b_j.clone();
for i in 0..8 {
let andnot_byte = mask_byte(ANDNOT_MASK_LO[j], ANDNOT_MASK_HI[j], i);
let padding_byte = mask_byte(PADDING_MASK_LO[j], PADDING_MASK_HI[j], i);
andnot_mask_bytes[i] += LB::Expr::from(Felt::from(andnot_byte)) * b_j.clone();
padding_mask_bytes[i] += LB::Expr::from(Felt::from(padding_byte)) * b_j.clone();
}
}
let is_intra: LB::Expr = LB::Expr::ONE - is_first_block.clone();
let is_first_row_of_invocation: LB::Expr = p_first * is_first_block;
let is_pad: LB::Expr = is_zero_next.clone() - is_zero;
let is_verbatim: LB::Expr = LB::Expr::ONE - is_zero_next;
let hundred_seq = LB::Expr::from(Felt::from(100u8)) * sponge_seq_id.clone();
let ninety_nine_idx = LB::Expr::from(Felt::from(99u8)) * p_idx.clone();
let addr_state_lane_prev =
hundred_seq.clone() - ninety_nine_idx.clone() - LB::Expr::from(Felt::from(128u8));
let addr_state_lane_new = hundred_seq.clone() - ninety_nine_idx.clone();
let addr_rc = hundred_seq.clone()
+ LB::Expr::from(Felt::from(28u8)) * p_idx
+ LB::Expr::from(Felt::from(25u8));
let addr_squeeze =
hundred_seq.clone() - ninety_nine_idx + LB::Expr::from(Felt::from(3072u32));
let addr_lane16 = hundred_seq - LB::Expr::from(Felt::from(2484u32));
let chunk_addr_base =
Felt::new(CHUNK_ADDR_BASE).expect("CHUNK_ADDR_BASE fits in canonical Goldilocks");
let addr_chunk = LB::Expr::from(chunk_addr_base) + chunk_ptr.clone();
let mult_prev_perm: LB::Expr = LB::Expr::from(Felt::from(2u8)) * act.clone() * is_intra;
let mult_new_state: LB::Expr =
LB::Expr::ZERO - LB::Expr::from(Felt::from(2u8)) * act.clone();
let mult_rc: LB::Expr =
LB::Expr::ZERO - LB::Expr::from(Felt::from(1u8)) * act.clone() * p_rc_active;
let mult_squeeze: LB::Expr =
LB::Expr::from(Felt::from(2u8)) * act.clone() * p_squeeze_active * b_sum.clone();
let mult_lane16_consume: LB::Expr =
LB::Expr::from(Felt::from(2u8)) * act.clone() * b_sum.clone();
let mult_lane16_provide: LB::Expr =
LB::Expr::ZERO - LB::Expr::from(Felt::from(2u8)) * act.clone() * b_sum.clone();
let andnot_tag = LB::Expr::from(Felt::from(BytePairOp::AndNot.tag()));
let xor_tag = LB::Expr::from(Felt::from(BytePairOp::Xor.tag()));
let interaction_deg = Deg { v: 1, u: 1 };
let pair_deg = Deg { v: 4, u: 2 };
let triple_deg = Deg { v: 5, u: 3 };
let solo_deg = Deg { v: 4, u: 1 };
let mixed_deg = Deg { v: 5, u: 2 };
frac_col!(
builder,
"memory64",
pair_deg,
(
"new-state",
p_state_lane.clone() * mult_new_state.clone(),
Memory64Msg {
addr: addr_state_lane_new.clone(),
lo: state_new_lo.clone(),
hi: state_new_hi.clone(),
},
interaction_deg
),
(
"prev-perm",
p_state_lane.clone() * mult_prev_perm.clone(),
Memory64Msg {
addr: addr_state_lane_prev.clone(),
lo: state_prev_lo.clone(),
hi: state_prev_hi.clone(),
},
interaction_deg
),
);
frac_col!(
builder,
"memory64",
triple_deg,
(
"rc",
p_state_lane.clone() * mult_rc.clone(),
Memory64Msg {
addr: addr_rc.clone(),
lo: rc_lo.clone(),
hi: rc_hi.clone()
},
interaction_deg
),
(
"lane16-consume",
p_pad_0x80.clone() * mult_lane16_consume.clone(),
Memory64Msg {
addr: addr_lane16.clone(),
lo: state_prev_lo.clone(),
hi: state_prev_hi.clone(),
},
interaction_deg
),
(
"lane16-provide",
p_pad_0x80.clone() * mult_lane16_provide.clone(),
Memory64Msg {
addr: addr_lane16.clone(),
lo: state_new_lo.clone(),
hi: state_new_hi.clone(),
},
interaction_deg
),
);
frac_col!(
builder,
"memory64",
solo_deg,
(
"squeeze",
p_state_lane.clone() * mult_squeeze.clone(),
Memory64Msg {
addr: addr_squeeze.clone(),
lo: state_out_lo.clone(),
hi: state_out_hi.clone()
},
interaction_deg
),
);
let pad_mult = p_rate_block.clone() * is_pad * act.clone();
for pair in 0..4 {
let i0 = pair * 2;
let i1 = i0 + 1;
frac_col!(
builder,
"byte-pair-lut",
pair_deg,
(
"andnot",
pad_mult.clone(),
BytePairLutMsg {
op: andnot_tag.clone(),
a: andnot_mask_bytes[i0].clone(),
b: chunk_bytes[i0].into(),
c: cleared_bytes[i0].into()
},
interaction_deg
),
(
"andnot",
pad_mult.clone(),
BytePairLutMsg {
op: andnot_tag.clone(),
a: andnot_mask_bytes[i1].clone(),
b: chunk_bytes[i1].into(),
c: cleared_bytes[i1].into()
},
interaction_deg
),
);
}
for pair in 0..4 {
let i0 = pair * 2;
let i1 = i0 + 1;
frac_col!(
builder,
"byte-pair-lut",
pair_deg,
(
"xor-padding",
pad_mult.clone(),
BytePairLutMsg {
op: xor_tag.clone(),
a: cleared_bytes[i0].into(),
b: padding_mask_bytes[i0].clone(),
c: padded_bytes[i0].into()
},
interaction_deg
),
(
"xor-padding",
pad_mult.clone(),
BytePairLutMsg {
op: xor_tag.clone(),
a: cleared_bytes[i1].into(),
b: padding_mask_bytes[i1].clone(),
c: padded_bytes[i1].into()
},
interaction_deg
),
);
}
for pair in 0..4 {
let i0 = pair * 2;
let i1 = i0 + 1;
frac_col!(
builder,
"byte-pair-lut",
pair_deg,
(
"xor-state",
pad_mult.clone(),
BytePairLutMsg {
op: xor_tag.clone(),
a: state_prev_bytes[i0].into(),
b: padded_bytes[i0].into(),
c: state_new_bytes[i0].into()
},
interaction_deg
),
(
"xor-state",
pad_mult.clone(),
BytePairLutMsg {
op: xor_tag.clone(),
a: state_prev_bytes[i1].into(),
b: padded_bytes[i1].into(),
c: state_new_bytes[i1].into()
},
interaction_deg
),
);
}
let verbatim_mult = p_rate_block.clone() * is_verbatim * act.clone();
for pair in 0..4 {
let i0 = pair * 2;
let i1 = i0 + 1;
frac_col!(
builder,
"byte-pair-lut",
pair_deg,
(
"xor-state-verbatim",
verbatim_mult.clone(),
BytePairLutMsg {
op: xor_tag.clone(),
a: state_prev_bytes[i0].into(),
b: chunk_bytes[i0].into(),
c: state_new_bytes[i0].into()
},
interaction_deg
),
(
"xor-state-verbatim",
verbatim_mult.clone(),
BytePairLutMsg {
op: xor_tag.clone(),
a: state_prev_bytes[i1].into(),
b: chunk_bytes[i1].into(),
c: state_new_bytes[i1].into()
},
interaction_deg
),
);
}
let lane16_mult = p_pad_0x80.clone() * b_sum.clone() * act.clone();
for pair in 0..4 {
let i0 = pair * 2;
let i1 = i0 + 1;
frac_col!(
builder,
"byte-pair-lut",
pair_deg,
(
"xor-lane16",
lane16_mult.clone(),
BytePairLutMsg {
op: xor_tag.clone(),
a: state_prev_bytes[i0].into(),
b: LB::Expr::from(Felt::from(PAD_CONST_BYTES[i0])),
c: state_new_bytes[i0].into()
},
interaction_deg
),
(
"xor-lane16",
lane16_mult.clone(),
BytePairLutMsg {
op: xor_tag.clone(),
a: state_prev_bytes[i1].into(),
b: LB::Expr::from(Felt::from(PAD_CONST_BYTES[i1])),
c: state_new_bytes[i1].into()
},
interaction_deg
),
);
}
frac_col!(
builder,
"ks-and-chunk",
mixed_deg,
(
"ks-request",
act.clone() * is_first_row_of_invocation.clone(),
KeccakSpongeMsg {
sponge_seq_id: sponge_seq_id.clone(),
chunk_ptr: chunk_ptr.clone(),
len_bytes: bytes_left.clone(),
},
interaction_deg
),
(
"chunk-consume",
act.clone()
* (p_rate_block.clone() + p_extra.clone() * b_sum.clone())
* is_chunk_avail.clone(),
Memory64Msg {
addr: addr_chunk.clone(),
lo: chunk_lo.clone(),
hi: chunk_hi.clone()
},
interaction_deg
),
);
}
}