diff --git a/crates/zk_vm/src/dot_product_air.rs b/crates/zk_vm/src/dot_product_air.rs index 526a6076..0c4fae83 100644 --- a/crates/zk_vm/src/dot_product_air.rs +++ b/crates/zk_vm/src/dot_product_air.rs @@ -5,7 +5,7 @@ use p3_field::PrimeCharacteristicRing; use p3_matrix::Matrix; use vm::EF; -use crate::execution_trace::WitnessDotProduct; +use crate::witness::dot_product::WitnessDotProduct; /* | StartFlag | Len | IndexA | IndexB | IndexRes | ValueA | ValueB | Res | Computation | diff --git a/crates/zk_vm/src/execution_trace.rs b/crates/zk_vm/src/execution_trace.rs index c1587b3a..40141e91 100644 --- a/crates/zk_vm/src/execution_trace.rs +++ b/crates/zk_vm/src/execution_trace.rs @@ -3,291 +3,259 @@ use p3_symmetric::Permutation; use rayon::prelude::*; use utils::{ToUsize, build_poseidon16, build_poseidon24}; use vm::{ - Bytecode, EF, ExecutionResult, F, Instruction, POSEIDON_16_NULL_HASH_PTR, - POSEIDON_24_NULL_HASH_PTR, + Bytecode, ExecutionResult, F, Instruction, POSEIDON_16_NULL_HASH_PTR, POSEIDON_24_NULL_HASH_PTR, }; use crate::{ COL_INDEX_FP, COL_INDEX_MEM_ADDRESS_A, COL_INDEX_MEM_ADDRESS_B, COL_INDEX_MEM_ADDRESS_C, COL_INDEX_MEM_VALUE_A, COL_INDEX_MEM_VALUE_B, COL_INDEX_MEM_VALUE_C, COL_INDEX_PC, - N_EXEC_COLUMNS, N_INSTRUCTION_COLUMNS, instruction_encoder::field_representation, + N_EXEC_COLUMNS, N_INSTRUCTION_COLUMNS, + instruction_encoder::field_representation, + witness::{ + dot_product::WitnessDotProduct, + multilinear_eval::WitnessMultilinearEval, + poseidon::{WitnessPoseidon16, WitnessPoseidon24}, + }, }; -pub struct WitnessDotProduct { - pub cycle: usize, - pub addr_0: usize, // vectorized pointer - pub addr_1: usize, // vectorized pointer - pub addr_res: usize, // vectorized pointer - pub len: usize, - pub slice_0: Vec, - pub slice_1: Vec, - pub res: EF, -} - -pub struct WitnessMultilinearEval { - pub cycle: usize, - pub addr_coeffs: usize, // vectorized pointer, of size 8.2^size - pub addr_point: usize, // vectorized pointer, of size `size` - pub addr_res: usize, // vectorized pointer - pub n_vars: usize, - pub point: Vec, - pub res: EF, -} - -pub struct WitnessPoseidon16 { - pub cycle: Option, - pub addr_input_a: usize, // vectorized pointer (of size 1) - pub addr_input_b: usize, // vectorized pointer (of size 1) - pub addr_output: usize, // vectorized pointer (of size 2) - pub input: [F; 16], - pub output: [F; 16], -} - -pub struct WitnessPoseidon24 { - pub cycle: Option, - pub addr_input_a: usize, // vectorized pointer (of size 2) - pub addr_input_b: usize, // vectorized pointer (of size 1) - pub addr_output: usize, // vectorized pointer (of size 1) - pub input: [F; 24], - pub output: [F; 8], // last 8 elements of the output -} - +/// Represents the complete trace of a program's execution, including precompile data. +#[derive(Debug)] pub struct ExecutionTrace { + /// The main CPU trace columns. pub full_trace: Vec>, + /// The actual number of Poseidon16 operations executed. pub n_poseidons_16: usize, + /// The actual number of Poseidon24 operations executed. pub n_poseidons_24: usize, - pub poseidons_16: Vec, // padded with empty poseidons - pub poseidons_24: Vec, // padded with empty poseidons + /// Padded witness data for all Poseidon16 operations. + pub poseidons_16: Vec, + /// Padded witness data for all Poseidon24 operations. + pub poseidons_24: Vec, + /// Witness data for all dot product operations. pub dot_products: Vec, + /// Witness data for all multilinear evaluation operations. pub vm_multilinear_evals: Vec, + /// The size of the public memory region. pub public_memory_size: usize, - pub memory: Vec, // of length a multiple of public_memory_size + /// The final, flattened state of the VM's memory. + pub memory: Vec, } -pub fn get_execution_trace( - bytecode: &Bytecode, - execution_result: &ExecutionResult, -) -> ExecutionTrace { - assert_eq!(execution_result.pcs.len(), execution_result.fps.len()); - let n_cycles = execution_result.pcs.len(); - let memory = &execution_result.memory; - let log_n_cycles_rounded_up = n_cycles.next_power_of_two().ilog2() as usize; - let mut trace = (0..N_INSTRUCTION_COLUMNS + N_EXEC_COLUMNS) - .map(|_| F::zero_vec(1 << log_n_cycles_rounded_up)) - .collect::>>(); - let mut poseidons_16 = Vec::new(); - let mut poseidons_24 = Vec::new(); - let mut dot_products = Vec::new(); - let mut vm_multilinear_evals = Vec::new(); - - for (cycle, (&pc, &fp)) in execution_result - .pcs - .iter() - .zip(&execution_result.fps) - .enumerate() - { - let instruction = &bytecode.instructions[pc]; - let field_repr = field_representation(instruction); +impl ExecutionTrace { + pub fn new(bytecode: &Bytecode, execution_result: &ExecutionResult) -> Self { + assert_eq!(execution_result.pcs.len(), execution_result.fps.len()); + let n_cycles = execution_result.pcs.len(); + let memory = &execution_result.memory; + let log_n_cycles_rounded_up = n_cycles.next_power_of_two().ilog2() as usize; + let mut trace = (0..N_INSTRUCTION_COLUMNS + N_EXEC_COLUMNS) + .map(|_| F::zero_vec(1 << log_n_cycles_rounded_up)) + .collect::>>(); + let mut poseidons_16 = Vec::new(); + let mut poseidons_24 = Vec::new(); + let mut dot_products = Vec::new(); + let mut vm_multilinear_evals = Vec::new(); - // println!( - // "Cycle {}: PC = {}, FP = {}, Instruction = {}", - // i, pc, fp, instruction.to_string() - // ); + for (cycle, (&pc, &fp)) in execution_result + .pcs + .iter() + .zip(&execution_result.fps) + .enumerate() + { + let instruction = &bytecode.instructions[pc]; + let field_repr = field_representation(instruction); - for (j, field) in field_repr.iter().enumerate() { - trace[j][cycle] = *field; - } + for (j, field) in field_repr.iter().enumerate() { + trace[j][cycle] = *field; + } - let addr_a = if field_repr[3].is_zero() { - // flag_a == 0 - // fp + operand_a - F::from_usize(fp) + field_repr[0] - } else { - F::ZERO - }; + let addr_a = if field_repr[3].is_zero() { + // flag_a == 0 + // fp + operand_a + F::from_usize(fp) + field_repr[0] + } else { + F::ZERO + }; - let value_a = memory.0[addr_a.to_usize()].unwrap(); - let addr_b = if field_repr[4].is_zero() { - // flag_b == 0 - // fp + operand_b - F::from_usize(fp) + field_repr[1] - } else { - F::ZERO - }; - let value_b = memory.0[addr_b.to_usize()].unwrap(); + let value_a = memory.0[addr_a.to_usize()].unwrap(); + let addr_b = if field_repr[4].is_zero() { + // flag_b == 0 + // fp + operand_b + F::from_usize(fp) + field_repr[1] + } else { + F::ZERO + }; + let value_b = memory.0[addr_b.to_usize()].unwrap(); - let mut addr_c = F::ZERO; - if field_repr[5].is_zero() { - // flag_c == 0 - addr_c = F::from_usize(fp) + field_repr[2]; // fp + operand_c - } else if let Instruction::Deref { shift_1, .. } = instruction { - let operand_c = F::from_usize(*shift_1); - assert_eq!(field_repr[2], operand_c); // debug purpose - addr_c = value_a + operand_c; - } - let value_c = memory.0[addr_c.to_usize()].unwrap(); + let mut addr_c = F::ZERO; + if field_repr[5].is_zero() { + // flag_c == 0 + addr_c = F::from_usize(fp) + field_repr[2]; // fp + operand_c + } else if let Instruction::Deref { shift_1, .. } = instruction { + let operand_c = F::from_usize(*shift_1); + assert_eq!(field_repr[2], operand_c); // debug purpose + addr_c = value_a + operand_c; + } + let value_c = memory.0[addr_c.to_usize()].unwrap(); - trace[COL_INDEX_MEM_VALUE_A][cycle] = value_a; - trace[COL_INDEX_MEM_VALUE_B][cycle] = value_b; - trace[COL_INDEX_MEM_VALUE_C][cycle] = value_c; - trace[COL_INDEX_PC][cycle] = F::from_usize(pc); - trace[COL_INDEX_FP][cycle] = F::from_usize(fp); - trace[COL_INDEX_MEM_ADDRESS_A][cycle] = addr_a; - trace[COL_INDEX_MEM_ADDRESS_B][cycle] = addr_b; - trace[COL_INDEX_MEM_ADDRESS_C][cycle] = addr_c; + trace[COL_INDEX_MEM_VALUE_A][cycle] = value_a; + trace[COL_INDEX_MEM_VALUE_B][cycle] = value_b; + trace[COL_INDEX_MEM_VALUE_C][cycle] = value_c; + trace[COL_INDEX_PC][cycle] = F::from_usize(pc); + trace[COL_INDEX_FP][cycle] = F::from_usize(fp); + trace[COL_INDEX_MEM_ADDRESS_A][cycle] = addr_a; + trace[COL_INDEX_MEM_ADDRESS_B][cycle] = addr_b; + trace[COL_INDEX_MEM_ADDRESS_C][cycle] = addr_c; - match instruction { - Instruction::Poseidon2_16 { arg_a, arg_b, res } => { - let addr_input_a = arg_a.read_value(memory, fp).unwrap().to_usize(); - let addr_input_b = arg_b.read_value(memory, fp).unwrap().to_usize(); - let addr_output = res.read_value(memory, fp).unwrap().to_usize(); - let value_a = memory.get_vector(addr_input_a).unwrap(); - let value_b = memory.get_vector(addr_input_b).unwrap(); - let output = memory.get_vectorized_slice(addr_output, 2).unwrap(); - poseidons_16.push(WitnessPoseidon16 { - cycle: Some(cycle), - addr_input_a, - addr_input_b, - addr_output, - input: [value_a, value_b].concat().try_into().unwrap(), - output: output.try_into().unwrap(), - }); - } - Instruction::Poseidon2_24 { arg_a, arg_b, res } => { - let addr_input_a = arg_a.read_value(memory, fp).unwrap().to_usize(); - let addr_input_b = arg_b.read_value(memory, fp).unwrap().to_usize(); - let addr_output = res.read_value(memory, fp).unwrap().to_usize(); - let value_a = memory.get_vectorized_slice(addr_input_a, 2).unwrap(); - let value_b = memory.get_vector(addr_input_b).unwrap().to_vec(); - let output = memory.get_vector(addr_output).unwrap(); - poseidons_24.push(WitnessPoseidon24 { - cycle: Some(cycle), - addr_input_a, - addr_input_b, - addr_output, - input: [value_a, value_b].concat().try_into().unwrap(), - output, - }); - } - Instruction::DotProductExtensionExtension { - arg0, - arg1, - res, - size, - } => { - let addr_0 = arg0.read_value(memory, fp).unwrap().to_usize(); - let addr_1 = arg1.read_value(memory, fp).unwrap().to_usize(); - let addr_res = res.read_value(memory, fp).unwrap().to_usize(); - let slice_0 = memory - .get_vectorized_slice_extension(addr_0, *size) - .unwrap(); - let slice_1 = memory - .get_vectorized_slice_extension(addr_1, *size) - .unwrap(); - let res = memory.get_extension(addr_res).unwrap(); - dot_products.push(WitnessDotProduct { - cycle, - addr_0, - addr_1, - addr_res, - len: *size, - slice_0, - slice_1, + match instruction { + Instruction::Poseidon2_16 { arg_a, arg_b, res } => { + let addr_input_a = arg_a.read_value(memory, fp).unwrap().to_usize(); + let addr_input_b = arg_b.read_value(memory, fp).unwrap().to_usize(); + let addr_output = res.read_value(memory, fp).unwrap().to_usize(); + let value_a = memory.get_vector(addr_input_a).unwrap(); + let value_b = memory.get_vector(addr_input_b).unwrap(); + let output = memory.get_vectorized_slice(addr_output, 2).unwrap(); + poseidons_16.push(WitnessPoseidon16 { + cycle: Some(cycle), + addr_input_a, + addr_input_b, + addr_output, + input: [value_a, value_b].concat().try_into().unwrap(), + output: output.try_into().unwrap(), + }); + } + Instruction::Poseidon2_24 { arg_a, arg_b, res } => { + let addr_input_a = arg_a.read_value(memory, fp).unwrap().to_usize(); + let addr_input_b = arg_b.read_value(memory, fp).unwrap().to_usize(); + let addr_output = res.read_value(memory, fp).unwrap().to_usize(); + let value_a = memory.get_vectorized_slice(addr_input_a, 2).unwrap(); + let value_b = memory.get_vector(addr_input_b).unwrap().to_vec(); + let output = memory.get_vector(addr_output).unwrap(); + poseidons_24.push(WitnessPoseidon24 { + cycle: Some(cycle), + addr_input_a, + addr_input_b, + addr_output, + input: [value_a, value_b].concat().try_into().unwrap(), + output, + }); + } + Instruction::DotProductExtensionExtension { + arg0, + arg1, res, - }); - } - Instruction::MultilinearEval { - coeffs, - point, - res, - n_vars, - } => { - let addr_coeffs = coeffs.read_value(memory, fp).unwrap().to_usize(); - let addr_point = point.read_value(memory, fp).unwrap().to_usize(); - let addr_res = res.read_value(memory, fp).unwrap().to_usize(); - let point = memory - .get_vectorized_slice_extension(addr_point, *n_vars) - .unwrap(); - let res = memory.get_extension(addr_res).unwrap(); - vm_multilinear_evals.push(WitnessMultilinearEval { - cycle, - addr_coeffs, - addr_point, - addr_res, - n_vars: *n_vars, + size, + } => { + let addr_0 = arg0.read_value(memory, fp).unwrap().to_usize(); + let addr_1 = arg1.read_value(memory, fp).unwrap().to_usize(); + let addr_res = res.read_value(memory, fp).unwrap().to_usize(); + let slice_0 = memory + .get_vectorized_slice_extension(addr_0, *size) + .unwrap(); + let slice_1 = memory + .get_vectorized_slice_extension(addr_1, *size) + .unwrap(); + let res = memory.get_extension(addr_res).unwrap(); + dot_products.push(WitnessDotProduct { + cycle, + addr_0, + addr_1, + addr_res, + len: *size, + slice_0, + slice_1, + res, + }); + } + Instruction::MultilinearEval { + coeffs, point, res, - }); + n_vars, + } => { + let addr_coeffs = coeffs.read_value(memory, fp).unwrap().to_usize(); + let addr_point = point.read_value(memory, fp).unwrap().to_usize(); + let addr_res = res.read_value(memory, fp).unwrap().to_usize(); + let point = memory + .get_vectorized_slice_extension(addr_point, *n_vars) + .unwrap(); + let res = memory.get_extension(addr_res).unwrap(); + vm_multilinear_evals.push(WitnessMultilinearEval { + cycle, + addr_coeffs, + addr_point, + addr_res, + n_vars: *n_vars, + point, + res, + }); + } + _ => {} } - _ => {} } - } - // repeat the last row to get to a power of two - for t in trace - .iter_mut() - .take(N_INSTRUCTION_COLUMNS + N_EXEC_COLUMNS) - { - let last_value = t[n_cycles - 1]; - for tt in t + // repeat the last row to get to a power of two + for t in trace .iter_mut() - .take(1 << log_n_cycles_rounded_up) - .skip(n_cycles) + .take(N_INSTRUCTION_COLUMNS + N_EXEC_COLUMNS) { - *tt = last_value; + let last_value = t[n_cycles - 1]; + for tt in t + .iter_mut() + .take(1 << log_n_cycles_rounded_up) + .skip(n_cycles) + { + *tt = last_value; + } } - } - let mut memory = memory - .0 - .par_iter() - .map(|&v| v.unwrap_or(F::ZERO)) - .collect::>(); - memory.resize( - memory - .len() - .next_multiple_of(execution_result.public_memory_size), - F::ZERO, - ); + let mut memory = memory + .0 + .par_iter() + .map(|&v| v.unwrap_or(F::ZERO)) + .collect::>(); + memory.resize( + memory + .len() + .next_multiple_of(execution_result.public_memory_size), + F::ZERO, + ); - let n_poseidons_16 = poseidons_16.len(); - let n_poseidons_24 = poseidons_24.len(); + let n_poseidons_16 = poseidons_16.len(); + let n_poseidons_24 = poseidons_24.len(); - let empty_poseidon16_output = build_poseidon16().permute([F::ZERO; 16]); - let empty_poseidon24_output = build_poseidon24().permute([F::ZERO; 24])[16..24] - .try_into() - .unwrap(); + let empty_poseidon16_output = build_poseidon16().permute([F::ZERO; 16]); + let empty_poseidon24_output = build_poseidon24().permute([F::ZERO; 24])[16..24] + .try_into() + .unwrap(); - poseidons_16.extend( - (0..n_poseidons_16.next_power_of_two() - n_poseidons_16).map(|_| WitnessPoseidon16 { + poseidons_16.resize_with(n_poseidons_16.next_power_of_two(), || WitnessPoseidon16 { cycle: None, addr_input_a: 0, addr_input_b: 0, addr_output: POSEIDON_16_NULL_HASH_PTR, input: [F::ZERO; 16], output: empty_poseidon16_output, - }), - ); - poseidons_24.extend( - (0..n_poseidons_24.next_power_of_two() - n_poseidons_24).map(|_| WitnessPoseidon24 { + }); + + poseidons_24.resize_with(n_poseidons_24.next_power_of_two(), || WitnessPoseidon24 { cycle: None, addr_input_a: 0, addr_input_b: 0, addr_output: POSEIDON_24_NULL_HASH_PTR, input: [F::ZERO; 24], output: empty_poseidon24_output, - }), - ); + }); - ExecutionTrace { - full_trace: trace, - n_poseidons_16, - n_poseidons_24, - poseidons_16, - poseidons_24, - dot_products, - vm_multilinear_evals, - public_memory_size: execution_result.public_memory_size, - memory, + Self { + full_trace: trace, + n_poseidons_16, + n_poseidons_24, + poseidons_16, + poseidons_24, + dot_products, + vm_multilinear_evals, + public_memory_size: execution_result.public_memory_size, + memory, + } } } diff --git a/crates/zk_vm/src/lib.rs b/crates/zk_vm/src/lib.rs index 473afbc2..7c228249 100644 --- a/crates/zk_vm/src/lib.rs +++ b/crates/zk_vm/src/lib.rs @@ -10,6 +10,7 @@ mod instruction_encoder; mod poseidon_tables; pub mod prove_execution; pub mod verify_execution; +pub mod witness; #[cfg(test)] pub mod recursion; diff --git a/crates/zk_vm/src/poseidon_tables.rs b/crates/zk_vm/src/poseidon_tables.rs index a6d3125c..3b3c83c5 100644 --- a/crates/zk_vm/src/poseidon_tables.rs +++ b/crates/zk_vm/src/poseidon_tables.rs @@ -4,7 +4,7 @@ use utils::{ }; use vm::F; -use crate::execution_trace::{WitnessPoseidon16, WitnessPoseidon24}; +use crate::witness::poseidon::{WitnessPoseidon16, WitnessPoseidon24}; pub fn build_poseidon_columns( poseidons_16: &[WitnessPoseidon16], diff --git a/crates/zk_vm/src/prove_execution.rs b/crates/zk_vm/src/prove_execution.rs index 3fed3d92..b3da02cb 100644 --- a/crates/zk_vm/src/prove_execution.rs +++ b/crates/zk_vm/src/prove_execution.rs @@ -35,7 +35,7 @@ use crate::{ }, dot_product_air::{DOT_PRODUCT_AIR_COLUMN_GROUPS, DotProductAir, build_dot_product_columns}, exec_column_groups, - execution_trace::{ExecutionTrace, get_execution_trace}, + execution_trace::ExecutionTrace, poseidon_tables::{all_poseidon_16_indexes, all_poseidon_24_indexes, build_poseidon_columns}, }; @@ -57,7 +57,7 @@ pub fn prove_execution( memory, } = info_span!("Witness generation").in_scope(|| { let execution_result = bytecode.execute(public_input, private_input); - get_execution_trace(bytecode, &execution_result) + ExecutionTrace::new(bytecode, &execution_result) }); let public_memory = &memory[..public_memory_size]; diff --git a/crates/zk_vm/src/witness/dot_product.rs b/crates/zk_vm/src/witness/dot_product.rs new file mode 100644 index 00000000..ce564239 --- /dev/null +++ b/crates/zk_vm/src/witness/dot_product.rs @@ -0,0 +1,22 @@ +use vm::EF; + +/// Holds the high-level witness data for a single dot product precompile execution. +#[derive(Debug)] +pub struct WitnessDotProduct { + /// The CPU cycle at which the dot product operation is initiated. + pub cycle: usize, + /// The starting memory address (vectorized pointer) of the first input slice. + pub addr_0: usize, + /// The starting memory address (vectorized pointer) of the second input slice. + pub addr_1: usize, + /// The memory address (vectorized pointer) where the final result is stored. + pub addr_res: usize, + /// The number of elements in each input slice. + pub len: usize, + /// The actual data values of the first input slice. + pub slice_0: Vec, + /// The actual data values of the second input slice. + pub slice_1: Vec, + /// The final computed result of the dot product. + pub res: EF, +} diff --git a/crates/zk_vm/src/witness/mod.rs b/crates/zk_vm/src/witness/mod.rs new file mode 100644 index 00000000..f56f8e89 --- /dev/null +++ b/crates/zk_vm/src/witness/mod.rs @@ -0,0 +1,16 @@ +use dot_product::WitnessDotProduct; +use multilinear_eval::WitnessMultilinearEval; +use poseidon::{WitnessPoseidon16, WitnessPoseidon24}; + +pub mod dot_product; +pub mod multilinear_eval; +pub mod poseidon; + +/// An enum to encapsulate any possible precompile witness. +#[derive(Debug)] +pub enum Witness { + Poseidon16(WitnessPoseidon16), + Poseidon24(WitnessPoseidon24), + DotProduct(WitnessDotProduct), + MultilinearEval(WitnessMultilinearEval), +} diff --git a/crates/zk_vm/src/witness/multilinear_eval.rs b/crates/zk_vm/src/witness/multilinear_eval.rs new file mode 100644 index 00000000..03dcb6bd --- /dev/null +++ b/crates/zk_vm/src/witness/multilinear_eval.rs @@ -0,0 +1,20 @@ +use vm::EF; + +/// Holds the high-level witness data for a single multilinear evaluation precompile. +#[derive(Debug)] +pub struct WitnessMultilinearEval { + /// The CPU cycle at which this operation is initiated. + pub cycle: usize, + /// The memory address of the polynomial's coefficients. + pub addr_coeffs: usize, + /// The memory address of the evaluation point's coordinates. + pub addr_point: usize, + /// The memory address where the final result is stored. + pub addr_res: usize, + /// The number of variables in the multilinear polynomial. + pub n_vars: usize, + /// The coordinates of the evaluation point. + pub point: Vec, + /// The final computed result of the evaluation. + pub res: EF, +} diff --git a/crates/zk_vm/src/witness/poseidon.rs b/crates/zk_vm/src/witness/poseidon.rs new file mode 100644 index 00000000..47b9a4ce --- /dev/null +++ b/crates/zk_vm/src/witness/poseidon.rs @@ -0,0 +1,35 @@ +use vm::F; + +/// Holds the high-level witness data for a single Poseidon2 permutation over 16 elements. +#[derive(Debug)] +pub struct WitnessPoseidon16 { + /// The CPU cycle at which this operation is initiated, if applicable. + pub cycle: Option, + /// The memory address (vectorized pointer, of size 1) of the first 8-element input vector. + pub addr_input_a: usize, + /// The memory address (vectorized pointer, of size 1) of the second 8-element input vector. + pub addr_input_b: usize, + /// The memory address (vectorized pointer, of size 2) where the two 8-element output vectors are stored. + pub addr_output: usize, + /// The full 16-element input state for the permutation. + pub input: [F; 16], + /// The full 16-element output state resulting from the permutation. + pub output: [F; 16], +} + +/// Holds the high-level witness data for a single Poseidon2 permutation over 24 elements. +#[derive(Debug)] +pub struct WitnessPoseidon24 { + /// The CPU cycle at which this operation is initiated, if applicable. + pub cycle: Option, + /// The memory address (vectorized pointer, of size 2) of the first two 8-element input vectors. + pub addr_input_a: usize, + /// The memory address (vectorized pointer, of size 1) of the third 8-element input vector. + pub addr_input_b: usize, + /// The memory address (vectorized pointer, of size 1) where the relevant 8-element output vector is stored. + pub addr_output: usize, + /// The full 24-element input state for the permutation. + pub input: [F; 24], + /// The last 8 elements of the 24-element output state from the permutation. + pub output: [F; 8], +}