Logic CTL for xor

This commit is contained in:
Daniel Lubarov 2022-09-04 16:53:04 -07:00
parent d392ec04e7
commit c9cfcecc9f
4 changed files with 86 additions and 57 deletions

View File

@ -56,6 +56,10 @@ impl<F: Field> Column<F> {
Self::constant(F::ZERO) Self::constant(F::ZERO)
} }
pub fn one() -> Self {
Self::constant(F::ONE)
}
pub fn linear_combination_with_constant<I: IntoIterator<Item = (usize, F)>>( pub fn linear_combination_with_constant<I: IntoIterator<Item = (usize, F)>>(
iter: I, iter: I,
constant: F, constant: F,

View File

@ -42,16 +42,16 @@ pub(crate) struct KeccakSpongeColumnsView<T: Copy> {
/// If this row represents a full input block, this should contain all 0s. /// If this row represents a full input block, this should contain all 0s.
pub is_final_input_len: [T; KECCAK_RATE_BYTES], pub is_final_input_len: [T; KECCAK_RATE_BYTES],
/// The initial rate bits of the sponge, at the start of this step. /// The initial rate part of the sponge, at the start of this step.
pub original_rate_bits: [T; KECCAK_RATE_BITS], pub original_rate_u32s: [T; KECCAK_RATE_U32S],
/// The capacity bits of the sponge, encoded as 32-bit chunks, at the start of this step. /// The capacity part of the sponge, encoded as 32-bit chunks, at the start of this step.
pub original_capacity_u32s: [T; KECCAK_CAPACITY_U32S], pub original_capacity_u32s: [T; KECCAK_CAPACITY_U32S],
/// The block being absorbed, which may contain input bytes and/or padding bytes. /// The block being absorbed, which may contain input bytes and/or padding bytes.
pub block_bits: [T; KECCAK_RATE_BITS], pub block_bytes: [T; KECCAK_RATE_BYTES],
/// The rate bits of the sponge, after the current block is xor'd in, but before the permutation /// The rate part of the sponge, after the current block is xor'd in, but before the permutation
/// is applied. /// is applied.
pub xored_rate_u32s: [T; KECCAK_RATE_U32S], pub xored_rate_u32s: [T; KECCAK_RATE_U32S],

View File

@ -1,6 +1,7 @@
use std::borrow::Borrow; use std::borrow::Borrow;
use std::iter; use std::iter::{once, repeat};
use std::marker::PhantomData; use std::marker::PhantomData;
use std::mem::size_of;
use itertools::Itertools; use itertools::Itertools;
use plonky2::field::extension::{Extendable, FieldExtension}; use plonky2::field::extension::{Extendable, FieldExtension};
@ -11,6 +12,7 @@ use plonky2::hash::hash_types::RichField;
use plonky2::iop::ext_target::ExtensionTarget; use plonky2::iop::ext_target::ExtensionTarget;
use plonky2::timed; use plonky2::timed;
use plonky2::util::timing::TimingTree; use plonky2::util::timing::TimingTree;
use plonky2_util::ceil_div_usize;
use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer};
use crate::cpu::kernel::keccak_util::keccakf_u32s; use crate::cpu::kernel::keccak_util::keccakf_u32s;
@ -18,7 +20,7 @@ use crate::cross_table_lookup::Column;
use crate::keccak_sponge::columns::*; use crate::keccak_sponge::columns::*;
use crate::memory::segments::Segment; use crate::memory::segments::Segment;
use crate::stark::Stark; use crate::stark::Stark;
use crate::util::{trace_rows_to_poly_values, u32_from_le_bits, u32_to_le_bits, u8_to_le_bits}; use crate::util::trace_rows_to_poly_values;
use crate::vars::StarkEvaluationTargets; use crate::vars::StarkEvaluationTargets;
use crate::vars::StarkEvaluationVars; use crate::vars::StarkEvaluationVars;
@ -39,18 +41,16 @@ pub(crate) fn ctl_looked_data<F: Field>() -> Vec<Column<F>> {
#[allow(unused)] // TODO: Should be used soon. #[allow(unused)] // TODO: Should be used soon.
pub(crate) fn ctl_looking_keccak<F: Field>() -> Vec<Column<F>> { pub(crate) fn ctl_looking_keccak<F: Field>() -> Vec<Column<F>> {
let input_rate_cols = (0..KECCAK_RATE_U32S) let cols = KECCAK_SPONGE_COL_MAP;
.map(|i| Column::le_bits(&KECCAK_SPONGE_COL_MAP.original_rate_bits[i * 32..(i + 1) * 32])); Column::singles(
let input_capacity_cols = Column::singles( [
(0..KECCAK_CAPACITY_U32S).map(|i| KECCAK_SPONGE_COL_MAP.original_capacity_u32s[i]), cols.original_rate_u32s.as_slice(),
); &cols.original_capacity_u32s,
let output_cols = Column::singles( &cols.updated_state_u32s,
(0..KECCAK_WIDTH_U32S).map(|i| KECCAK_SPONGE_COL_MAP.updated_state_u32s[i]), ]
); .concat(),
input_rate_cols )
.chain(input_capacity_cols) .collect()
.chain(output_cols)
.collect()
} }
#[allow(unused)] // TODO: Should be used soon. #[allow(unused)] // TODO: Should be used soon.
@ -68,7 +68,7 @@ pub(crate) fn ctl_looking_memory<F: Field>(i: usize) -> Vec<Column<F>> {
)); ));
// The i'th input byte being read. // The i'th input byte being read.
res.push(Column::le_bits(&cols.block_bits[i * 8..(i + 1) * 8])); res.push(Column::single(cols.block_bytes[i]));
// Since we're reading a single byte, the higher limbs must be zero. // Since we're reading a single byte, the higher limbs must be zero.
res.extend((1..8).map(|_| Column::zero())); res.extend((1..8).map(|_| Column::zero()));
@ -82,6 +82,49 @@ pub(crate) fn ctl_looking_memory<F: Field>(i: usize) -> Vec<Column<F>> {
res res
} }
/// CTL for performing the `i`th logic CTL. Since we need to do 136 byte XORs, and the logic CTL can
/// XOR 32 bytes per CTL, there are 5 such CTLs.
#[allow(unused)] // TODO: Should be used soon.
pub(crate) fn ctl_looking_logic<F: Field>(i: usize) -> Vec<Column<F>> {
const U32S_PER_CTL: usize = 8;
const U8S_PER_CTL: usize = 32;
debug_assert!(i < ceil_div_usize(KECCAK_RATE_BYTES, U8S_PER_CTL));
let cols = KECCAK_SPONGE_COL_MAP;
let mut res = vec![
Column::zero(), // is_and
Column::zero(), // is_or
Column::one(), // is_xor
];
// Input 0 contains some of the sponge's original rate chunks. If this is the last CTL, we won't
// need to use all of the CTL's inputs, so we will pass some zeros.
res.extend(
Column::singles(&cols.original_rate_u32s[i * U32S_PER_CTL..])
.chain(repeat(Column::zero()))
.take(U32S_PER_CTL),
);
// Input 1 contains some of block's chunks. Again, for the last CTL it will include some zeros.
res.extend(
cols.block_bytes[i * U8S_PER_CTL..]
.chunks(size_of::<u32>())
.map(|chunk| Column::le_bytes(chunk))
.chain(repeat(Column::zero()))
.take(U8S_PER_CTL),
);
// The output contains the XOR'd rate part.
res.extend(
Column::singles(&cols.xored_rate_u32s[i * U32S_PER_CTL..])
.chain(repeat(Column::zero()))
.take(U32S_PER_CTL),
);
res
}
#[allow(unused)] // TODO: Should be used soon. #[allow(unused)] // TODO: Should be used soon.
pub(crate) fn ctl_looked_filter<F: Field>() -> Column<F> { pub(crate) fn ctl_looked_filter<F: Field>() -> Column<F> {
// The CPU table is only interested in our final-block rows, since those contain the final // The CPU table is only interested in our final-block rows, since those contain the final
@ -96,7 +139,7 @@ pub(crate) fn ctl_looking_memory_filter<F: Field>(i: usize) -> Column<F> {
// - this is a full input block, or // - this is a full input block, or
// - this is a final block of length `i` or greater // - this is a final block of length `i` or greater
let cols = KECCAK_SPONGE_COL_MAP; let cols = KECCAK_SPONGE_COL_MAP;
Column::sum(iter::once(&cols.is_full_input_block).chain(&cols.is_final_input_len[i..])) Column::sum(once(&cols.is_full_input_block).chain(&cols.is_final_input_len[i..]))
} }
/// Information about a Keccak sponge operation needed for witness generation. /// Information about a Keccak sponge operation needed for witness generation.
@ -157,7 +200,7 @@ impl<F: RichField + Extendable<D>, const D: usize> KeccakSpongeStark<F, D> {
operations operations
.into_iter() .into_iter()
.flat_map(|op| self.generate_rows_for_op(op)) .flat_map(|op| self.generate_rows_for_op(op))
.chain(iter::repeat(self.generate_padding_row())) .chain(repeat(self.generate_padding_row()))
.take(num_rows) .take(num_rows)
.collect() .collect()
} }
@ -208,13 +251,7 @@ impl<F: RichField + Extendable<D>, const D: usize> KeccakSpongeStark<F, D> {
..Default::default() ..Default::default()
}; };
row.block_bits = block row.block_bytes = block.map(F::from_canonical_u8);
.into_iter()
.flat_map(u8_to_le_bits)
.map(F::from_bool)
.collect_vec()
.try_into()
.unwrap();
Self::generate_common_fields(&mut row, op, already_absorbed_bytes, sponge_state); Self::generate_common_fields(&mut row, op, already_absorbed_bytes, sponge_state);
row row
@ -234,16 +271,18 @@ impl<F: RichField + Extendable<D>, const D: usize> KeccakSpongeStark<F, D> {
..Default::default() ..Default::default()
}; };
let final_input_bits = final_inputs for (block_byte, input_byte) in row.block_bytes.iter_mut().zip(final_inputs) {
.iter() *block_byte = F::from_canonical_u8(*input_byte);
.flat_map(|x| u8_to_le_bits(*x))
.map(F::from_bool);
for (block_bit, input_bit) in row.block_bits.iter_mut().zip(final_input_bits) {
*block_bit = input_bit;
} }
// pad10*1 rule // pad10*1 rule
row.block_bits[final_inputs.len() * 8] = F::ONE; if final_inputs.len() == KECCAK_RATE_BYTES - 1 {
row.block_bits[KECCAK_RATE_BITS - 1] = F::ONE; // Both 1s are placed in the same byte.
row.block_bytes[final_inputs.len()] = F::from_canonical_u8(0b10000001);
} else {
row.block_bytes[final_inputs.len()] = F::ONE;
row.block_bytes[KECCAK_RATE_BYTES - 1] = F::ONE;
}
row.is_final_input_len[final_inputs.len()] = F::ONE; row.is_final_input_len[final_inputs.len()] = F::ONE;
@ -252,6 +291,7 @@ impl<F: RichField + Extendable<D>, const D: usize> KeccakSpongeStark<F, D> {
} }
/// Generate fields that are common to both full-input-block rows and final-block rows. /// Generate fields that are common to both full-input-block rows and final-block rows.
/// Also updates the sponge state with a single absorption.
fn generate_common_fields( fn generate_common_fields(
row: &mut KeccakSpongeColumnsView<F>, row: &mut KeccakSpongeColumnsView<F>,
op: &KeccakSpongeOp, op: &KeccakSpongeOp,
@ -265,10 +305,9 @@ impl<F: RichField + Extendable<D>, const D: usize> KeccakSpongeStark<F, D> {
row.len = F::from_canonical_usize(op.len); row.len = F::from_canonical_usize(op.len);
row.already_absorbed_bytes = F::from_canonical_usize(already_absorbed_bytes); row.already_absorbed_bytes = F::from_canonical_usize(already_absorbed_bytes);
row.original_rate_bits = sponge_state[..KECCAK_RATE_U32S] row.original_rate_u32s = sponge_state[..KECCAK_RATE_U32S]
.iter() .iter()
.flat_map(|x| u32_to_le_bits(*x)) .map(|x| F::from_canonical_u32(*x))
.map(F::from_bool)
.collect_vec() .collect_vec()
.try_into() .try_into()
.unwrap(); .unwrap();
@ -281,10 +320,10 @@ impl<F: RichField + Extendable<D>, const D: usize> KeccakSpongeStark<F, D> {
.unwrap(); .unwrap();
let block_u32s = (0..KECCAK_RATE_U32S).map(|i| { let block_u32s = (0..KECCAK_RATE_U32S).map(|i| {
u32_from_le_bits( u32::from_le_bytes(
row.block_bits[i * 32..(i + 1) * 32] row.block_bytes[i * 4..(i + 1) * 4]
.iter() .iter()
.map(Field::is_one) .map(|x| x.to_canonical_u64() as u8)
.collect_vec() .collect_vec()
.try_into() .try_into()
.unwrap(), .unwrap(),

View File

@ -70,20 +70,6 @@ pub(crate) fn h160_limbs<F: Field>(h160: H160) -> [F; 5] {
.unwrap() .unwrap()
} }
pub(crate) fn u8_to_le_bits(x: u8) -> [bool; 8] {
std::array::from_fn(|i| ((x >> i) & 1) != 0)
}
pub(crate) fn u32_to_le_bits(x: u32) -> [bool; 32] {
std::array::from_fn(|i| ((x >> i) & 1) != 0)
}
pub(crate) fn u32_from_le_bits(bits: [bool; 32]) -> u32 {
bits.into_iter()
.rev()
.fold(0, |acc, b| (acc << 1) | b as u32)
}
pub(crate) const fn indices_arr<const N: usize>() -> [usize; N] { pub(crate) const fn indices_arr<const N: usize>() -> [usize; N] {
let mut indices_arr = [0; N]; let mut indices_arr = [0; N];
let mut i = 0; let mut i = 0;