openvm_keccak256_circuit/keccakf_op/
mod.rs

1mod air;
2pub mod columns;
3mod execution;
4#[cfg(test)]
5pub mod tests;
6/// Preflight and CPU trace generation
7pub mod trace;
8
9use std::mem::MaybeUninit;
10
11pub use air::*;
12pub use columns::*;
13pub use trace::*;
14
15use crate::{KECCAK_WIDTH_BYTES, KECCAK_WIDTH_U64S};
16
17pub const NUM_OP_ROWS_PER_INS: usize = 1;
18
19#[derive(derive_new::new, Clone, Copy)]
20pub struct KeccakfExecutor {
21    pub offset: usize,
22    pub pointer_max_bits: usize,
23}
24
25#[cfg(target_endian = "little")]
26fn copy_bytes_to_u64(src: &[u8; KECCAK_WIDTH_BYTES]) -> [u64; KECCAK_WIDTH_U64S] {
27    // 1. Create uninitialized memory for the destination. This avoids the performance cost of
28    //    writing zeros first.
29    let mut dst = MaybeUninit::<[u64; KECCAK_WIDTH_U64S]>::uninit();
30
31    unsafe {
32        // 2. Perform a raw memory copy. We cast the destination pointer to *mut u8 to copy
33        //    byte-by-byte. This is crucial: copying as u8s ignores the alignment of 'src'.
34        std::ptr::copy_nonoverlapping(
35            src.as_ptr(),                // Source pointer (*const u8)
36            dst.as_mut_ptr() as *mut u8, // Destination pointer (cast to *mut u8)
37            KECCAK_WIDTH_BYTES,          // Number of BYTES to copy
38        );
39        // 3. Assume initialization is complete. We know we filled all 200 bytes (25 * 8), so this
40        //    is safe.
41        dst.assume_init()
42    }
43}
44
45#[cfg(not(target_endian = "little"))]
46fn copy_bytes_to_u64(src: &[u8; KECCAK_WIDTH_BYTES]) -> [u64; KECCAK_WIDTH_U64S] {
47    let mut dst = [0u64; KECCAK_WIDTH_U64S];
48    for (u64_word, chunk) in dst.iter_mut().zip(src.chunks_exact(8)) {
49        *u64_word = u64::from_le_bytes(chunk.try_into().unwrap());
50    }
51    dst
52}
53
54#[cfg(target_endian = "little")]
55fn transmute_u64_to_bytes(src: [u64; KECCAK_WIDTH_U64S]) -> [u8; KECCAK_WIDTH_BYTES] {
56    // SAFETY:
57    // - The size of both is the same, and `src` is plain old data (so we do not even need the fact
58    //   that src has alignment > alignment of dst).
59    unsafe { std::mem::transmute::<[u64; KECCAK_WIDTH_U64S], [u8; KECCAK_WIDTH_BYTES]>(src) }
60}
61
62#[cfg(not(target_endian = "little"))]
63fn transmute_u64_to_bytes(src: [u64; KECCAK_WIDTH_U64S]) -> [u8; KECCAK_WIDTH_BYTES] {
64    let mut dst = [0u8; KECCAK_WIDTH_BYTES];
65    for (chunk, u64_word) in dst.chunks_exact_mut(8).zip(src.iter()) {
66        chunk.copy_from_slice(&u64_word.to_le_bytes());
67    }
68    dst
69}
70
71fn keccakf_postimage_bytes(
72    preimage_buffer_bytes: &[u8; KECCAK_WIDTH_BYTES],
73) -> [u8; KECCAK_WIDTH_BYTES] {
74    let mut state = copy_bytes_to_u64(preimage_buffer_bytes);
75    tiny_keccak::keccakf(&mut state);
76    transmute_u64_to_bytes(state)
77}