From 522bbf9f6943782fad22a1dd3af061acf0f7fc89 Mon Sep 17 00:00:00 2001 From: TlatoaniHJ Date: Fri, 10 Jan 2025 18:44:09 -0500 Subject: [PATCH 01/50] Constraints for inside level --- Cargo.lock | 2 +- extensions/native/circuit/src/lib.rs | 2 + .../native/circuit/src/verify_batch/mod.rs | 648 ++++++++++++++++++ .../native/circuit/src/verify_batch/tests.rs | 159 +++++ 4 files changed, 810 insertions(+), 1 deletion(-) create mode 100644 extensions/native/circuit/src/verify_batch/mod.rs create mode 100644 extensions/native/circuit/src/verify_batch/tests.rs diff --git a/Cargo.lock b/Cargo.lock index a33b8bae3a..cc9d97f6fe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3709,7 +3709,7 @@ version = "0.1.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.95", ] [[package]] diff --git a/extensions/native/circuit/src/lib.rs b/extensions/native/circuit/src/lib.rs index 46c6bc890f..762fbba1b2 100644 --- a/extensions/native/circuit/src/lib.rs +++ b/extensions/native/circuit/src/lib.rs @@ -9,6 +9,8 @@ mod jal; mod loadstore; mod poseidon2; +mod verify_batch; + pub use branch_eq::*; pub use castf::*; pub use field_arithmetic::*; diff --git a/extensions/native/circuit/src/verify_batch/mod.rs b/extensions/native/circuit/src/verify_batch/mod.rs new file mode 100644 index 0000000000..d39fb775c6 --- /dev/null +++ b/extensions/native/circuit/src/verify_batch/mod.rs @@ -0,0 +1,648 @@ +use std::{ + borrow::{Borrow, BorrowMut}, + sync::{Arc, Mutex}, +}; + +use openvm_circuit::{ + arch::{ExecutionBridge, ExecutionBus, ExecutionError, ExecutionState, InstructionExecutor}, + system::{ + memory::{ + offline_checker::{ + MemoryBaseAuxCols, MemoryBridge, MemoryReadAuxCols, MemoryWriteAuxCols, + }, + MemoryAddress, MemoryAuxColsFactory, MemoryController, OfflineMemory, RecordId, + }, + program::ProgramBus, + }, +}; +use openvm_circuit_primitives::{ + is_zero::{IsZeroIo, IsZeroSubAir}, + utils::{assert_array_eq, next_power_of_two_or_zero, not}, + SubAir, TraceSubRowGenerator, +}; +use openvm_circuit_primitives_derive::AlignedBorrow; +use openvm_instructions::{instruction::Instruction, program::DEFAULT_PC_STEP}; +use openvm_native_compiler::FriOpcode::FRI_REDUCED_OPENING; +use openvm_poseidon2_air::{Poseidon2SubAir, Poseidon2SubCols}; +use openvm_stark_backend::{ + config::{StarkGenericConfig, Val}, + interaction::InteractionBuilder, + p3_air::{Air, AirBuilder, BaseAir}, + p3_field::{Field, FieldAlgebra, PrimeField32}, + p3_matrix::{dense::RowMajorMatrix, Matrix}, + p3_maybe_rayon::prelude::*, + prover::types::AirProofInput, + rap::{AnyRap, BaseAirWithPublicValues, PartitionedBaseAir}, + Chip, ChipUsageGetter, +}; + +use super::field_extension::{FieldExtension, EXT_DEG}; +use crate::NATIVE_POSEIDON2_CHUNK_SIZE; + +#[cfg(test)] +mod tests; + +const CHUNK: usize = NATIVE_POSEIDON2_CHUNK_SIZE; + +#[repr(C)] +#[derive(AlignedBorrow)] +pub struct VerifyBatchCols { + // flags - at most 1 is true, if none is true then row is disabled + pub incorporate_row: T, + pub incorporate_sibling: T, + pub inside_row: T, + + pub end_inside_row: T, + pub end_top_level: T, + + // execution state + pub pc: T, + pub start_timestamp: T, + + // instruction (a, b, c, d, e) + pub dim_register: T, + pub opened_register: T, + pub sibling_register: T, + pub commit_register: T, + pub address_space: T, + + // poseidon2 + pub inner: Poseidon2SubCols, + + pub cells: [VerifyBatchCellCols; CHUNK], + pub initial_opened_index: T, + + pub height: T, + pub opened_length: T, + + pub opened_base_pointer: T, + pub dim_base_pointer: T, + pub sibling_pointer: T, + + pub dim_read: MemoryReadAuxCols, + pub opened_or_sibling_read: MemoryReadAuxCols, + + pub commit_pointer: T, + pub commit_read: MemoryReadAuxCols, +} + +#[repr(C)] +#[derive(AlignedBorrow, Copy, Clone)] +pub struct VerifyBatchCellCols { + pub read: MemoryReadAuxCols, + pub opened_index: T, + pub read_row_pointer: MemoryReadAuxCols, + pub read_row_length: MemoryReadAuxCols, + pub row_pointer: T, + pub row_end: T, + pub is_first_in_row: T, + pub is_exhausted: T, + pub read_height: MemoryReadAuxCols, +} + +#[derive(Clone, Copy, Debug)] +pub struct VerifyBatchBus(usize); + +impl VerifyBatchBus { + pub fn interact( + &self, + builder: &mut AB, + send: bool, + multiplicity: impl Into, + timestamp: impl Into, + height: impl Into, + opened_base_pointer: impl Into, + dim_base_pointer: impl Into, + initial_opened_index: impl Into, + final_opened_index: impl Into, + hash: [impl Into; CHUNK], + ) { + } +} +#[derive(Clone, Debug)] +pub struct VerifyBatchAir { + pub execution_bridge: ExecutionBridge, + pub memory_bridge: MemoryBridge, + pub internal_bus: VerifyBatchBus, + pub(super) subair: Arc>, + offset: usize, + half: F, +} + +impl BaseAir for VerifyBatchAir { + fn width(&self) -> usize { + VerifyBatchCols::::width() + } +} + +impl BaseAirWithPublicValues + for VerifyBatchAir +{ +} +impl PartitionedBaseAir + for VerifyBatchAir +{ +} + +impl Air + for VerifyBatchAir +{ + fn eval(&self, builder: &mut AB) { + let main = builder.main(); + let local = main.row_slice(0); + let local: &VerifyBatchCols = (*local).borrow(); + let next = main.row_slice(1); + let next: &VerifyBatchCols = (*next).borrow(); + + let &VerifyBatchCols { + incorporate_row, + incorporate_sibling, + inside_row, + end_inside_row, + end_top_level, + pc, + start_timestamp, + dim_register, + opened_register, + sibling_register, + commit_register, + address_space, + inner, + cells, + initial_opened_index, + height, + opened_length, + dim_base_pointer, + opened_base_pointer, + sibling_pointer, + dim_read, + opened_or_sibling_read, + commit_pointer, + commit_read, + } = local; + + let left_input = std::array::from_fn::<_, CHUNK, _>(|i| inner.inputs[i]); + let right_input = std::array::from_fn::<_, CHUNK, _>(|i| inner.inputs[i + CHUNK]); + let left_output = + std::array::from_fn::<_, CHUNK, _>(|i| inner.ending_full_rounds[0].post[i]); + let right_output = + std::array::from_fn::<_, CHUNK, _>(|i| inner.ending_full_rounds[0].post[i + CHUNK]); + let next_left_input = std::array::from_fn::<_, CHUNK, _>(|i| next.inner.inputs[i]); + let next_right_input = std::array::from_fn::<_, CHUNK, _>(|i| next.inner.inputs[i + CHUNK]); + + builder.assert_bool(incorporate_row); + builder.assert_bool(incorporate_sibling); + builder.assert_bool(inside_row); + let enabled = incorporate_row + incorporate_sibling + inside_row; + builder.assert_bool(enabled.clone()); + builder.assert_bool(end_inside_row); + builder.when(end_inside_row).assert_one(inside_row); + builder.assert_bool(end_top_level); + builder.when(end_top_level).assert_one(incorporate_row); + + let end = end_inside_row + end_top_level + (AB::Expr::ONE - enabled.clone()); + + self.subair.eval(builder); + + //// inside row constraints + + // start + builder + .when(end.clone()) + .when(next.inside_row) + .assert_eq(next.initial_opened_index, next.cells[0].opened_index); + + // end + self.internal_bus.interact( + builder, + false, + end_inside_row, + start_timestamp + AB::F::from_canonical_usize(4 * CHUNK), + height, + opened_base_pointer, + dim_base_pointer, + initial_opened_index, + cells[CHUNK - 1].opened_index + AB::F::ONE, + left_output, + ); + + // things that stay the same (roughly) + + builder.when(inside_row - end_inside_row).assert_eq( + next.start_timestamp, + start_timestamp + AB::F::from_canonical_usize(4 * CHUNK), + ); + builder + .when(inside_row - end_inside_row) + .assert_eq(next.height, height); + builder + .when(inside_row - end_inside_row) + .assert_eq(next.opened_base_pointer, opened_base_pointer); + builder + .when(inside_row - end_inside_row) + .assert_eq(next.dim_base_pointer, dim_base_pointer); + builder + .when(inside_row - end_inside_row) + .assert_eq(next.initial_opened_index, initial_opened_index); + + // right input + + for i in 0..CHUNK { + builder + .when(end.clone()) + .when(next.inside_row) + .assert_zero(next_right_input[i]); + } + + for i in 0..CHUNK { + builder + .when(inside_row - end_inside_row) + .assert_eq(right_output[i], next_right_input[i]); + } + + // left input + + for i in 0..CHUNK { + let cell = cells[i]; + let next_cell = if i + 1 == CHUNK { + next.cells[0] + } else { + cells[i + 1] + }; + + builder.assert_bool(cell.is_first_in_row); + builder.assert_bool(cell.is_exhausted); + builder.assert_bool(cell.is_first_in_row + cell.is_exhausted); + + let next_is_normal = AB::Expr::ONE - next_cell.is_first_in_row - next_cell.is_exhausted; + self.memory_bridge + .read( + MemoryAddress::new(address_space, cell.row_pointer), + [left_input[i]], + start_timestamp + AB::F::from_canonical_usize((4 * i) + 2), + &cell.read, + ) + .eval(builder, inside_row * not(cell.is_exhausted)); + builder + .when(cell.is_exhausted) + .assert_eq(left_input[i], AB::F::ZERO); + + let mut when_inside_row_not_last = builder.when(inside_row - end_inside_row); + + // update state for normal cell + when_inside_row_not_last + .when(next_is_normal.clone()) + .assert_eq(next_cell.row_pointer, cell.row_pointer + AB::F::ONE); + when_inside_row_not_last + .when(next_is_normal.clone()) + .assert_eq(next_cell.row_end, cell.row_end); + when_inside_row_not_last + .when(AB::Expr::ONE - next_cell.is_first_in_row) + .assert_eq(next_cell.opened_index, cell.opened_index); + + // update state for first in row cell + self.memory_bridge + .read( + MemoryAddress::new( + address_space, + opened_base_pointer + (cell.opened_index * AB::F::TWO), + ), + [cell.row_pointer - AB::F::ONE], + start_timestamp + AB::F::from_canonical_usize(4 * i), + &cell.read_row_pointer, + ) + .eval(builder, inside_row * cell.is_first_in_row); + self.memory_bridge + .read( + MemoryAddress::new( + address_space, + opened_base_pointer + (cell.opened_index * AB::F::TWO) + AB::F::ONE, + ), + [cell.row_end - cell.row_pointer], + start_timestamp + AB::F::from_canonical_usize((4 * i) + 1), + &cell.read_row_length, + ) + .eval(builder, inside_row * cell.is_first_in_row); + let mut when_inside_row_not_last = builder.when(inside_row - end_inside_row); + when_inside_row_not_last + .when(next_cell.is_first_in_row) + .assert_eq(next_cell.opened_index, cell.opened_index + AB::F::ONE); + + when_inside_row_not_last + .when(cell.is_exhausted) + .assert_eq(next_cell.is_exhausted, AB::F::ONE); + + let is_last_in_row = if i == CHUNK - 1 { + end_inside_row.into() + } else { + next_cell.is_first_in_row + next_cell.is_exhausted + }; + builder + .when(inside_row) + .when(is_last_in_row) + .assert_eq(cell.row_pointer, cell.row_end); + + // ensure that height matches + self.memory_bridge + .read( + MemoryAddress::new(address_space, dim_base_pointer + cell.opened_index), + [height], + start_timestamp + AB::F::from_canonical_usize((4 * i) + 3), + &cell.read_height, + ) + .eval(builder, inside_row * cell.is_first_in_row); + } + } +} + +pub struct FriReducedOpeningRecord { + pub pc: F, + pub start_timestamp: F, + pub instruction: Instruction, + pub alpha_read: RecordId, + pub length_read: RecordId, + pub a_ptr_read: RecordId, + pub b_ptr_read: RecordId, + pub a_reads: Vec, + pub b_reads: Vec, + pub alpha_pow_original: [F; EXT_DEG], + pub alpha_pow_write: RecordId, + pub result_write: RecordId, +} + +pub struct FriReducedOpeningChip { + air: VerifyBatchAir, + records: Vec>, + height: usize, + offline_memory: Arc>>, +} + +impl FriReducedOpeningChip { + pub fn new( + execution_bus: ExecutionBus, + program_bus: ProgramBus, + memory_bridge: MemoryBridge, + offset: usize, + offline_memory: Arc>>, + ) -> Self { + let air = VerifyBatchAir { + execution_bridge: ExecutionBridge::new(execution_bus, program_bus), + memory_bridge, + offset, + }; + Self { + records: vec![], + air, + height: 0, + offline_memory, + } + } +} + +fn elem_to_ext(elem: F) -> [F; EXT_DEG] { + let mut ret = [F::ZERO; EXT_DEG]; + ret[0] = elem; + ret +} + +impl InstructionExecutor for FriReducedOpeningChip { + fn execute( + &mut self, + memory: &mut MemoryController, + instruction: &Instruction, + from_state: ExecutionState, + ) -> Result, ExecutionError> { + let &Instruction { + a: a_ptr_ptr, + b: b_ptr_ptr, + c: result_ptr, + d: addr_space, + e: length_ptr, + f: alpha_ptr, + g: alpha_pow_ptr, + .. + } = instruction; + + let alpha_read = memory.read(addr_space, alpha_ptr); + let length_read = memory.read_cell(addr_space, length_ptr); + let a_ptr_read = memory.read_cell(addr_space, a_ptr_ptr); + let b_ptr_read = memory.read_cell(addr_space, b_ptr_ptr); + + let alpha = alpha_read.1; + let alpha_pow_original = from_fn(|i| { + memory.unsafe_read_cell(addr_space, alpha_pow_ptr + F::from_canonical_usize(i)) + }); + let mut alpha_pow = alpha_pow_original; + let length = length_read.1.as_canonical_u32() as usize; + let a_ptr = a_ptr_read.1; + let b_ptr = b_ptr_read.1; + + let mut a_reads = Vec::with_capacity(length); + let mut b_reads = Vec::with_capacity(length); + let mut result = [F::ZERO; EXT_DEG]; + + for i in 0..length { + let a_read = memory.read_cell(addr_space, a_ptr + F::from_canonical_usize(i)); + let b_read = memory.read(addr_space, b_ptr + F::from_canonical_usize(4 * i)); + a_reads.push(a_read); + b_reads.push(b_read); + let a = a_read.1; + let b = b_read.1; + result = FieldExtension::add( + result, + FieldExtension::multiply(FieldExtension::subtract(b, elem_to_ext(a)), alpha_pow), + ); + alpha_pow = FieldExtension::multiply(alpha, alpha_pow); + } + + let (alpha_pow_write, prev_data) = memory.write(addr_space, alpha_pow_ptr, alpha_pow); + debug_assert_eq!(prev_data, alpha_pow_original); + let (result_write, _) = memory.write(addr_space, result_ptr, result); + + self.records.push(FriReducedOpeningRecord { + pc: F::from_canonical_u32(from_state.pc), + start_timestamp: F::from_canonical_u32(from_state.timestamp), + instruction: instruction.clone(), + alpha_read: alpha_read.0, + length_read: length_read.0, + a_ptr_read: a_ptr_read.0, + b_ptr_read: b_ptr_read.0, + a_reads: a_reads.into_iter().map(|r| r.0).collect(), + b_reads: b_reads.into_iter().map(|r| r.0).collect(), + alpha_pow_original, + alpha_pow_write, + result_write, + }); + + self.height += length; + + Ok(ExecutionState { + pc: from_state.pc + DEFAULT_PC_STEP, + timestamp: memory.timestamp(), + }) + } + + fn get_opcode_name(&self, opcode: usize) -> String { + assert_eq!(opcode, (FRI_REDUCED_OPENING as usize) + self.air.offset); + String::from("FRI_REDUCED_OPENING") + } +} + +impl ChipUsageGetter for FriReducedOpeningChip { + fn air_name(&self) -> String { + "FriReducedOpeningAir".to_string() + } + + fn current_trace_height(&self) -> usize { + self.height + } + + fn trace_width(&self) -> usize { + VerifyBatchCols::::width() + } +} + +impl FriReducedOpeningChip { + fn record_to_rows( + record: FriReducedOpeningRecord, + aux_cols_factory: &MemoryAuxColsFactory, + slice: &mut [F], + memory: &OfflineMemory, + ) { + let width = VerifyBatchCols::::width(); + + let Instruction { + a: a_ptr_ptr, + b: b_ptr_ptr, + c: result_ptr, + d: addr_space, + e: length_ptr, + f: alpha_ptr, + g: alpha_pow_ptr, + .. + } = record.instruction; + + let length_read = memory.record_by_id(record.length_read); + let alpha_read = memory.record_by_id(record.alpha_read); + let a_ptr_read = memory.record_by_id(record.a_ptr_read); + let b_ptr_read = memory.record_by_id(record.b_ptr_read); + + let length = length_read.data[0].as_canonical_u32() as usize; + let alpha: [F; EXT_DEG] = array::from_fn(|i| alpha_read.data[i]); + let a_ptr = a_ptr_read.data[0]; + let b_ptr = b_ptr_read.data[0]; + + let mut alpha_pow_current = record.alpha_pow_original; + let mut current = [F::ZERO; EXT_DEG]; + + let alpha_aux = aux_cols_factory.make_read_aux_cols(alpha_read); + let length_aux = aux_cols_factory.make_read_aux_cols(length_read); + let a_ptr_aux = aux_cols_factory.make_read_aux_cols(a_ptr_read); + let b_ptr_aux = aux_cols_factory.make_read_aux_cols(b_ptr_read); + + let alpha_pow_aux = aux_cols_factory + .make_write_aux_cols::(memory.record_by_id(record.alpha_pow_write)) + .get_base(); + let result_aux = + aux_cols_factory.make_write_aux_cols(memory.record_by_id(record.result_write)); + + for i in 0..length { + let a_read = memory.record_by_id(record.a_reads[i]); + let b_read = memory.record_by_id(record.b_reads[i]); + let a = a_read.data[0]; + let b: [F; EXT_DEG] = array::from_fn(|i| b_read.data[i]); + current = FieldExtension::add( + current, + FieldExtension::multiply( + FieldExtension::subtract(b, elem_to_ext(a)), + alpha_pow_current, + ), + ); + + let mut idx_is_zero = F::ZERO; + let mut is_zero_aux = F::ZERO; + + let idx = F::from_canonical_usize(i); + IsZeroSubAir.generate_subrow(idx, (&mut is_zero_aux, &mut idx_is_zero)); + + let cols: &mut VerifyBatchCols = slice[i * width..(i + 1) * width].borrow_mut(); + *cols = VerifyBatchCols { + enabled: F::ONE, + pc: record.pc, + a_ptr_ptr, + b_ptr_ptr, + result_ptr, + addr_space, + length_ptr, + alpha_ptr, + alpha_pow_ptr, + start_timestamp: record.start_timestamp, + a_ptr_aux, + b_ptr_aux, + a_aux: aux_cols_factory.make_read_aux_cols(a_read), + b_aux: aux_cols_factory.make_read_aux_cols(b_read), + alpha_aux, + length_aux, + alpha_pow_aux, + result_aux, + a_ptr, + b_ptr, + a, + b, + alpha, + alpha_pow_original: record.alpha_pow_original, + alpha_pow_current, + idx, + idx_is_zero, + is_zero_aux, + current, + }; + + alpha_pow_current = FieldExtension::multiply(alpha, alpha_pow_current); + } + } + + fn generate_trace(self) -> RowMajorMatrix { + let width = self.trace_width(); + let height = next_power_of_two_or_zero(self.height); + let mut flat_trace = F::zero_vec(width * height); + + let memory = self.offline_memory.lock().unwrap(); + + let aux_cols_factory = memory.aux_cols_factory(); + + let mut idx = 0; + for record in self.records { + let length = record.a_reads.len(); + Self::record_to_rows( + record, + &aux_cols_factory, + &mut flat_trace[idx..idx + (length * width)], + &memory, + ); + idx += length * width; + } + // In padding rows, need idx_is_zero = 1 so IsZero constraints pass, and also because next.idx_is_zero is used + // to determine the last row per instruction, so the last non-padding row needs next.idx_is_zero = 1 + flat_trace[self.height * width..] + .par_chunks_mut(width) + .for_each(|row| { + let row: &mut VerifyBatchCols = row.borrow_mut(); + row.idx_is_zero = F::ONE; + }); + + RowMajorMatrix::new(flat_trace, width) + } +} + +impl Chip for FriReducedOpeningChip> +where + Val: PrimeField32, +{ + fn air(&self) -> Arc> { + Arc::new(self.air) + } + fn generate_air_proof_input(self) -> AirProofInput { + AirProofInput::simple_no_pis(self.air(), self.generate_trace()) + } +} diff --git a/extensions/native/circuit/src/verify_batch/tests.rs b/extensions/native/circuit/src/verify_batch/tests.rs new file mode 100644 index 0000000000..91b40e2f38 --- /dev/null +++ b/extensions/native/circuit/src/verify_batch/tests.rs @@ -0,0 +1,159 @@ +use itertools::Itertools; +use openvm_circuit::arch::testing::{memory::gen_pointer, VmChipTestBuilder}; +use openvm_instructions::{instruction::Instruction, UsizeOpcode, VmOpcode}; +use openvm_native_compiler::FriOpcode::{self, FRI_REDUCED_OPENING}; +use openvm_stark_backend::{ + p3_field::{Field, FieldAlgebra}, + utils::disable_debug_builder, + verifier::VerificationError, +}; +use openvm_stark_sdk::{p3_baby_bear::BabyBear, utils::create_seeded_rng}; +use rand::Rng; + +use super::{ + super::field_extension::FieldExtension, elem_to_ext, FriReducedOpeningChip, VerifyBatchCols, + EXT_DEG, +}; + +fn compute_fri_mat_opening( + alpha: [F; EXT_DEG], + mut alpha_pow: [F; EXT_DEG], + a: &[F], + b: &[[F; EXT_DEG]], +) -> ([F; EXT_DEG], [F; EXT_DEG]) { + let mut result = [F::ZERO; EXT_DEG]; + for (&a, &b) in a.iter().zip_eq(b) { + result = FieldExtension::add( + result, + FieldExtension::multiply(FieldExtension::subtract(b, elem_to_ext(a)), alpha_pow), + ); + alpha_pow = FieldExtension::multiply(alpha, alpha_pow); + } + (alpha_pow, result) +} + +#[test] +fn fri_mat_opening_air_test() { + let num_ops = 3; // non-power-of-2 to also test padding + let elem_range = || 1..=100; + let address_space_range = || 1usize..=2; + let length_range = || 1..=49; + + let offset = FriOpcode::default_offset(); + + let mut tester = VmChipTestBuilder::default(); + let mut chip = FriReducedOpeningChip::new( + tester.execution_bus(), + tester.program_bus(), + tester.memory_bridge(), + offset, + tester.offline_memory_mutex_arc(), + ); + + let mut rng = create_seeded_rng(); + + macro_rules! gen_ext { + () => { + std::array::from_fn::<_, EXT_DEG, _>(|_| { + BabyBear::from_canonical_u32(rng.gen_range(elem_range())) + }) + }; + } + + for _ in 0..num_ops { + let alpha = gen_ext!(); + let length = rng.gen_range(length_range()); + let alpha_pow_initial = gen_ext!(); + let a = (0..length) + .map(|_| BabyBear::from_canonical_u32(rng.gen_range(elem_range()))) + .collect_vec(); + let b = (0..length).map(|_| gen_ext!()).collect_vec(); + + let (alpha_pow_final, result) = compute_fri_mat_opening(alpha, alpha_pow_initial, &a, &b); + + let alpha_pointer = gen_pointer(&mut rng, 4); + let length_pointer = gen_pointer(&mut rng, 1); + let a_pointer_pointer = gen_pointer(&mut rng, 1); + let b_pointer_pointer = gen_pointer(&mut rng, 1); + let alpha_pow_pointer = gen_pointer(&mut rng, 4); + let result_pointer = gen_pointer(&mut rng, 4); + let a_pointer = gen_pointer(&mut rng, 1); + let b_pointer = gen_pointer(&mut rng, 4); + + let address_space = rng.gen_range(address_space_range()); + + /*tracing::debug!( + "{opcode:?} d = {}, e = {}, f = {}, result_addr = {}, addr1 = {}, addr2 = {}, z = {}, x = {}, y = {}", + result_as, as1, as2, result_pointer, address1, address2, result, operand1, operand2, + );*/ + + tester.write(address_space, alpha_pointer, alpha); + tester.write_cell( + address_space, + length_pointer, + BabyBear::from_canonical_usize(length), + ); + tester.write_cell( + address_space, + a_pointer_pointer, + BabyBear::from_canonical_usize(a_pointer), + ); + tester.write_cell( + address_space, + b_pointer_pointer, + BabyBear::from_canonical_usize(b_pointer), + ); + tester.write(address_space, alpha_pow_pointer, alpha_pow_initial); + for i in 0..length { + tester.write_cell(address_space, a_pointer + i, a[i]); + tester.write(address_space, b_pointer + (4 * i), b[i]); + } + + tester.execute( + &mut chip, + &Instruction::from_usize( + VmOpcode::from_usize(FRI_REDUCED_OPENING as usize + offset), + [ + a_pointer_pointer, + b_pointer_pointer, + result_pointer, + address_space, + length_pointer, + alpha_pointer, + alpha_pow_pointer, + ], + ), + ); + assert_eq!( + alpha_pow_final, + tester.read(address_space, alpha_pow_pointer) + ); + assert_eq!(result, tester.read(address_space, result_pointer)); + } + + let mut tester = tester.build().load(chip).finalize(); + tester.simple_test().expect("Verification failed"); + + disable_debug_builder(); + // negative test pranking each value + for height in 0..num_ops { + // TODO: better way to modify existing traces in tester + let trace = tester.air_proof_inputs[2].raw.common_main.as_mut().unwrap(); + let old_trace = trace.clone(); + for width in 0..VerifyBatchCols::::width() + /* num operands */ + { + let prank_value = BabyBear::from_canonical_u32(rng.gen_range(1..=100)); + trace.row_mut(height)[width] = prank_value; + } + + // Run a test after pranking each row + assert_eq!( + tester.simple_test().err(), + Some(VerificationError::OodEvaluationMismatch), + "Expected constraint to fail" + ); + + tester.air_proof_inputs[2].raw.common_main = Some(old_trace); + } +} From ecd112950bbd13122cccd1d02cf3258559a447a8 Mon Sep 17 00:00:00 2001 From: TlatoaniHJ Date: Mon, 13 Jan 2025 10:53:49 -0500 Subject: [PATCH 02/50] Make optimizations to inside row constraints --- .../native/circuit/src/verify_batch/mod.rs | 49 ++++++------------- 1 file changed, 14 insertions(+), 35 deletions(-) diff --git a/extensions/native/circuit/src/verify_batch/mod.rs b/extensions/native/circuit/src/verify_batch/mod.rs index d39fb775c6..7a8cec2c45 100644 --- a/extensions/native/circuit/src/verify_batch/mod.rs +++ b/extensions/native/circuit/src/verify_batch/mod.rs @@ -70,6 +70,7 @@ pub struct VerifyBatchCols { pub inner: Poseidon2SubCols, pub cells: [VerifyBatchCellCols; CHUNK], + // inside row: this is the initial opened index for the entire row hash pub initial_opened_index: T, pub height: T, @@ -79,8 +80,10 @@ pub struct VerifyBatchCols { pub dim_base_pointer: T, pub sibling_pointer: T, - pub dim_read: MemoryReadAuxCols, - pub opened_or_sibling_read: MemoryReadAuxCols, + // can optimize some (9) columns away by merging these with the MemoryReadAuxCols in cells + pub height_read_initial: MemoryReadAuxCols, + pub height_read_final: MemoryReadAuxCols, + pub sibling_read: MemoryReadAuxCols, pub commit_pointer: T, pub commit_read: MemoryReadAuxCols, @@ -91,13 +94,11 @@ pub struct VerifyBatchCols { pub struct VerifyBatchCellCols { pub read: MemoryReadAuxCols, pub opened_index: T, - pub read_row_pointer: MemoryReadAuxCols, - pub read_row_length: MemoryReadAuxCols, + pub read_row_pointer_and_length: MemoryReadAuxCols, pub row_pointer: T, pub row_end: T, pub is_first_in_row: T, pub is_exhausted: T, - pub read_height: MemoryReadAuxCols, } #[derive(Clone, Copy, Debug)] @@ -110,7 +111,6 @@ impl VerifyBatchBus { send: bool, multiplicity: impl Into, timestamp: impl Into, - height: impl Into, opened_base_pointer: impl Into, dim_base_pointer: impl Into, initial_opened_index: impl Into, @@ -175,8 +175,9 @@ impl Air dim_base_pointer, opened_base_pointer, sibling_pointer, - dim_read, - opened_or_sibling_read, + height_read_initial, + height_read_final, + sibling_read, commit_pointer, commit_read, } = local; @@ -218,11 +219,10 @@ impl Air false, end_inside_row, start_timestamp + AB::F::from_canonical_usize(4 * CHUNK), - height, opened_base_pointer, dim_base_pointer, initial_opened_index, - cells[CHUNK - 1].opened_index + AB::F::ONE, + cells[CHUNK - 1].opened_index, left_output, ); @@ -279,7 +279,7 @@ impl Air .read( MemoryAddress::new(address_space, cell.row_pointer), [left_input[i]], - start_timestamp + AB::F::from_canonical_usize((4 * i) + 2), + start_timestamp + AB::F::from_canonical_usize((2 * i) + 1), &cell.read, ) .eval(builder, inside_row * not(cell.is_exhausted)); @@ -307,20 +307,9 @@ impl Air address_space, opened_base_pointer + (cell.opened_index * AB::F::TWO), ), - [cell.row_pointer - AB::F::ONE], - start_timestamp + AB::F::from_canonical_usize(4 * i), - &cell.read_row_pointer, - ) - .eval(builder, inside_row * cell.is_first_in_row); - self.memory_bridge - .read( - MemoryAddress::new( - address_space, - opened_base_pointer + (cell.opened_index * AB::F::TWO) + AB::F::ONE, - ), - [cell.row_end - cell.row_pointer], - start_timestamp + AB::F::from_canonical_usize((4 * i) + 1), - &cell.read_row_length, + [cell.row_pointer.into(), cell.row_end - cell.row_pointer], + start_timestamp + AB::F::from_canonical_usize(2 * i), + &cell.read_row_pointer_and_length, ) .eval(builder, inside_row * cell.is_first_in_row); let mut when_inside_row_not_last = builder.when(inside_row - end_inside_row); @@ -341,16 +330,6 @@ impl Air .when(inside_row) .when(is_last_in_row) .assert_eq(cell.row_pointer, cell.row_end); - - // ensure that height matches - self.memory_bridge - .read( - MemoryAddress::new(address_space, dim_base_pointer + cell.opened_index), - [height], - start_timestamp + AB::F::from_canonical_usize((4 * i) + 3), - &cell.read_height, - ) - .eval(builder, inside_row * cell.is_first_in_row); } } } From dc3a36ea4f61cb0c7c4ad29ab40412fff0bd021f Mon Sep 17 00:00:00 2001 From: TlatoaniHJ Date: Mon, 13 Jan 2025 12:57:07 -0500 Subject: [PATCH 03/50] Finish all constraints --- .../native/circuit/src/verify_batch/mod.rs | 291 ++++++++++++++++-- 1 file changed, 268 insertions(+), 23 deletions(-) diff --git a/extensions/native/circuit/src/verify_batch/mod.rs b/extensions/native/circuit/src/verify_batch/mod.rs index 7a8cec2c45..a4aeae8e04 100644 --- a/extensions/native/circuit/src/verify_batch/mod.rs +++ b/extensions/native/circuit/src/verify_batch/mod.rs @@ -23,7 +23,7 @@ use openvm_circuit_primitives::{ use openvm_circuit_primitives_derive::AlignedBorrow; use openvm_instructions::{instruction::Instruction, program::DEFAULT_PC_STEP}; use openvm_native_compiler::FriOpcode::FRI_REDUCED_OPENING; -use openvm_poseidon2_air::{Poseidon2SubAir, Poseidon2SubCols}; +use openvm_poseidon2_air::{BABY_BEAR_POSEIDON2_HALF_FULL_ROUNDS, Poseidon2SubAir, Poseidon2SubCols}; use openvm_stark_backend::{ config::{StarkGenericConfig, Val}, interaction::InteractionBuilder, @@ -54,15 +54,19 @@ pub struct VerifyBatchCols { pub end_inside_row: T, pub end_top_level: T, + pub start: T, // execution state pub pc: T, + pub very_first_timestamp: T, pub start_timestamp: T, + pub end_timestamp: T, // instruction (a, b, c, d, e) pub dim_register: T, pub opened_register: T, pub sibling_register: T, + pub index_register: T, pub commit_register: T, pub address_space: T, @@ -70,20 +74,32 @@ pub struct VerifyBatchCols { pub inner: Poseidon2SubCols, pub cells: [VerifyBatchCellCols; CHUNK], - // inside row: this is the initial opened index for the entire row hash + // initial/final opened index for a subsegment with same height + // initial is used in both, final is used only in top level pub initial_opened_index: T, + pub final_opened_index: T, pub height: T, pub opened_length: T, - pub opened_base_pointer: T, pub dim_base_pointer: T, - pub sibling_pointer: T, - - // can optimize some (9) columns away by merging these with the MemoryReadAuxCols in cells - pub height_read_initial: MemoryReadAuxCols, - pub height_read_final: MemoryReadAuxCols, - pub sibling_read: MemoryReadAuxCols, + pub opened_base_pointer: T, + pub sibling_base_pointer: T, + pub index_base_pointer: T, + + pub dim_base_pointer_read: MemoryReadAuxCols, + pub opened_base_pointer_and_length_read: MemoryReadAuxCols, + pub sibling_base_pointer_read: MemoryReadAuxCols, + pub index_base_pointer_read: MemoryReadAuxCols, + pub commit_pointer_read: MemoryReadAuxCols, + + pub proof_index: T, + + pub read_initial_height_or_root_is_on_right: MemoryReadAuxCols, + pub read_final_height_or_sibling_array_start: MemoryReadAuxCols, + + pub root_is_on_right: T, + pub sibling_array_start: T, pub commit_pointer: T, pub commit_read: MemoryReadAuxCols, @@ -110,9 +126,9 @@ impl VerifyBatchBus { builder: &mut AB, send: bool, multiplicity: impl Into, - timestamp: impl Into, + start_timestamp: impl Into, + end_timestamp: impl Into, opened_base_pointer: impl Into, - dim_base_pointer: impl Into, initial_opened_index: impl Into, final_opened_index: impl Into, hash: [impl Into; CHUNK], @@ -160,36 +176,53 @@ impl Air inside_row, end_inside_row, end_top_level, + start, pc, + very_first_timestamp, start_timestamp, + end_timestamp, dim_register, opened_register, sibling_register, + index_register, commit_register, address_space, inner, cells, initial_opened_index, + final_opened_index, height, opened_length, dim_base_pointer, opened_base_pointer, - sibling_pointer, - height_read_initial, - height_read_final, - sibling_read, + sibling_base_pointer, + index_base_pointer, + read_initial_height_or_root_is_on_right, + read_final_height_or_sibling_array_start, + dim_base_pointer_read, + opened_base_pointer_and_length_read, + sibling_base_pointer_read, + index_base_pointer_read, + commit_pointer_read, + root_is_on_right, commit_pointer, commit_read, + proof_index, + sibling_array_start, } = local; let left_input = std::array::from_fn::<_, CHUNK, _>(|i| inner.inputs[i]); let right_input = std::array::from_fn::<_, CHUNK, _>(|i| inner.inputs[i + CHUNK]); let left_output = - std::array::from_fn::<_, CHUNK, _>(|i| inner.ending_full_rounds[0].post[i]); + std::array::from_fn::<_, CHUNK, _>(|i| inner.ending_full_rounds[BABY_BEAR_POSEIDON2_HALF_FULL_ROUNDS - 1].post[i]); let right_output = - std::array::from_fn::<_, CHUNK, _>(|i| inner.ending_full_rounds[0].post[i + CHUNK]); + std::array::from_fn::<_, CHUNK, _>(|i| inner.ending_full_rounds[BABY_BEAR_POSEIDON2_HALF_FULL_ROUNDS - 1].post[i + CHUNK]); let next_left_input = std::array::from_fn::<_, CHUNK, _>(|i| next.inner.inputs[i]); let next_right_input = std::array::from_fn::<_, CHUNK, _>(|i| next.inner.inputs[i + CHUNK]); + let next_left_output = + std::array::from_fn::<_, CHUNK, _>(|i| next.inner.ending_full_rounds[BABY_BEAR_POSEIDON2_HALF_FULL_ROUNDS - 1].post[i]); + let next_right_output = + std::array::from_fn::<_, CHUNK, _>(|i| next.inner.ending_full_rounds[BABY_BEAR_POSEIDON2_HALF_FULL_ROUNDS - 1].post[i + CHUNK]); builder.assert_bool(incorporate_row); builder.assert_bool(incorporate_sibling); @@ -202,6 +235,8 @@ impl Air builder.when(end_top_level).assert_one(incorporate_row); let end = end_inside_row + end_top_level + (AB::Expr::ONE - enabled.clone()); + builder.assert_eq(next.start, end.clone()); + builder.when(end.clone()).assert_zero(next.incorporate_sibling); self.subair.eval(builder); @@ -212,15 +247,19 @@ impl Air .when(end.clone()) .when(next.inside_row) .assert_eq(next.initial_opened_index, next.cells[0].opened_index); + builder + .when(end.clone()) + .when(next.inside_row) + .assert_eq(next.very_first_timestamp, next.start_timestamp); // end self.internal_bus.interact( builder, false, end_inside_row, - start_timestamp + AB::F::from_canonical_usize(4 * CHUNK), + very_first_timestamp, + start_timestamp + AB::F::from_canonical_usize(2 * CHUNK), opened_base_pointer, - dim_base_pointer, initial_opened_index, cells[CHUNK - 1].opened_index, left_output, @@ -230,11 +269,8 @@ impl Air builder.when(inside_row - end_inside_row).assert_eq( next.start_timestamp, - start_timestamp + AB::F::from_canonical_usize(4 * CHUNK), + start_timestamp + AB::F::from_canonical_usize(2 * CHUNK), ); - builder - .when(inside_row - end_inside_row) - .assert_eq(next.height, height); builder .when(inside_row - end_inside_row) .assert_eq(next.opened_base_pointer, opened_base_pointer); @@ -244,6 +280,9 @@ impl Air builder .when(inside_row - end_inside_row) .assert_eq(next.initial_opened_index, initial_opened_index); + builder + .when(inside_row - end_inside_row) + .assert_eq(next.very_first_timestamp, very_first_timestamp); // right input @@ -331,6 +370,212 @@ impl Air .when(is_last_in_row) .assert_eq(cell.row_pointer, cell.row_end); } + + //// top level constraints + + builder.when(end.clone()).when(next.incorporate_row + next.incorporate_sibling).assert_eq(next.proof_index, AB::F::ZERO); + + let timestamp_after_end_operations = start_timestamp + AB::F::from_canonical_usize(5 + 1); + + builder + .when(end.clone()) + .when(next.incorporate_row) + .assert_eq(next.initial_opened_index, AB::F::ZERO); + self.execution_bridge.execute_and_increment_pc( + AB::Expr::from_canonical_usize(FRI_REDUCED_OPENING as usize + self.offset), + [dim_register, opened_register, sibling_register, index_register, commit_register, address_space], + ExecutionState::new(pc, very_first_timestamp), + end_timestamp - very_first_timestamp, + ).eval(builder, end_top_level); + + self.memory_bridge + .read( + MemoryAddress::new( + address_space, + dim_register, + ), + [dim_base_pointer], + start_timestamp, + &dim_base_pointer_read, + ) + .eval(builder, end_top_level); + self.memory_bridge + .read( + MemoryAddress::new( + address_space, + opened_register, + ), + [opened_base_pointer, opened_length], + start_timestamp + AB::F::ONE, + &opened_base_pointer_and_length_read, + ) + .eval(builder, end_top_level); + self.memory_bridge + .read( + MemoryAddress::new( + address_space, + sibling_register, + ), + [sibling_base_pointer], + start_timestamp + AB::F::TWO, + &sibling_base_pointer_read, + ) + .eval(builder, end_top_level); + self.memory_bridge + .read( + MemoryAddress::new( + address_space, + index_register, + ), + [index_base_pointer], + start_timestamp + AB::F::from_canonical_usize(3), + &index_base_pointer_read, + ) + .eval(builder, end_top_level); + self.memory_bridge + .read( + MemoryAddress::new( + address_space, + commit_register, + ), + [commit_pointer], + start_timestamp + AB::F::from_canonical_usize(4), + &commit_pointer_read + ) + .eval(builder, end_top_level); + + self.memory_bridge + .read( + MemoryAddress::new( + address_space, + commit_pointer, + ), + left_output, + start_timestamp + AB::F::from_canonical_usize(5), + &commit_read, + ) + .eval(builder, end_top_level); + + let mut when_top_level_not_end = builder + .when(incorporate_row + incorporate_sibling - end_top_level); + + when_top_level_not_end + .assert_eq(next.dim_base_pointer, dim_base_pointer); + when_top_level_not_end + .assert_eq(next.opened_base_pointer, opened_base_pointer); + when_top_level_not_end + .assert_eq(next.sibling_base_pointer, sibling_base_pointer); + when_top_level_not_end + .assert_eq(next.index_base_pointer, index_base_pointer); + when_top_level_not_end + .assert_eq(next.start_timestamp, end_timestamp); + when_top_level_not_end + .assert_eq(next.opened_length, opened_length); + when_top_level_not_end + .assert_eq(next.initial_opened_index, final_opened_index + AB::F::ONE); + + builder.when(incorporate_sibling - end_top_level).assert_eq(next.height * AB::F::TWO, height); + builder.when(incorporate_row - end_top_level).assert_eq(next.height, height); + builder.when(incorporate_sibling - end_top_level).assert_eq(next.proof_index, proof_index + AB::F::ONE); + builder.when(incorporate_row - end_top_level).assert_eq(next.proof_index, proof_index); + + builder.when(end_top_level).assert_eq(height, AB::F::ONE); + + // incorporate row + + builder.when(incorporate_row - end_top_level).assert_one(next.incorporate_sibling); + + let row_hash = std::array::from_fn(|i| (start * left_output[i]) + ((AB::Expr::ONE - start) * right_input[i])); + + self.internal_bus.interact( + builder, + true, + incorporate_row, + timestamp_after_end_operations.clone(), + end_timestamp - AB::F::TWO, + opened_base_pointer, + initial_opened_index, + final_opened_index, + row_hash, + ); + + for i in 0..CHUNK { + builder.when(AB::Expr::ONE - end.clone()).when(next.incorporate_row).assert_eq(next_left_input[i], left_output[i]); + } + + builder.when(end_top_level).when(incorporate_row).assert_eq(final_opened_index, opened_length - AB::F::ONE); + + self.memory_bridge + .read( + MemoryAddress::new( + address_space, + dim_base_pointer + initial_opened_index, + ), + [height], + end_timestamp - AB::F::TWO, + &read_initial_height_or_root_is_on_right, + ) + .eval(builder, incorporate_row); + self.memory_bridge + .read( + MemoryAddress::new( + address_space, + dim_base_pointer + final_opened_index, + ), + [height], + end_timestamp - AB::F::ONE, + &read_initial_height_or_root_is_on_right, + ) + .eval(builder, incorporate_row); + + // incorporate sibling + + builder.when(incorporate_sibling - end_top_level).assert_one(next.incorporate_row + next.incorporate_sibling); + builder.when(end_top_level).when(incorporate_sibling).assert_eq(initial_opened_index, opened_length); + + builder.when(incorporate_sibling).assert_eq(final_opened_index + AB::F::ONE, initial_opened_index); + + self.memory_bridge + .read( + MemoryAddress::new( + address_space, + index_base_pointer + proof_index, + ), + [root_is_on_right], + timestamp_after_end_operations.clone(), + &read_initial_height_or_root_is_on_right, + ) + .eval(builder, incorporate_row); + self.memory_bridge + .read( + MemoryAddress::new( + address_space, + sibling_base_pointer + proof_index, + ), + [sibling_array_start], + timestamp_after_end_operations.clone() + AB::F::ONE, + &read_final_height_or_sibling_array_start + ) + .eval(builder, incorporate_row); + + for i in 0..CHUNK { + builder.when(next.incorporate_sibling).when(next.root_is_on_right).assert_eq(next_right_input[i], left_output[i]); + builder.when(next.incorporate_sibling).when(AB::Expr::ONE - next.root_is_on_right).assert_eq(next_left_input[i], left_output[i]); + + self.memory_bridge + .read( + MemoryAddress::new( + address_space, + sibling_array_start + proof_index, + ), + [(root_is_on_right * left_input[i]) + ((AB::Expr::ONE - root_is_on_right) * right_input[i])], + timestamp_after_end_operations.clone() + AB::F::from_canonical_usize(2 + i), + &read_initial_height_or_root_is_on_right, + ) + .eval(builder, incorporate_row); + } + + builder.assert_eq(end_timestamp, timestamp_after_end_operations + AB::F::from_canonical_usize(2 + CHUNK)); } } From b6b1fb175845313744fea025cdd030fdf5b18cad Mon Sep 17 00:00:00 2001 From: TlatoaniHJ Date: Mon, 13 Jan 2025 14:23:31 -0500 Subject: [PATCH 04/50] Make records and split out into files --- .../native/circuit/src/verify_batch/air.rs | 474 ++++++++++++ .../native/circuit/src/verify_batch/chip.rs | 177 +++++ .../circuit/src/verify_batch/columns.rs | 77 ++ .../native/circuit/src/verify_batch/mod.rs | 715 +----------------- .../native/circuit/src/verify_batch/tests.rs | 8 +- 5 files changed, 753 insertions(+), 698 deletions(-) create mode 100644 extensions/native/circuit/src/verify_batch/air.rs create mode 100644 extensions/native/circuit/src/verify_batch/chip.rs create mode 100644 extensions/native/circuit/src/verify_batch/columns.rs diff --git a/extensions/native/circuit/src/verify_batch/air.rs b/extensions/native/circuit/src/verify_batch/air.rs new file mode 100644 index 0000000000..009e2cb118 --- /dev/null +++ b/extensions/native/circuit/src/verify_batch/air.rs @@ -0,0 +1,474 @@ +use openvm_stark_backend::p3_field::{Field, FieldAlgebra}; +use openvm_circuit::arch::{ExecutionBridge, ExecutionState}; +use openvm_circuit::system::memory::offline_checker::MemoryBridge; +use std::sync::Arc; +use openvm_poseidon2_air::{BABY_BEAR_POSEIDON2_HALF_FULL_ROUNDS, Poseidon2SubAir}; +use openvm_stark_backend::rap::{BaseAirWithPublicValues, PartitionedBaseAir}; +use openvm_stark_backend::p3_air::{Air, AirBuilder, BaseAir}; +use openvm_stark_backend::interaction::InteractionBuilder; +use openvm_circuit::system::memory::MemoryAddress; +use openvm_circuit_primitives::utils::not; +use openvm_native_compiler::FriOpcode::FRI_REDUCED_OPENING; +use openvm_stark_backend::p3_matrix::Matrix; +use std::borrow::Borrow; +use crate::verify_batch::CHUNK; +use crate::verify_batch::columns::VerifyBatchCols; + +#[derive(Clone, Debug)] +pub struct VerifyBatchAir { + pub execution_bridge: ExecutionBridge, + pub memory_bridge: MemoryBridge, + pub internal_bus: VerifyBatchBus, + pub(crate) subair: Arc>, + pub(super) offset: usize, +} + +impl BaseAir for VerifyBatchAir { + fn width(&self) -> usize { + VerifyBatchCols::::width() + } +} + +impl BaseAirWithPublicValues + for VerifyBatchAir +{ +} + +impl PartitionedBaseAir + for VerifyBatchAir +{ +} + +impl Air + for VerifyBatchAir +{ + fn eval(&self, builder: &mut AB) { + let main = builder.main(); + let local = main.row_slice(0); + let local: &VerifyBatchCols = (*local).borrow(); + let next = main.row_slice(1); + let next: &VerifyBatchCols = (*next).borrow(); + + let &VerifyBatchCols { + incorporate_row, + incorporate_sibling, + inside_row, + end_inside_row, + end_top_level, + start, + pc, + very_first_timestamp, + start_timestamp, + end_timestamp, + dim_register, + opened_register, + sibling_register, + index_register, + commit_register, + address_space, + inner, + cells, + initial_opened_index, + final_opened_index, + height, + opened_length, + dim_base_pointer, + opened_base_pointer, + sibling_base_pointer, + index_base_pointer, + read_initial_height_or_root_is_on_right, + read_final_height_or_sibling_array_start, + dim_base_pointer_read, + opened_base_pointer_and_length_read, + sibling_base_pointer_read, + index_base_pointer_read, + commit_pointer_read, + root_is_on_right, + commit_pointer, + commit_read, + proof_index, + sibling_array_start, + } = local; + + let left_input = std::array::from_fn::<_, CHUNK, _>(|i| inner.inputs[i]); + let right_input = std::array::from_fn::<_, CHUNK, _>(|i| inner.inputs[i + CHUNK]); + let left_output = + std::array::from_fn::<_, CHUNK, _>(|i| inner.ending_full_rounds[BABY_BEAR_POSEIDON2_HALF_FULL_ROUNDS - 1].post[i]); + let right_output = + std::array::from_fn::<_, CHUNK, _>(|i| inner.ending_full_rounds[BABY_BEAR_POSEIDON2_HALF_FULL_ROUNDS - 1].post[i + CHUNK]); + let next_left_input = std::array::from_fn::<_, CHUNK, _>(|i| next.inner.inputs[i]); + let next_right_input = std::array::from_fn::<_, CHUNK, _>(|i| next.inner.inputs[i + CHUNK]); + + builder.assert_bool(incorporate_row); + builder.assert_bool(incorporate_sibling); + builder.assert_bool(inside_row); + let enabled = incorporate_row + incorporate_sibling + inside_row; + builder.assert_bool(enabled.clone()); + builder.assert_bool(end_inside_row); + builder.when(end_inside_row).assert_one(inside_row); + builder.assert_bool(end_top_level); + builder.when(end_top_level).assert_one(incorporate_sibling); + + let end = end_inside_row + end_top_level + (AB::Expr::ONE - enabled.clone()); + builder.assert_eq(next.start, end.clone()); + builder.when(end.clone()).assert_zero(next.incorporate_sibling); + + self.subair.eval(builder); + + //// inside row constraints + + // start + builder + .when(end.clone()) + .when(next.inside_row) + .assert_eq(next.initial_opened_index, next.cells[0].opened_index); + builder + .when(end.clone()) + .when(next.inside_row) + .assert_eq(next.very_first_timestamp, next.start_timestamp); + + // end + self.internal_bus.interact( + builder, + false, + end_inside_row, + very_first_timestamp, + start_timestamp + AB::F::from_canonical_usize(2 * CHUNK), + opened_base_pointer, + initial_opened_index, + cells[CHUNK - 1].opened_index, + left_output, + ); + + // things that stay the same (roughly) + + builder.when(inside_row - end_inside_row).assert_eq( + next.start_timestamp, + start_timestamp + AB::F::from_canonical_usize(2 * CHUNK), + ); + builder + .when(inside_row - end_inside_row) + .assert_eq(next.opened_base_pointer, opened_base_pointer); + builder + .when(inside_row - end_inside_row) + .assert_eq(next.dim_base_pointer, dim_base_pointer); + builder + .when(inside_row - end_inside_row) + .assert_eq(next.initial_opened_index, initial_opened_index); + builder + .when(inside_row - end_inside_row) + .assert_eq(next.very_first_timestamp, very_first_timestamp); + + // right input + + for i in 0..CHUNK { + builder + .when(end.clone()) + .when(next.inside_row) + .assert_zero(next_right_input[i]); + } + + for i in 0..CHUNK { + builder + .when(inside_row - end_inside_row) + .assert_eq(right_output[i], next_right_input[i]); + } + + // left input + + for i in 0..CHUNK { + let cell = cells[i]; + let next_cell = if i + 1 == CHUNK { + next.cells[0] + } else { + cells[i + 1] + }; + + builder.assert_bool(cell.is_first_in_row); + builder.assert_bool(cell.is_exhausted); + builder.assert_bool(cell.is_first_in_row + cell.is_exhausted); + + let next_is_normal = AB::Expr::ONE - next_cell.is_first_in_row - next_cell.is_exhausted; + self.memory_bridge + .read( + MemoryAddress::new(address_space, cell.row_pointer), + [left_input[i]], + start_timestamp + AB::F::from_canonical_usize((2 * i) + 1), + &cell.read, + ) + .eval(builder, inside_row * not(cell.is_exhausted)); + builder + .when(cell.is_exhausted) + .assert_eq(left_input[i], AB::F::ZERO); + + let mut when_inside_row_not_last = builder.when(inside_row - end_inside_row); + + // update state for normal cell + when_inside_row_not_last + .when(next_is_normal.clone()) + .assert_eq(next_cell.row_pointer, cell.row_pointer + AB::F::ONE); + when_inside_row_not_last + .when(next_is_normal.clone()) + .assert_eq(next_cell.row_end, cell.row_end); + when_inside_row_not_last + .when(AB::Expr::ONE - next_cell.is_first_in_row) + .assert_eq(next_cell.opened_index, cell.opened_index); + + // update state for first in row cell + self.memory_bridge + .read( + MemoryAddress::new( + address_space, + opened_base_pointer + (cell.opened_index * AB::F::TWO), + ), + [cell.row_pointer.into(), cell.row_end - cell.row_pointer], + start_timestamp + AB::F::from_canonical_usize(2 * i), + &cell.read_row_pointer_and_length, + ) + .eval(builder, inside_row * cell.is_first_in_row); + let mut when_inside_row_not_last = builder.when(inside_row - end_inside_row); + when_inside_row_not_last + .when(next_cell.is_first_in_row) + .assert_eq(next_cell.opened_index, cell.opened_index + AB::F::ONE); + + when_inside_row_not_last + .when(cell.is_exhausted) + .assert_eq(next_cell.is_exhausted, AB::F::ONE); + + let is_last_in_row = if i == CHUNK - 1 { + end_inside_row.into() + } else { + next_cell.is_first_in_row + next_cell.is_exhausted + }; + builder + .when(inside_row) + .when(is_last_in_row) + .assert_eq(cell.row_pointer, cell.row_end); + } + + //// top level constraints + + builder.when(end.clone()).when(next.incorporate_row + next.incorporate_sibling).assert_eq(next.proof_index, AB::F::ZERO); + + let timestamp_after_end_operations = start_timestamp + AB::F::from_canonical_usize(5 + 1); + + builder + .when(end.clone()) + .when(next.incorporate_row) + .assert_eq(next.initial_opened_index, AB::F::ZERO); + self.execution_bridge.execute_and_increment_pc( + AB::Expr::from_canonical_usize(FRI_REDUCED_OPENING as usize + self.offset), + [dim_register, opened_register, sibling_register, index_register, commit_register, address_space], + ExecutionState::new(pc, very_first_timestamp), + end_timestamp - very_first_timestamp, + ).eval(builder, end_top_level); + + self.memory_bridge + .read( + MemoryAddress::new( + address_space, + dim_register, + ), + [dim_base_pointer], + start_timestamp, + &dim_base_pointer_read, + ) + .eval(builder, end_top_level); + self.memory_bridge + .read( + MemoryAddress::new( + address_space, + opened_register, + ), + [opened_base_pointer, opened_length], + start_timestamp + AB::F::ONE, + &opened_base_pointer_and_length_read, + ) + .eval(builder, end_top_level); + self.memory_bridge + .read( + MemoryAddress::new( + address_space, + sibling_register, + ), + [sibling_base_pointer], + start_timestamp + AB::F::TWO, + &sibling_base_pointer_read, + ) + .eval(builder, end_top_level); + self.memory_bridge + .read( + MemoryAddress::new( + address_space, + index_register, + ), + [index_base_pointer], + start_timestamp + AB::F::from_canonical_usize(3), + &index_base_pointer_read, + ) + .eval(builder, end_top_level); + self.memory_bridge + .read( + MemoryAddress::new( + address_space, + commit_register, + ), + [commit_pointer], + start_timestamp + AB::F::from_canonical_usize(4), + &commit_pointer_read + ) + .eval(builder, end_top_level); + + self.memory_bridge + .read( + MemoryAddress::new( + address_space, + commit_pointer, + ), + left_output, + start_timestamp + AB::F::from_canonical_usize(5), + &commit_read, + ) + .eval(builder, end_top_level); + + let mut when_top_level_not_end = builder + .when(incorporate_row + incorporate_sibling - end_top_level); + + when_top_level_not_end + .assert_eq(next.dim_base_pointer, dim_base_pointer); + when_top_level_not_end + .assert_eq(next.opened_base_pointer, opened_base_pointer); + when_top_level_not_end + .assert_eq(next.sibling_base_pointer, sibling_base_pointer); + when_top_level_not_end + .assert_eq(next.index_base_pointer, index_base_pointer); + when_top_level_not_end + .assert_eq(next.start_timestamp, end_timestamp); + when_top_level_not_end + .assert_eq(next.opened_length, opened_length); + when_top_level_not_end + .assert_eq(next.initial_opened_index, final_opened_index + AB::F::ONE); + + builder.when(incorporate_sibling - end_top_level).assert_eq(next.height * AB::F::TWO, height); + builder.when(incorporate_row - end_top_level).assert_eq(next.height, height); + builder.when(incorporate_sibling - end_top_level).assert_eq(next.proof_index, proof_index + AB::F::ONE); + builder.when(incorporate_row - end_top_level).assert_eq(next.proof_index, proof_index); + + builder.when(end_top_level).assert_eq(height, AB::F::ONE); + + // incorporate row + + builder.when(incorporate_row - end_top_level).assert_one(next.incorporate_sibling); + + let row_hash = std::array::from_fn(|i| (start * left_output[i]) + ((AB::Expr::ONE - start) * right_input[i])); + + self.internal_bus.interact( + builder, + true, + incorporate_row, + timestamp_after_end_operations.clone(), + end_timestamp - AB::F::TWO, + opened_base_pointer, + initial_opened_index, + final_opened_index, + row_hash, + ); + + for i in 0..CHUNK { + builder.when(AB::Expr::ONE - end.clone()).when(next.incorporate_row).assert_eq(next_left_input[i], left_output[i]); + } + + builder.when(end_top_level).when(incorporate_row).assert_eq(final_opened_index, opened_length - AB::F::ONE); + + self.memory_bridge + .read( + MemoryAddress::new( + address_space, + dim_base_pointer + initial_opened_index, + ), + [height], + end_timestamp - AB::F::TWO, + &read_initial_height_or_root_is_on_right, + ) + .eval(builder, incorporate_row); + self.memory_bridge + .read( + MemoryAddress::new( + address_space, + dim_base_pointer + final_opened_index, + ), + [height], + end_timestamp - AB::F::ONE, + &read_initial_height_or_root_is_on_right, + ) + .eval(builder, incorporate_row); + + // incorporate sibling + + builder.when(incorporate_sibling - end_top_level).assert_one(next.incorporate_row + next.incorporate_sibling); + builder.when(end_top_level).when(incorporate_sibling).assert_eq(initial_opened_index, opened_length); + + builder.when(incorporate_sibling).assert_eq(final_opened_index + AB::F::ONE, initial_opened_index); + + self.memory_bridge + .read( + MemoryAddress::new( + address_space, + index_base_pointer + proof_index, + ), + [root_is_on_right], + timestamp_after_end_operations.clone(), + &read_initial_height_or_root_is_on_right, + ) + .eval(builder, incorporate_row); + self.memory_bridge + .read( + MemoryAddress::new( + address_space, + sibling_base_pointer + proof_index, + ), + [sibling_array_start], + timestamp_after_end_operations.clone() + AB::F::ONE, + &read_final_height_or_sibling_array_start + ) + .eval(builder, incorporate_row); + + for i in 0..CHUNK { + builder.when(next.incorporate_sibling).when(next.root_is_on_right).assert_eq(next_right_input[i], left_output[i]); + builder.when(next.incorporate_sibling).when(AB::Expr::ONE - next.root_is_on_right).assert_eq(next_left_input[i], left_output[i]); + + self.memory_bridge + .read( + MemoryAddress::new( + address_space, + sibling_array_start + proof_index, + ), + [(root_is_on_right * left_input[i]) + ((AB::Expr::ONE - root_is_on_right) * right_input[i])], + timestamp_after_end_operations.clone() + AB::F::from_canonical_usize(2 + i), + &read_initial_height_or_root_is_on_right, + ) + .eval(builder, incorporate_row); + } + + builder.assert_eq(end_timestamp, timestamp_after_end_operations + AB::F::from_canonical_usize(2 + CHUNK)); + } +} + +impl VerifyBatchBus { + pub fn interact( + &self, + builder: &mut AB, + send: bool, + multiplicity: impl Into, + start_timestamp: impl Into, + end_timestamp: impl Into, + opened_base_pointer: impl Into, + initial_opened_index: impl Into, + final_opened_index: impl Into, + hash: [impl Into; CHUNK], + ) { + } +} + +#[derive(Clone, Copy, Debug)] +pub struct VerifyBatchBus(pub usize); \ No newline at end of file diff --git a/extensions/native/circuit/src/verify_batch/chip.rs b/extensions/native/circuit/src/verify_batch/chip.rs new file mode 100644 index 0000000000..170852f02b --- /dev/null +++ b/extensions/native/circuit/src/verify_batch/chip.rs @@ -0,0 +1,177 @@ +use openvm_stark_backend::p3_field::{Field, FieldAlgebra, PrimeField32}; +use openvm_circuit::arch::{ExecutionBridge, ExecutionBus, ExecutionError, ExecutionState, InstructionExecutor}; +use openvm_circuit::system::memory::offline_checker::MemoryBridge; +use openvm_circuit::system::memory::{MemoryController, OfflineMemory, RecordId}; +use openvm_circuit::system::program::ProgramBus; +use openvm_instructions::instruction::Instruction; +use std::sync::{Arc, Mutex}; +use openvm_instructions::program::DEFAULT_PC_STEP; +use openvm_native_compiler::FriOpcode::FRI_REDUCED_OPENING; +use openvm_poseidon2_air::{Poseidon2Config, Poseidon2SubAir}; +use crate::{EXT_DEG, FieldExtension}; +use crate::verify_batch::air::{VerifyBatchAir, VerifyBatchBus}; +use crate::verify_batch::CHUNK; + +pub struct VerifyBatchRecord { + initial_state: ExecutionState, + instruction: Instruction, + dim_base_pointer: usize, + opened_base_pointer: usize, + opened_length: usize, + sibling_base_pointer: usize, + index_base_pointer: usize, + commit_pointer: usize, + dim_read: RecordId, + opened_read: RecordId, + sibling_read: RecordId, + index_read: RecordId, + commit_read: RecordId, + top_level: Vec>, +} + +struct TopLevelRecord { + incorporate_row: Option>, + incorporate_sibling_record: IncorporateSiblingRecord, +} + +struct IncorporateSiblingRecord { + read_sibling_array_start: RecordId, + read_root_is_on_right: RecordId, + sibling: [F; CHUNK], + reads: [RecordId; CHUNK], +} + +struct IncorporateRowRecord { + chunks: Vec>, + initial_height_read: RecordId, + final_height_read: RecordId, +} + +struct InsideRowRecord { + cells: Vec, + chunk: [F; CHUNK], +} + +struct CellRecord { + read: RecordId, + opened_index: usize, + read_row_pointer_and_length: Option, + row_pointer: usize, + row_end: usize, +} + +pub struct VerifyBatchChip { + air: VerifyBatchAir, + records: Vec>, + height: usize, + offline_memory: Arc>>, +} + +impl VerifyBatchChip { + pub fn new( + execution_bus: ExecutionBus, + program_bus: ProgramBus, + memory_bridge: MemoryBridge, + offset: usize, + offline_memory: Arc>>, + poseidon2_config: Poseidon2Config, + ) -> Self { + let air = VerifyBatchAir { + execution_bridge: ExecutionBridge::new(execution_bus, program_bus), + memory_bridge, + internal_bus: VerifyBatchBus(7), + subair: Arc::new(Poseidon2SubAir::new( + poseidon2_config.constants.into(), + )), + offset, + }; + Self { + records: vec![], + air, + height: 0, + offline_memory, + } + } +} + +impl InstructionExecutor for VerifyBatchChip { + fn execute( + &mut self, + memory: &mut MemoryController, + instruction: &Instruction, + from_state: ExecutionState, + ) -> Result, ExecutionError> { + let &Instruction { + a: dim_register, + b: opened_register, + c: sibling_register, + d: index_register, + e: commit_register, + f: address_space, + .. + } = instruction; + + let alpha_read = memory.read(addr_space, alpha_ptr); + let length_read = memory.read_cell(addr_space, length_ptr); + let a_ptr_read = memory.read_cell(addr_space, a_ptr_ptr); + let b_ptr_read = memory.read_cell(addr_space, b_ptr_ptr); + + let alpha = alpha_read.1; + let alpha_pow_original = from_fn(|i| { + memory.unsafe_read_cell(addr_space, alpha_pow_ptr + F::from_canonical_usize(i)) + }); + let mut alpha_pow = alpha_pow_original; + let length = length_read.1.as_canonical_u32() as usize; + let a_ptr = a_ptr_read.1; + let b_ptr = b_ptr_read.1; + + let mut a_reads = Vec::with_capacity(length); + let mut b_reads = Vec::with_capacity(length); + let mut result = [F::ZERO; EXT_DEG]; + + for i in 0..length { + let a_read = memory.read_cell(addr_space, a_ptr + F::from_canonical_usize(i)); + let b_read = memory.read(addr_space, b_ptr + F::from_canonical_usize(4 * i)); + a_reads.push(a_read); + b_reads.push(b_read); + let a = a_read.1; + let b = b_read.1; + result = FieldExtension::add( + result, + FieldExtension::multiply(FieldExtension::subtract(b, elem_to_ext(a)), alpha_pow), + ); + alpha_pow = FieldExtension::multiply(alpha, alpha_pow); + } + + let (alpha_pow_write, prev_data) = memory.write(addr_space, alpha_pow_ptr, alpha_pow); + debug_assert_eq!(prev_data, alpha_pow_original); + let (result_write, _) = memory.write(addr_space, result_ptr, result); + + self.records.push(VerifyBatchRecord { + pc: F::from_canonical_u32(from_state.pc), + start_timestamp: F::from_canonical_u32(from_state.timestamp), + instruction: instruction.clone(), + alpha_read: alpha_read.0, + length_read: length_read.0, + a_ptr_read: a_ptr_read.0, + b_ptr_read: b_ptr_read.0, + a_reads: a_reads.into_iter().map(|r| r.0).collect(), + b_reads: b_reads.into_iter().map(|r| r.0).collect(), + alpha_pow_original, + alpha_pow_write, + result_write, + }); + + self.height += length; + + Ok(ExecutionState { + pc: from_state.pc + DEFAULT_PC_STEP, + timestamp: memory.timestamp(), + }) + } + + fn get_opcode_name(&self, opcode: usize) -> String { + assert_eq!(opcode, (FRI_REDUCED_OPENING as usize) + self.air.offset); + String::from("FRI_REDUCED_OPENING") + } +} \ No newline at end of file diff --git a/extensions/native/circuit/src/verify_batch/columns.rs b/extensions/native/circuit/src/verify_batch/columns.rs new file mode 100644 index 0000000000..bfd5a5b48f --- /dev/null +++ b/extensions/native/circuit/src/verify_batch/columns.rs @@ -0,0 +1,77 @@ +use openvm_circuit::system::memory::offline_checker::MemoryReadAuxCols; +use openvm_circuit_primitives_derive::AlignedBorrow; +use openvm_poseidon2_air::Poseidon2SubCols; +use crate::verify_batch::CHUNK; + +#[repr(C)] +#[derive(AlignedBorrow)] +pub struct VerifyBatchCols { + // flags - at most 1 is true, if none is true then row is disabled + pub incorporate_row: T, + pub incorporate_sibling: T, + pub inside_row: T, + + pub end_inside_row: T, + pub end_top_level: T, + pub start: T, + + // execution state + pub pc: T, + pub very_first_timestamp: T, + pub start_timestamp: T, + pub end_timestamp: T, + + // instruction (a, b, c, d, e) + pub dim_register: T, + pub opened_register: T, + pub sibling_register: T, + pub index_register: T, + pub commit_register: T, + pub address_space: T, + + // poseidon2 + pub inner: Poseidon2SubCols, + + pub cells: [VerifyBatchCellCols; CHUNK], + // initial/final opened index for a subsegment with same height + // initial is used in both, final is used only in top level + pub initial_opened_index: T, + pub final_opened_index: T, + + pub height: T, + pub opened_length: T, + + pub dim_base_pointer: T, + pub opened_base_pointer: T, + pub sibling_base_pointer: T, + pub index_base_pointer: T, + + pub dim_base_pointer_read: MemoryReadAuxCols, + pub opened_base_pointer_and_length_read: MemoryReadAuxCols, + pub sibling_base_pointer_read: MemoryReadAuxCols, + pub index_base_pointer_read: MemoryReadAuxCols, + pub commit_pointer_read: MemoryReadAuxCols, + + pub proof_index: T, + + pub read_initial_height_or_root_is_on_right: MemoryReadAuxCols, + pub read_final_height_or_sibling_array_start: MemoryReadAuxCols, + + pub root_is_on_right: T, + pub sibling_array_start: T, + + pub commit_pointer: T, + pub commit_read: MemoryReadAuxCols, +} + +#[repr(C)] +#[derive(AlignedBorrow, Copy, Clone)] +pub struct VerifyBatchCellCols { + pub read: MemoryReadAuxCols, + pub opened_index: T, + pub read_row_pointer_and_length: MemoryReadAuxCols, + pub row_pointer: T, + pub row_end: T, + pub is_first_in_row: T, + pub is_exhausted: T, +} \ No newline at end of file diff --git a/extensions/native/circuit/src/verify_batch/mod.rs b/extensions/native/circuit/src/verify_batch/mod.rs index a4aeae8e04..68878a459e 100644 --- a/extensions/native/circuit/src/verify_batch/mod.rs +++ b/extensions/native/circuit/src/verify_batch/mod.rs @@ -1,718 +1,45 @@ use std::{ borrow::{Borrow, BorrowMut}, - sync::{Arc, Mutex}, + sync::Arc, }; use openvm_circuit::{ - arch::{ExecutionBridge, ExecutionBus, ExecutionError, ExecutionState, InstructionExecutor}, - system::{ - memory::{ - offline_checker::{ - MemoryBaseAuxCols, MemoryBridge, MemoryReadAuxCols, MemoryWriteAuxCols, - }, - MemoryAddress, MemoryAuxColsFactory, MemoryController, OfflineMemory, RecordId, - }, - program::ProgramBus, + arch::InstructionExecutor, + system::memory::{ + MemoryAuxColsFactory, OfflineMemory, }, }; use openvm_circuit_primitives::{ - is_zero::{IsZeroIo, IsZeroSubAir}, - utils::{assert_array_eq, next_power_of_two_or_zero, not}, - SubAir, TraceSubRowGenerator, + is_zero::IsZeroSubAir, + SubAir, + TraceSubRowGenerator, utils::next_power_of_two_or_zero, }; -use openvm_circuit_primitives_derive::AlignedBorrow; -use openvm_instructions::{instruction::Instruction, program::DEFAULT_PC_STEP}; -use openvm_native_compiler::FriOpcode::FRI_REDUCED_OPENING; -use openvm_poseidon2_air::{BABY_BEAR_POSEIDON2_HALF_FULL_ROUNDS, Poseidon2SubAir, Poseidon2SubCols}; +use openvm_instructions::instruction::Instruction; use openvm_stark_backend::{ + Chip, + ChipUsageGetter, config::{StarkGenericConfig, Val}, interaction::InteractionBuilder, p3_air::{Air, AirBuilder, BaseAir}, p3_field::{Field, FieldAlgebra, PrimeField32}, p3_matrix::{dense::RowMajorMatrix, Matrix}, p3_maybe_rayon::prelude::*, - prover::types::AirProofInput, - rap::{AnyRap, BaseAirWithPublicValues, PartitionedBaseAir}, - Chip, ChipUsageGetter, + prover::types::AirProofInput, rap::{AnyRap, BaseAirWithPublicValues, PartitionedBaseAir}, }; - -use super::field_extension::{FieldExtension, EXT_DEG}; +use chip::{VerifyBatchChip, VerifyBatchRecord}; +use columns::VerifyBatchCols; +use super::field_extension::{EXT_DEG, FieldExtension}; use crate::NATIVE_POSEIDON2_CHUNK_SIZE; #[cfg(test)] mod tests; +mod air; +mod columns; +mod chip; const CHUNK: usize = NATIVE_POSEIDON2_CHUNK_SIZE; -#[repr(C)] -#[derive(AlignedBorrow)] -pub struct VerifyBatchCols { - // flags - at most 1 is true, if none is true then row is disabled - pub incorporate_row: T, - pub incorporate_sibling: T, - pub inside_row: T, - - pub end_inside_row: T, - pub end_top_level: T, - pub start: T, - - // execution state - pub pc: T, - pub very_first_timestamp: T, - pub start_timestamp: T, - pub end_timestamp: T, - - // instruction (a, b, c, d, e) - pub dim_register: T, - pub opened_register: T, - pub sibling_register: T, - pub index_register: T, - pub commit_register: T, - pub address_space: T, - - // poseidon2 - pub inner: Poseidon2SubCols, - - pub cells: [VerifyBatchCellCols; CHUNK], - // initial/final opened index for a subsegment with same height - // initial is used in both, final is used only in top level - pub initial_opened_index: T, - pub final_opened_index: T, - - pub height: T, - pub opened_length: T, - - pub dim_base_pointer: T, - pub opened_base_pointer: T, - pub sibling_base_pointer: T, - pub index_base_pointer: T, - - pub dim_base_pointer_read: MemoryReadAuxCols, - pub opened_base_pointer_and_length_read: MemoryReadAuxCols, - pub sibling_base_pointer_read: MemoryReadAuxCols, - pub index_base_pointer_read: MemoryReadAuxCols, - pub commit_pointer_read: MemoryReadAuxCols, - - pub proof_index: T, - - pub read_initial_height_or_root_is_on_right: MemoryReadAuxCols, - pub read_final_height_or_sibling_array_start: MemoryReadAuxCols, - - pub root_is_on_right: T, - pub sibling_array_start: T, - - pub commit_pointer: T, - pub commit_read: MemoryReadAuxCols, -} - -#[repr(C)] -#[derive(AlignedBorrow, Copy, Clone)] -pub struct VerifyBatchCellCols { - pub read: MemoryReadAuxCols, - pub opened_index: T, - pub read_row_pointer_and_length: MemoryReadAuxCols, - pub row_pointer: T, - pub row_end: T, - pub is_first_in_row: T, - pub is_exhausted: T, -} - -#[derive(Clone, Copy, Debug)] -pub struct VerifyBatchBus(usize); - -impl VerifyBatchBus { - pub fn interact( - &self, - builder: &mut AB, - send: bool, - multiplicity: impl Into, - start_timestamp: impl Into, - end_timestamp: impl Into, - opened_base_pointer: impl Into, - initial_opened_index: impl Into, - final_opened_index: impl Into, - hash: [impl Into; CHUNK], - ) { - } -} -#[derive(Clone, Debug)] -pub struct VerifyBatchAir { - pub execution_bridge: ExecutionBridge, - pub memory_bridge: MemoryBridge, - pub internal_bus: VerifyBatchBus, - pub(super) subair: Arc>, - offset: usize, - half: F, -} - -impl BaseAir for VerifyBatchAir { - fn width(&self) -> usize { - VerifyBatchCols::::width() - } -} - -impl BaseAirWithPublicValues - for VerifyBatchAir -{ -} -impl PartitionedBaseAir - for VerifyBatchAir -{ -} - -impl Air - for VerifyBatchAir -{ - fn eval(&self, builder: &mut AB) { - let main = builder.main(); - let local = main.row_slice(0); - let local: &VerifyBatchCols = (*local).borrow(); - let next = main.row_slice(1); - let next: &VerifyBatchCols = (*next).borrow(); - - let &VerifyBatchCols { - incorporate_row, - incorporate_sibling, - inside_row, - end_inside_row, - end_top_level, - start, - pc, - very_first_timestamp, - start_timestamp, - end_timestamp, - dim_register, - opened_register, - sibling_register, - index_register, - commit_register, - address_space, - inner, - cells, - initial_opened_index, - final_opened_index, - height, - opened_length, - dim_base_pointer, - opened_base_pointer, - sibling_base_pointer, - index_base_pointer, - read_initial_height_or_root_is_on_right, - read_final_height_or_sibling_array_start, - dim_base_pointer_read, - opened_base_pointer_and_length_read, - sibling_base_pointer_read, - index_base_pointer_read, - commit_pointer_read, - root_is_on_right, - commit_pointer, - commit_read, - proof_index, - sibling_array_start, - } = local; - - let left_input = std::array::from_fn::<_, CHUNK, _>(|i| inner.inputs[i]); - let right_input = std::array::from_fn::<_, CHUNK, _>(|i| inner.inputs[i + CHUNK]); - let left_output = - std::array::from_fn::<_, CHUNK, _>(|i| inner.ending_full_rounds[BABY_BEAR_POSEIDON2_HALF_FULL_ROUNDS - 1].post[i]); - let right_output = - std::array::from_fn::<_, CHUNK, _>(|i| inner.ending_full_rounds[BABY_BEAR_POSEIDON2_HALF_FULL_ROUNDS - 1].post[i + CHUNK]); - let next_left_input = std::array::from_fn::<_, CHUNK, _>(|i| next.inner.inputs[i]); - let next_right_input = std::array::from_fn::<_, CHUNK, _>(|i| next.inner.inputs[i + CHUNK]); - let next_left_output = - std::array::from_fn::<_, CHUNK, _>(|i| next.inner.ending_full_rounds[BABY_BEAR_POSEIDON2_HALF_FULL_ROUNDS - 1].post[i]); - let next_right_output = - std::array::from_fn::<_, CHUNK, _>(|i| next.inner.ending_full_rounds[BABY_BEAR_POSEIDON2_HALF_FULL_ROUNDS - 1].post[i + CHUNK]); - - builder.assert_bool(incorporate_row); - builder.assert_bool(incorporate_sibling); - builder.assert_bool(inside_row); - let enabled = incorporate_row + incorporate_sibling + inside_row; - builder.assert_bool(enabled.clone()); - builder.assert_bool(end_inside_row); - builder.when(end_inside_row).assert_one(inside_row); - builder.assert_bool(end_top_level); - builder.when(end_top_level).assert_one(incorporate_row); - - let end = end_inside_row + end_top_level + (AB::Expr::ONE - enabled.clone()); - builder.assert_eq(next.start, end.clone()); - builder.when(end.clone()).assert_zero(next.incorporate_sibling); - - self.subair.eval(builder); - - //// inside row constraints - - // start - builder - .when(end.clone()) - .when(next.inside_row) - .assert_eq(next.initial_opened_index, next.cells[0].opened_index); - builder - .when(end.clone()) - .when(next.inside_row) - .assert_eq(next.very_first_timestamp, next.start_timestamp); - - // end - self.internal_bus.interact( - builder, - false, - end_inside_row, - very_first_timestamp, - start_timestamp + AB::F::from_canonical_usize(2 * CHUNK), - opened_base_pointer, - initial_opened_index, - cells[CHUNK - 1].opened_index, - left_output, - ); - - // things that stay the same (roughly) - - builder.when(inside_row - end_inside_row).assert_eq( - next.start_timestamp, - start_timestamp + AB::F::from_canonical_usize(2 * CHUNK), - ); - builder - .when(inside_row - end_inside_row) - .assert_eq(next.opened_base_pointer, opened_base_pointer); - builder - .when(inside_row - end_inside_row) - .assert_eq(next.dim_base_pointer, dim_base_pointer); - builder - .when(inside_row - end_inside_row) - .assert_eq(next.initial_opened_index, initial_opened_index); - builder - .when(inside_row - end_inside_row) - .assert_eq(next.very_first_timestamp, very_first_timestamp); - - // right input - - for i in 0..CHUNK { - builder - .when(end.clone()) - .when(next.inside_row) - .assert_zero(next_right_input[i]); - } - - for i in 0..CHUNK { - builder - .when(inside_row - end_inside_row) - .assert_eq(right_output[i], next_right_input[i]); - } - - // left input - - for i in 0..CHUNK { - let cell = cells[i]; - let next_cell = if i + 1 == CHUNK { - next.cells[0] - } else { - cells[i + 1] - }; - - builder.assert_bool(cell.is_first_in_row); - builder.assert_bool(cell.is_exhausted); - builder.assert_bool(cell.is_first_in_row + cell.is_exhausted); - - let next_is_normal = AB::Expr::ONE - next_cell.is_first_in_row - next_cell.is_exhausted; - self.memory_bridge - .read( - MemoryAddress::new(address_space, cell.row_pointer), - [left_input[i]], - start_timestamp + AB::F::from_canonical_usize((2 * i) + 1), - &cell.read, - ) - .eval(builder, inside_row * not(cell.is_exhausted)); - builder - .when(cell.is_exhausted) - .assert_eq(left_input[i], AB::F::ZERO); - - let mut when_inside_row_not_last = builder.when(inside_row - end_inside_row); - - // update state for normal cell - when_inside_row_not_last - .when(next_is_normal.clone()) - .assert_eq(next_cell.row_pointer, cell.row_pointer + AB::F::ONE); - when_inside_row_not_last - .when(next_is_normal.clone()) - .assert_eq(next_cell.row_end, cell.row_end); - when_inside_row_not_last - .when(AB::Expr::ONE - next_cell.is_first_in_row) - .assert_eq(next_cell.opened_index, cell.opened_index); - - // update state for first in row cell - self.memory_bridge - .read( - MemoryAddress::new( - address_space, - opened_base_pointer + (cell.opened_index * AB::F::TWO), - ), - [cell.row_pointer.into(), cell.row_end - cell.row_pointer], - start_timestamp + AB::F::from_canonical_usize(2 * i), - &cell.read_row_pointer_and_length, - ) - .eval(builder, inside_row * cell.is_first_in_row); - let mut when_inside_row_not_last = builder.when(inside_row - end_inside_row); - when_inside_row_not_last - .when(next_cell.is_first_in_row) - .assert_eq(next_cell.opened_index, cell.opened_index + AB::F::ONE); - - when_inside_row_not_last - .when(cell.is_exhausted) - .assert_eq(next_cell.is_exhausted, AB::F::ONE); - - let is_last_in_row = if i == CHUNK - 1 { - end_inside_row.into() - } else { - next_cell.is_first_in_row + next_cell.is_exhausted - }; - builder - .when(inside_row) - .when(is_last_in_row) - .assert_eq(cell.row_pointer, cell.row_end); - } - - //// top level constraints - - builder.when(end.clone()).when(next.incorporate_row + next.incorporate_sibling).assert_eq(next.proof_index, AB::F::ZERO); - - let timestamp_after_end_operations = start_timestamp + AB::F::from_canonical_usize(5 + 1); - - builder - .when(end.clone()) - .when(next.incorporate_row) - .assert_eq(next.initial_opened_index, AB::F::ZERO); - self.execution_bridge.execute_and_increment_pc( - AB::Expr::from_canonical_usize(FRI_REDUCED_OPENING as usize + self.offset), - [dim_register, opened_register, sibling_register, index_register, commit_register, address_space], - ExecutionState::new(pc, very_first_timestamp), - end_timestamp - very_first_timestamp, - ).eval(builder, end_top_level); - - self.memory_bridge - .read( - MemoryAddress::new( - address_space, - dim_register, - ), - [dim_base_pointer], - start_timestamp, - &dim_base_pointer_read, - ) - .eval(builder, end_top_level); - self.memory_bridge - .read( - MemoryAddress::new( - address_space, - opened_register, - ), - [opened_base_pointer, opened_length], - start_timestamp + AB::F::ONE, - &opened_base_pointer_and_length_read, - ) - .eval(builder, end_top_level); - self.memory_bridge - .read( - MemoryAddress::new( - address_space, - sibling_register, - ), - [sibling_base_pointer], - start_timestamp + AB::F::TWO, - &sibling_base_pointer_read, - ) - .eval(builder, end_top_level); - self.memory_bridge - .read( - MemoryAddress::new( - address_space, - index_register, - ), - [index_base_pointer], - start_timestamp + AB::F::from_canonical_usize(3), - &index_base_pointer_read, - ) - .eval(builder, end_top_level); - self.memory_bridge - .read( - MemoryAddress::new( - address_space, - commit_register, - ), - [commit_pointer], - start_timestamp + AB::F::from_canonical_usize(4), - &commit_pointer_read - ) - .eval(builder, end_top_level); - - self.memory_bridge - .read( - MemoryAddress::new( - address_space, - commit_pointer, - ), - left_output, - start_timestamp + AB::F::from_canonical_usize(5), - &commit_read, - ) - .eval(builder, end_top_level); - - let mut when_top_level_not_end = builder - .when(incorporate_row + incorporate_sibling - end_top_level); - - when_top_level_not_end - .assert_eq(next.dim_base_pointer, dim_base_pointer); - when_top_level_not_end - .assert_eq(next.opened_base_pointer, opened_base_pointer); - when_top_level_not_end - .assert_eq(next.sibling_base_pointer, sibling_base_pointer); - when_top_level_not_end - .assert_eq(next.index_base_pointer, index_base_pointer); - when_top_level_not_end - .assert_eq(next.start_timestamp, end_timestamp); - when_top_level_not_end - .assert_eq(next.opened_length, opened_length); - when_top_level_not_end - .assert_eq(next.initial_opened_index, final_opened_index + AB::F::ONE); - - builder.when(incorporate_sibling - end_top_level).assert_eq(next.height * AB::F::TWO, height); - builder.when(incorporate_row - end_top_level).assert_eq(next.height, height); - builder.when(incorporate_sibling - end_top_level).assert_eq(next.proof_index, proof_index + AB::F::ONE); - builder.when(incorporate_row - end_top_level).assert_eq(next.proof_index, proof_index); - - builder.when(end_top_level).assert_eq(height, AB::F::ONE); - - // incorporate row - - builder.when(incorporate_row - end_top_level).assert_one(next.incorporate_sibling); - - let row_hash = std::array::from_fn(|i| (start * left_output[i]) + ((AB::Expr::ONE - start) * right_input[i])); - - self.internal_bus.interact( - builder, - true, - incorporate_row, - timestamp_after_end_operations.clone(), - end_timestamp - AB::F::TWO, - opened_base_pointer, - initial_opened_index, - final_opened_index, - row_hash, - ); - - for i in 0..CHUNK { - builder.when(AB::Expr::ONE - end.clone()).when(next.incorporate_row).assert_eq(next_left_input[i], left_output[i]); - } - - builder.when(end_top_level).when(incorporate_row).assert_eq(final_opened_index, opened_length - AB::F::ONE); - - self.memory_bridge - .read( - MemoryAddress::new( - address_space, - dim_base_pointer + initial_opened_index, - ), - [height], - end_timestamp - AB::F::TWO, - &read_initial_height_or_root_is_on_right, - ) - .eval(builder, incorporate_row); - self.memory_bridge - .read( - MemoryAddress::new( - address_space, - dim_base_pointer + final_opened_index, - ), - [height], - end_timestamp - AB::F::ONE, - &read_initial_height_or_root_is_on_right, - ) - .eval(builder, incorporate_row); - - // incorporate sibling - - builder.when(incorporate_sibling - end_top_level).assert_one(next.incorporate_row + next.incorporate_sibling); - builder.when(end_top_level).when(incorporate_sibling).assert_eq(initial_opened_index, opened_length); - - builder.when(incorporate_sibling).assert_eq(final_opened_index + AB::F::ONE, initial_opened_index); - - self.memory_bridge - .read( - MemoryAddress::new( - address_space, - index_base_pointer + proof_index, - ), - [root_is_on_right], - timestamp_after_end_operations.clone(), - &read_initial_height_or_root_is_on_right, - ) - .eval(builder, incorporate_row); - self.memory_bridge - .read( - MemoryAddress::new( - address_space, - sibling_base_pointer + proof_index, - ), - [sibling_array_start], - timestamp_after_end_operations.clone() + AB::F::ONE, - &read_final_height_or_sibling_array_start - ) - .eval(builder, incorporate_row); - - for i in 0..CHUNK { - builder.when(next.incorporate_sibling).when(next.root_is_on_right).assert_eq(next_right_input[i], left_output[i]); - builder.when(next.incorporate_sibling).when(AB::Expr::ONE - next.root_is_on_right).assert_eq(next_left_input[i], left_output[i]); - - self.memory_bridge - .read( - MemoryAddress::new( - address_space, - sibling_array_start + proof_index, - ), - [(root_is_on_right * left_input[i]) + ((AB::Expr::ONE - root_is_on_right) * right_input[i])], - timestamp_after_end_operations.clone() + AB::F::from_canonical_usize(2 + i), - &read_initial_height_or_root_is_on_right, - ) - .eval(builder, incorporate_row); - } - - builder.assert_eq(end_timestamp, timestamp_after_end_operations + AB::F::from_canonical_usize(2 + CHUNK)); - } -} - -pub struct FriReducedOpeningRecord { - pub pc: F, - pub start_timestamp: F, - pub instruction: Instruction, - pub alpha_read: RecordId, - pub length_read: RecordId, - pub a_ptr_read: RecordId, - pub b_ptr_read: RecordId, - pub a_reads: Vec, - pub b_reads: Vec, - pub alpha_pow_original: [F; EXT_DEG], - pub alpha_pow_write: RecordId, - pub result_write: RecordId, -} - -pub struct FriReducedOpeningChip { - air: VerifyBatchAir, - records: Vec>, - height: usize, - offline_memory: Arc>>, -} - -impl FriReducedOpeningChip { - pub fn new( - execution_bus: ExecutionBus, - program_bus: ProgramBus, - memory_bridge: MemoryBridge, - offset: usize, - offline_memory: Arc>>, - ) -> Self { - let air = VerifyBatchAir { - execution_bridge: ExecutionBridge::new(execution_bus, program_bus), - memory_bridge, - offset, - }; - Self { - records: vec![], - air, - height: 0, - offline_memory, - } - } -} - -fn elem_to_ext(elem: F) -> [F; EXT_DEG] { - let mut ret = [F::ZERO; EXT_DEG]; - ret[0] = elem; - ret -} - -impl InstructionExecutor for FriReducedOpeningChip { - fn execute( - &mut self, - memory: &mut MemoryController, - instruction: &Instruction, - from_state: ExecutionState, - ) -> Result, ExecutionError> { - let &Instruction { - a: a_ptr_ptr, - b: b_ptr_ptr, - c: result_ptr, - d: addr_space, - e: length_ptr, - f: alpha_ptr, - g: alpha_pow_ptr, - .. - } = instruction; - - let alpha_read = memory.read(addr_space, alpha_ptr); - let length_read = memory.read_cell(addr_space, length_ptr); - let a_ptr_read = memory.read_cell(addr_space, a_ptr_ptr); - let b_ptr_read = memory.read_cell(addr_space, b_ptr_ptr); - - let alpha = alpha_read.1; - let alpha_pow_original = from_fn(|i| { - memory.unsafe_read_cell(addr_space, alpha_pow_ptr + F::from_canonical_usize(i)) - }); - let mut alpha_pow = alpha_pow_original; - let length = length_read.1.as_canonical_u32() as usize; - let a_ptr = a_ptr_read.1; - let b_ptr = b_ptr_read.1; - - let mut a_reads = Vec::with_capacity(length); - let mut b_reads = Vec::with_capacity(length); - let mut result = [F::ZERO; EXT_DEG]; - - for i in 0..length { - let a_read = memory.read_cell(addr_space, a_ptr + F::from_canonical_usize(i)); - let b_read = memory.read(addr_space, b_ptr + F::from_canonical_usize(4 * i)); - a_reads.push(a_read); - b_reads.push(b_read); - let a = a_read.1; - let b = b_read.1; - result = FieldExtension::add( - result, - FieldExtension::multiply(FieldExtension::subtract(b, elem_to_ext(a)), alpha_pow), - ); - alpha_pow = FieldExtension::multiply(alpha, alpha_pow); - } - - let (alpha_pow_write, prev_data) = memory.write(addr_space, alpha_pow_ptr, alpha_pow); - debug_assert_eq!(prev_data, alpha_pow_original); - let (result_write, _) = memory.write(addr_space, result_ptr, result); - - self.records.push(FriReducedOpeningRecord { - pc: F::from_canonical_u32(from_state.pc), - start_timestamp: F::from_canonical_u32(from_state.timestamp), - instruction: instruction.clone(), - alpha_read: alpha_read.0, - length_read: length_read.0, - a_ptr_read: a_ptr_read.0, - b_ptr_read: b_ptr_read.0, - a_reads: a_reads.into_iter().map(|r| r.0).collect(), - b_reads: b_reads.into_iter().map(|r| r.0).collect(), - alpha_pow_original, - alpha_pow_write, - result_write, - }); - - self.height += length; - - Ok(ExecutionState { - pc: from_state.pc + DEFAULT_PC_STEP, - timestamp: memory.timestamp(), - }) - } - - fn get_opcode_name(&self, opcode: usize) -> String { - assert_eq!(opcode, (FRI_REDUCED_OPENING as usize) + self.air.offset); - String::from("FRI_REDUCED_OPENING") - } -} - -impl ChipUsageGetter for FriReducedOpeningChip { +impl ChipUsageGetter for VerifyBatchChip { fn air_name(&self) -> String { "FriReducedOpeningAir".to_string() } @@ -726,9 +53,9 @@ impl ChipUsageGetter for FriReducedOpeningChip { } } -impl FriReducedOpeningChip { +impl VerifyBatchChip { fn record_to_rows( - record: FriReducedOpeningRecord, + record: VerifyBatchRecord, aux_cols_factory: &MemoryAuxColsFactory, slice: &mut [F], memory: &OfflineMemory, @@ -859,7 +186,7 @@ impl FriReducedOpeningChip { } } -impl Chip for FriReducedOpeningChip> +impl Chip for VerifyBatchChip> where Val: PrimeField32, { diff --git a/extensions/native/circuit/src/verify_batch/tests.rs b/extensions/native/circuit/src/verify_batch/tests.rs index 91b40e2f38..99b56ad908 100644 --- a/extensions/native/circuit/src/verify_batch/tests.rs +++ b/extensions/native/circuit/src/verify_batch/tests.rs @@ -9,10 +9,10 @@ use openvm_stark_backend::{ }; use openvm_stark_sdk::{p3_baby_bear::BabyBear, utils::create_seeded_rng}; use rand::Rng; - +use crate::verify_batch::chip::VerifyBatchChip; +use crate::verify_batch::columns::VerifyBatchCols; use super::{ - super::field_extension::FieldExtension, elem_to_ext, FriReducedOpeningChip, VerifyBatchCols, - EXT_DEG, + elem_to_ext, EXT_DEG, super::field_extension::FieldExtension, }; fn compute_fri_mat_opening( @@ -42,7 +42,7 @@ fn fri_mat_opening_air_test() { let offset = FriOpcode::default_offset(); let mut tester = VmChipTestBuilder::default(); - let mut chip = FriReducedOpeningChip::new( + let mut chip = VerifyBatchChip::new( tester.execution_bus(), tester.program_bus(), tester.memory_bridge(), From 81a6766e49abc9dd02d72fdcb17ad97ac475939a Mon Sep 17 00:00:00 2001 From: TlatoaniHJ Date: Mon, 13 Jan 2025 14:54:41 -0500 Subject: [PATCH 05/50] Some stuff --- .../native/circuit/src/verify_batch/air.rs | 42 +++++++++---------- .../native/circuit/src/verify_batch/chip.rs | 32 +++++++++++--- 2 files changed, 47 insertions(+), 27 deletions(-) diff --git a/extensions/native/circuit/src/verify_batch/air.rs b/extensions/native/circuit/src/verify_batch/air.rs index 009e2cb118..537fb6434e 100644 --- a/extensions/native/circuit/src/verify_batch/air.rs +++ b/extensions/native/circuit/src/verify_batch/air.rs @@ -111,7 +111,6 @@ impl Air let end = end_inside_row + end_top_level + (AB::Expr::ONE - enabled.clone()); builder.assert_eq(next.start, end.clone()); - builder.when(end.clone()).assert_zero(next.incorporate_sibling); self.subair.eval(builder); @@ -245,13 +244,13 @@ impl Air .when(is_last_in_row) .assert_eq(cell.row_pointer, cell.row_end); } - + //// top level constraints - + builder.when(end.clone()).when(next.incorporate_row + next.incorporate_sibling).assert_eq(next.proof_index, AB::F::ZERO); - + let timestamp_after_end_operations = start_timestamp + AB::F::from_canonical_usize(5 + 1); - + builder .when(end.clone()) .when(next.incorporate_row) @@ -262,7 +261,7 @@ impl Air ExecutionState::new(pc, very_first_timestamp), end_timestamp - very_first_timestamp, ).eval(builder, end_top_level); - + self.memory_bridge .read( MemoryAddress::new( @@ -330,7 +329,7 @@ impl Air &commit_read, ) .eval(builder, end_top_level); - + let mut when_top_level_not_end = builder .when(incorporate_row + incorporate_sibling - end_top_level); @@ -348,18 +347,19 @@ impl Air .assert_eq(next.opened_length, opened_length); when_top_level_not_end .assert_eq(next.initial_opened_index, final_opened_index + AB::F::ONE); - + builder.when(incorporate_sibling - end_top_level).assert_eq(next.height * AB::F::TWO, height); builder.when(incorporate_row - end_top_level).assert_eq(next.height, height); builder.when(incorporate_sibling - end_top_level).assert_eq(next.proof_index, proof_index + AB::F::ONE); builder.when(incorporate_row - end_top_level).assert_eq(next.proof_index, proof_index); - - builder.when(end_top_level).assert_eq(height, AB::F::ONE); - + + builder.when(end_top_level).when(incorporate_row).assert_eq(height, AB::F::ONE); + builder.when(end_top_level).when(incorporate_sibling).assert_eq(height, AB::F::TWO); + // incorporate row - + builder.when(incorporate_row - end_top_level).assert_one(next.incorporate_sibling); - + let row_hash = std::array::from_fn(|i| (start * left_output[i]) + ((AB::Expr::ONE - start) * right_input[i])); self.internal_bus.interact( @@ -373,13 +373,13 @@ impl Air final_opened_index, row_hash, ); - + for i in 0..CHUNK { builder.when(AB::Expr::ONE - end.clone()).when(next.incorporate_row).assert_eq(next_left_input[i], left_output[i]); } - + builder.when(end_top_level).when(incorporate_row).assert_eq(final_opened_index, opened_length - AB::F::ONE); - + self.memory_bridge .read( MemoryAddress::new( @@ -402,12 +402,12 @@ impl Air &read_initial_height_or_root_is_on_right, ) .eval(builder, incorporate_row); - + // incorporate sibling - + builder.when(incorporate_sibling - end_top_level).assert_one(next.incorporate_row + next.incorporate_sibling); builder.when(end_top_level).when(incorporate_sibling).assert_eq(initial_opened_index, opened_length); - + builder.when(incorporate_sibling).assert_eq(final_opened_index + AB::F::ONE, initial_opened_index); self.memory_bridge @@ -432,7 +432,7 @@ impl Air &read_final_height_or_sibling_array_start ) .eval(builder, incorporate_row); - + for i in 0..CHUNK { builder.when(next.incorporate_sibling).when(next.root_is_on_right).assert_eq(next_right_input[i], left_output[i]); builder.when(next.incorporate_sibling).when(AB::Expr::ONE - next.root_is_on_right).assert_eq(next_left_input[i], left_output[i]); @@ -449,7 +449,7 @@ impl Air ) .eval(builder, incorporate_row); } - + builder.assert_eq(end_timestamp, timestamp_after_end_operations + AB::F::from_canonical_usize(2 + CHUNK)); } } diff --git a/extensions/native/circuit/src/verify_batch/chip.rs b/extensions/native/circuit/src/verify_batch/chip.rs index 170852f02b..5f687feca8 100644 --- a/extensions/native/circuit/src/verify_batch/chip.rs +++ b/extensions/native/circuit/src/verify_batch/chip.rs @@ -15,23 +15,26 @@ use crate::verify_batch::CHUNK; pub struct VerifyBatchRecord { initial_state: ExecutionState, instruction: Instruction, - dim_base_pointer: usize, - opened_base_pointer: usize, + dim_base_pointer: F, + opened_base_pointer: F, opened_length: usize, - sibling_base_pointer: usize, - index_base_pointer: usize, - commit_pointer: usize, + sibling_base_pointer: F, + index_base_pointer: F, + commit_pointer: F, dim_read: RecordId, opened_read: RecordId, sibling_read: RecordId, index_read: RecordId, commit_read: RecordId, + initial_height: usize, top_level: Vec>, } struct TopLevelRecord { + // must be present in first record incorporate_row: Option>, - incorporate_sibling_record: IncorporateSiblingRecord, + // must be present in all bust last record + incorporate_sibling_record: Option>, } struct IncorporateSiblingRecord { @@ -110,6 +113,23 @@ impl InstructionExecutor for Ve f: address_space, .. } = instruction; + + let (dim_read, dim_base_pointer) = memory.read_cell(address_space, dim_register); + let (opened_read, [opened_base_pointer, opened_len]) = memory.read(address_space, opened_register); + let (sibling_read, sibling_base_pointer) = memory.read_cell(address_space, sibling_register); + let (index_read, index_base_pointer) = memory.read_cell(address_space, index_register); + let (commit_read, commit_pointer) = memory.read_cell(address_space, commit_register); + + let initial_height = memory.unsafe_read_cell(address_space, dim_base_pointer); + let mut height = initial_height.as_canonical_u32(); + let mut proof_index = 0; + let mut top_level = vec![]; + + while height >= 1 { + + height /= 2; + proof_index += 1; + } let alpha_read = memory.read(addr_space, alpha_ptr); let length_read = memory.read_cell(addr_space, length_ptr); From 48ec9d9e3ef9533ce793bb84909a7c5c8116c9af Mon Sep 17 00:00:00 2001 From: TlatoaniHJ Date: Mon, 13 Jan 2025 15:37:49 -0500 Subject: [PATCH 06/50] Write row hash part of execute --- .../native/circuit/src/verify_batch/air.rs | 223 ++++++++++-------- .../native/circuit/src/verify_batch/chip.rs | 158 +++++++++++-- .../circuit/src/verify_batch/columns.rs | 9 +- .../native/circuit/src/verify_batch/mod.rs | 29 +-- .../native/circuit/src/verify_batch/tests.rs | 8 +- 5 files changed, 277 insertions(+), 150 deletions(-) diff --git a/extensions/native/circuit/src/verify_batch/air.rs b/extensions/native/circuit/src/verify_batch/air.rs index 537fb6434e..f6181653b3 100644 --- a/extensions/native/circuit/src/verify_batch/air.rs +++ b/extensions/native/circuit/src/verify_batch/air.rs @@ -1,18 +1,21 @@ -use openvm_stark_backend::p3_field::{Field, FieldAlgebra}; -use openvm_circuit::arch::{ExecutionBridge, ExecutionState}; -use openvm_circuit::system::memory::offline_checker::MemoryBridge; -use std::sync::Arc; -use openvm_poseidon2_air::{BABY_BEAR_POSEIDON2_HALF_FULL_ROUNDS, Poseidon2SubAir}; -use openvm_stark_backend::rap::{BaseAirWithPublicValues, PartitionedBaseAir}; -use openvm_stark_backend::p3_air::{Air, AirBuilder, BaseAir}; -use openvm_stark_backend::interaction::InteractionBuilder; -use openvm_circuit::system::memory::MemoryAddress; +use std::{borrow::Borrow, sync::Arc}; + +use openvm_circuit::{ + arch::{ExecutionBridge, ExecutionState}, + system::memory::{offline_checker::MemoryBridge, MemoryAddress}, +}; use openvm_circuit_primitives::utils::not; use openvm_native_compiler::FriOpcode::FRI_REDUCED_OPENING; -use openvm_stark_backend::p3_matrix::Matrix; -use std::borrow::Borrow; -use crate::verify_batch::CHUNK; -use crate::verify_batch::columns::VerifyBatchCols; +use openvm_poseidon2_air::{Poseidon2SubAir, BABY_BEAR_POSEIDON2_HALF_FULL_ROUNDS}; +use openvm_stark_backend::{ + interaction::InteractionBuilder, + p3_air::{Air, AirBuilder, BaseAir}, + p3_field::{Field, FieldAlgebra}, + p3_matrix::Matrix, + rap::{BaseAirWithPublicValues, PartitionedBaseAir}, +}; + +use crate::verify_batch::{columns::VerifyBatchCols, CHUNK}; #[derive(Clone, Debug)] pub struct VerifyBatchAir { @@ -92,10 +95,12 @@ impl Air let left_input = std::array::from_fn::<_, CHUNK, _>(|i| inner.inputs[i]); let right_input = std::array::from_fn::<_, CHUNK, _>(|i| inner.inputs[i + CHUNK]); - let left_output = - std::array::from_fn::<_, CHUNK, _>(|i| inner.ending_full_rounds[BABY_BEAR_POSEIDON2_HALF_FULL_ROUNDS - 1].post[i]); - let right_output = - std::array::from_fn::<_, CHUNK, _>(|i| inner.ending_full_rounds[BABY_BEAR_POSEIDON2_HALF_FULL_ROUNDS - 1].post[i + CHUNK]); + let left_output = std::array::from_fn::<_, CHUNK, _>(|i| { + inner.ending_full_rounds[BABY_BEAR_POSEIDON2_HALF_FULL_ROUNDS - 1].post[i] + }); + let right_output = std::array::from_fn::<_, CHUNK, _>(|i| { + inner.ending_full_rounds[BABY_BEAR_POSEIDON2_HALF_FULL_ROUNDS - 1].post[i + CHUNK] + }); let next_left_input = std::array::from_fn::<_, CHUNK, _>(|i| next.inner.inputs[i]); let next_right_input = std::array::from_fn::<_, CHUNK, _>(|i| next.inner.inputs[i + CHUNK]); @@ -247,7 +252,10 @@ impl Air //// top level constraints - builder.when(end.clone()).when(next.incorporate_row + next.incorporate_sibling).assert_eq(next.proof_index, AB::F::ZERO); + builder + .when(end.clone()) + .when(next.incorporate_row + next.incorporate_sibling) + .assert_eq(next.proof_index, AB::F::ZERO); let timestamp_after_end_operations = start_timestamp + AB::F::from_canonical_usize(5 + 1); @@ -255,19 +263,25 @@ impl Air .when(end.clone()) .when(next.incorporate_row) .assert_eq(next.initial_opened_index, AB::F::ZERO); - self.execution_bridge.execute_and_increment_pc( - AB::Expr::from_canonical_usize(FRI_REDUCED_OPENING as usize + self.offset), - [dim_register, opened_register, sibling_register, index_register, commit_register, address_space], - ExecutionState::new(pc, very_first_timestamp), - end_timestamp - very_first_timestamp, - ).eval(builder, end_top_level); + self.execution_bridge + .execute_and_increment_pc( + AB::Expr::from_canonical_usize(FRI_REDUCED_OPENING as usize + self.offset), + [ + dim_register, + opened_register, + sibling_register, + index_register, + commit_register, + address_space, + ], + ExecutionState::new(pc, very_first_timestamp), + end_timestamp - very_first_timestamp, + ) + .eval(builder, end_top_level); self.memory_bridge .read( - MemoryAddress::new( - address_space, - dim_register, - ), + MemoryAddress::new(address_space, dim_register), [dim_base_pointer], start_timestamp, &dim_base_pointer_read, @@ -275,10 +289,7 @@ impl Air .eval(builder, end_top_level); self.memory_bridge .read( - MemoryAddress::new( - address_space, - opened_register, - ), + MemoryAddress::new(address_space, opened_register), [opened_base_pointer, opened_length], start_timestamp + AB::F::ONE, &opened_base_pointer_and_length_read, @@ -286,10 +297,7 @@ impl Air .eval(builder, end_top_level); self.memory_bridge .read( - MemoryAddress::new( - address_space, - sibling_register, - ), + MemoryAddress::new(address_space, sibling_register), [sibling_base_pointer], start_timestamp + AB::F::TWO, &sibling_base_pointer_read, @@ -297,10 +305,7 @@ impl Air .eval(builder, end_top_level); self.memory_bridge .read( - MemoryAddress::new( - address_space, - index_register, - ), + MemoryAddress::new(address_space, index_register), [index_base_pointer], start_timestamp + AB::F::from_canonical_usize(3), &index_base_pointer_read, @@ -308,59 +313,65 @@ impl Air .eval(builder, end_top_level); self.memory_bridge .read( - MemoryAddress::new( - address_space, - commit_register, - ), + MemoryAddress::new(address_space, commit_register), [commit_pointer], start_timestamp + AB::F::from_canonical_usize(4), - &commit_pointer_read + &commit_pointer_read, ) .eval(builder, end_top_level); self.memory_bridge .read( - MemoryAddress::new( - address_space, - commit_pointer, - ), + MemoryAddress::new(address_space, commit_pointer), left_output, start_timestamp + AB::F::from_canonical_usize(5), &commit_read, ) .eval(builder, end_top_level); - let mut when_top_level_not_end = builder - .when(incorporate_row + incorporate_sibling - end_top_level); + let mut when_top_level_not_end = + builder.when(incorporate_row + incorporate_sibling - end_top_level); - when_top_level_not_end - .assert_eq(next.dim_base_pointer, dim_base_pointer); - when_top_level_not_end - .assert_eq(next.opened_base_pointer, opened_base_pointer); - when_top_level_not_end - .assert_eq(next.sibling_base_pointer, sibling_base_pointer); - when_top_level_not_end - .assert_eq(next.index_base_pointer, index_base_pointer); - when_top_level_not_end - .assert_eq(next.start_timestamp, end_timestamp); - when_top_level_not_end - .assert_eq(next.opened_length, opened_length); + when_top_level_not_end.assert_eq(next.dim_base_pointer, dim_base_pointer); + when_top_level_not_end.assert_eq(next.opened_base_pointer, opened_base_pointer); + when_top_level_not_end.assert_eq(next.sibling_base_pointer, sibling_base_pointer); + when_top_level_not_end.assert_eq(next.index_base_pointer, index_base_pointer); + when_top_level_not_end.assert_eq(next.start_timestamp, end_timestamp); + when_top_level_not_end.assert_eq(next.opened_length, opened_length); when_top_level_not_end .assert_eq(next.initial_opened_index, final_opened_index + AB::F::ONE); - builder.when(incorporate_sibling - end_top_level).assert_eq(next.height * AB::F::TWO, height); - builder.when(incorporate_row - end_top_level).assert_eq(next.height, height); - builder.when(incorporate_sibling - end_top_level).assert_eq(next.proof_index, proof_index + AB::F::ONE); - builder.when(incorporate_row - end_top_level).assert_eq(next.proof_index, proof_index); + builder + .when(incorporate_sibling - end_top_level) + .assert_eq(next.height * AB::F::TWO, height); + builder + .when(incorporate_row - end_top_level) + .assert_eq(next.height, height); + builder + .when(incorporate_sibling - end_top_level) + .assert_eq(next.proof_index, proof_index + AB::F::ONE); + builder + .when(incorporate_row - end_top_level) + .assert_eq(next.proof_index, proof_index); - builder.when(end_top_level).when(incorporate_row).assert_eq(height, AB::F::ONE); - builder.when(end_top_level).when(incorporate_sibling).assert_eq(height, AB::F::TWO); + builder + .when(end_top_level) + .when(incorporate_row) + .assert_eq(height, AB::F::ONE); + builder + .when(end_top_level) + .when(incorporate_sibling) + .assert_eq(height, AB::F::TWO); // incorporate row - builder.when(incorporate_row - end_top_level).assert_one(next.incorporate_sibling); + builder + .when(incorporate_row - end_top_level) + .assert_one(next.incorporate_sibling); - let row_hash = std::array::from_fn(|i| (start * left_output[i]) + ((AB::Expr::ONE - start) * right_input[i])); + let row_hash = std::array::from_fn(|i| { + (start * left_output[i]) + ((AB::Expr::ONE - start) * right_input[i]) + }); self.internal_bus.interact( builder, @@ -375,17 +386,20 @@ impl Air ); for i in 0..CHUNK { - builder.when(AB::Expr::ONE - end.clone()).when(next.incorporate_row).assert_eq(next_left_input[i], left_output[i]); + builder + .when(AB::Expr::ONE - end.clone()) + .when(next.incorporate_row) + .assert_eq(next_left_input[i], left_output[i]); } - builder.when(end_top_level).when(incorporate_row).assert_eq(final_opened_index, opened_length - AB::F::ONE); + builder + .when(end_top_level) + .when(incorporate_row) + .assert_eq(final_opened_index, opened_length - AB::F::ONE); self.memory_bridge .read( - MemoryAddress::new( - address_space, - dim_base_pointer + initial_opened_index, - ), + MemoryAddress::new(address_space, dim_base_pointer + initial_opened_index), [height], end_timestamp - AB::F::TWO, &read_initial_height_or_root_is_on_right, @@ -393,10 +407,7 @@ impl Air .eval(builder, incorporate_row); self.memory_bridge .read( - MemoryAddress::new( - address_space, - dim_base_pointer + final_opened_index, - ), + MemoryAddress::new(address_space, dim_base_pointer + final_opened_index), [height], end_timestamp - AB::F::ONE, &read_initial_height_or_root_is_on_right, @@ -405,17 +416,21 @@ impl Air // incorporate sibling - builder.when(incorporate_sibling - end_top_level).assert_one(next.incorporate_row + next.incorporate_sibling); - builder.when(end_top_level).when(incorporate_sibling).assert_eq(initial_opened_index, opened_length); + builder + .when(incorporate_sibling - end_top_level) + .assert_one(next.incorporate_row + next.incorporate_sibling); + builder + .when(end_top_level) + .when(incorporate_sibling) + .assert_eq(initial_opened_index, opened_length); - builder.when(incorporate_sibling).assert_eq(final_opened_index + AB::F::ONE, initial_opened_index); + builder + .when(incorporate_sibling) + .assert_eq(final_opened_index + AB::F::ONE, initial_opened_index); self.memory_bridge .read( - MemoryAddress::new( - address_space, - index_base_pointer + proof_index, - ), + MemoryAddress::new(address_space, index_base_pointer + proof_index), [root_is_on_right], timestamp_after_end_operations.clone(), &read_initial_height_or_root_is_on_right, @@ -423,34 +438,38 @@ impl Air .eval(builder, incorporate_row); self.memory_bridge .read( - MemoryAddress::new( - address_space, - sibling_base_pointer + proof_index, - ), + MemoryAddress::new(address_space, sibling_base_pointer + proof_index), [sibling_array_start], timestamp_after_end_operations.clone() + AB::F::ONE, - &read_final_height_or_sibling_array_start + &read_final_height_or_sibling_array_start, ) .eval(builder, incorporate_row); for i in 0..CHUNK { - builder.when(next.incorporate_sibling).when(next.root_is_on_right).assert_eq(next_right_input[i], left_output[i]); - builder.when(next.incorporate_sibling).when(AB::Expr::ONE - next.root_is_on_right).assert_eq(next_left_input[i], left_output[i]); + builder + .when(next.incorporate_sibling) + .when(next.root_is_on_right) + .assert_eq(next_right_input[i], left_output[i]); + builder + .when(next.incorporate_sibling) + .when(AB::Expr::ONE - next.root_is_on_right) + .assert_eq(next_left_input[i], left_output[i]); self.memory_bridge .read( - MemoryAddress::new( - address_space, - sibling_array_start + proof_index, - ), - [(root_is_on_right * left_input[i]) + ((AB::Expr::ONE - root_is_on_right) * right_input[i])], + MemoryAddress::new(address_space, sibling_array_start + proof_index), + [(root_is_on_right * left_input[i]) + + ((AB::Expr::ONE - root_is_on_right) * right_input[i])], timestamp_after_end_operations.clone() + AB::F::from_canonical_usize(2 + i), &read_initial_height_or_root_is_on_right, ) .eval(builder, incorporate_row); } - builder.assert_eq(end_timestamp, timestamp_after_end_operations + AB::F::from_canonical_usize(2 + CHUNK)); + builder.assert_eq( + end_timestamp, + timestamp_after_end_operations + AB::F::from_canonical_usize(2 + CHUNK), + ); } } @@ -471,4 +490,4 @@ impl VerifyBatchBus { } #[derive(Clone, Copy, Debug)] -pub struct VerifyBatchBus(pub usize); \ No newline at end of file +pub struct VerifyBatchBus(pub usize); diff --git a/extensions/native/circuit/src/verify_batch/chip.rs b/extensions/native/circuit/src/verify_batch/chip.rs index 5f687feca8..abaad400ab 100644 --- a/extensions/native/circuit/src/verify_batch/chip.rs +++ b/extensions/native/circuit/src/verify_batch/chip.rs @@ -1,16 +1,24 @@ -use openvm_stark_backend::p3_field::{Field, FieldAlgebra, PrimeField32}; -use openvm_circuit::arch::{ExecutionBridge, ExecutionBus, ExecutionError, ExecutionState, InstructionExecutor}; -use openvm_circuit::system::memory::offline_checker::MemoryBridge; -use openvm_circuit::system::memory::{MemoryController, OfflineMemory, RecordId}; -use openvm_circuit::system::program::ProgramBus; -use openvm_instructions::instruction::Instruction; use std::sync::{Arc, Mutex}; -use openvm_instructions::program::DEFAULT_PC_STEP; + +use openvm_circuit::{ + arch::{ExecutionBridge, ExecutionBus, ExecutionError, ExecutionState, InstructionExecutor}, + system::{ + memory::{offline_checker::MemoryBridge, MemoryController, OfflineMemory, RecordId}, + program::ProgramBus, + }, +}; +use openvm_instructions::{instruction::Instruction, program::DEFAULT_PC_STEP}; use openvm_native_compiler::FriOpcode::FRI_REDUCED_OPENING; -use openvm_poseidon2_air::{Poseidon2Config, Poseidon2SubAir}; -use crate::{EXT_DEG, FieldExtension}; -use crate::verify_batch::air::{VerifyBatchAir, VerifyBatchBus}; -use crate::verify_batch::CHUNK; +use openvm_poseidon2_air::{Poseidon2Config, Poseidon2SubAir, Poseidon2SubChip}; +use openvm_stark_backend::p3_field::{Field, FieldAlgebra, PrimeField32}; + +use crate::{ + verify_batch::{ + air::{VerifyBatchAir, VerifyBatchBus}, + CHUNK, + }, + FieldExtension, EXT_DEG, +}; pub struct VerifyBatchRecord { initial_state: ExecutionState, @@ -46,6 +54,8 @@ struct IncorporateSiblingRecord { struct IncorporateRowRecord { chunks: Vec>, + initial_opened_index: usize, + final_opened_index: usize, initial_height_read: RecordId, final_height_read: RecordId, } @@ -68,6 +78,7 @@ pub struct VerifyBatchChip { records: Vec>, height: usize, offline_memory: Arc>>, + subchip: Poseidon2SubChip } impl VerifyBatchChip { @@ -83,9 +94,7 @@ impl VerifyBatchChip VerifyBatchChip [F; CHUNK] { + let concatenated = std::array::from_fn(|i| if i < CHUNK { left[i] } else { right[i - CHUNK] }); + let permuted = self.subchip.permute(concatenated); + std::array::from_fn(|i| permuted[i]) + } } -impl InstructionExecutor for VerifyBatchChip { +impl InstructionExecutor + for VerifyBatchChip +{ fn execute( &mut self, memory: &mut MemoryController, @@ -113,20 +131,114 @@ impl InstructionExecutor for Ve f: address_space, .. } = instruction; - + let (dim_read, dim_base_pointer) = memory.read_cell(address_space, dim_register); - let (opened_read, [opened_base_pointer, opened_len]) = memory.read(address_space, opened_register); - let (sibling_read, sibling_base_pointer) = memory.read_cell(address_space, sibling_register); + let (opened_read, [opened_base_pointer, opened_len]) = + memory.read(address_space, opened_register); + let (sibling_read, sibling_base_pointer) = + memory.read_cell(address_space, sibling_register); let (index_read, index_base_pointer) = memory.read_cell(address_space, index_register); let (commit_read, commit_pointer) = memory.read_cell(address_space, commit_register); - - let initial_height = memory.unsafe_read_cell(address_space, dim_base_pointer); - let mut height = initial_height.as_canonical_u32(); + + let opened_len = opened_len.as_canonical_usize(); + + let initial_height = memory.unsafe_read_cell(address_space, dim_base_pointer).as_canonical_u32(); + let mut height = initial_height; let mut proof_index = 0; + let mut opened_index = 0; let mut top_level = vec![]; + let mut root = [F::ZERO; CHUNK]; + while height >= 1 { - + let incorporate_row = if opened_index < opened_len + && memory.unsafe_read_cell( + address_space, + dim_base_pointer + F::from_canonical_usize(opened_index), + ) == F::from_canonical_u32(height) + { + let initial_opened_index = opened_index; + for _ in 0..6 { + memory.increment_timestamp(); + } + let mut chunks = vec![]; + + opened_index -= 1; + let mut row_pointer = 0; + let mut row_end = 0; + + let mut rolling_hash = [F::ZERO; 2 * CHUNK]; + + while opened_index < opened_len + && memory.unsafe_read_cell( + address_space, + dim_base_pointer + F::from_canonical_usize(opened_index), + ) == F::from_canonical_u32(height) { + let mut cells = vec![]; + let mut chunk = [F::ZERO; CHUNK]; + for i in 0..CHUNK { + let read_row_pointer_and_length = if row_pointer == row_end { + opened_index += 1; + if opened_index == opened_len || memory.unsafe_read_cell( + address_space, + dim_base_pointer + F::from_canonical_u32(opened_index), + ) != F::from_canonical_u32(height) { + break; + } + let (result, [new_row_pointer, row_len]) = memory.read(address_space, opened_base_pointer + F::from_canonical_u32(2 * opened_index)); + row_pointer = new_row_pointer.as_canonical_u32() as usize; + row_end = row_pointer + row_len.as_canonical_u32() as usize; + Some(result) + } else { + memory.increment_timestamp(); + None + }; + let (read, value) = memory.read_cell(address_space, F::from_canonical_usize(row_pointer)); + + cells.push(CellRecord { + read, + opened_index, + read_row_pointer_and_length, + row_pointer, + row_end, + }); + chunk[i] = value; + row_pointer += 1; + } + chunks.push(InsideRowRecord { + cells, + chunk, + }); + for i in 0..CHUNK { + rolling_hash[i] = chunk[i]; + } + self.subchip.permute_mut(rolling_hash); + } + let final_opened_index = opened_index - 1; + let (initial_height_read, height_check) = memory.read_cell(address_space, dim_base_pointer + F::from_canonical_usize(initial_opened_index)); + assert_eq!(height_check, F::from_canonical_u32(height)); + let (final_height_read, height_check) = memory.read_cell(address_space, dim_base_pointer + F::from_canonical_usize(final_opened_index)); + assert_eq!(height_check, F::from_canonical_u32(height)); + + let hash: [F; CHUNK] = std::array::from_fn(|i| rolling_hash[i]); + + root = if height == initial_height { + hash + } else { + self.compress(root, hash) + }; + + Some(IncorporateRowRecord { + chunks, + initial_opened_index, + final_opened_index, + initial_height_read, + final_height_read, + }) + } else { + None + }; + height /= 2; proof_index += 1; } @@ -194,4 +306,4 @@ impl InstructionExecutor for Ve assert_eq!(opcode, (FRI_REDUCED_OPENING as usize) + self.air.offset); String::from("FRI_REDUCED_OPENING") } -} \ No newline at end of file +} diff --git a/extensions/native/circuit/src/verify_batch/columns.rs b/extensions/native/circuit/src/verify_batch/columns.rs index bfd5a5b48f..5ecc6e8ff3 100644 --- a/extensions/native/circuit/src/verify_batch/columns.rs +++ b/extensions/native/circuit/src/verify_batch/columns.rs @@ -1,6 +1,7 @@ use openvm_circuit::system::memory::offline_checker::MemoryReadAuxCols; use openvm_circuit_primitives_derive::AlignedBorrow; use openvm_poseidon2_air::Poseidon2SubCols; + use crate::verify_batch::CHUNK; #[repr(C)] @@ -51,12 +52,12 @@ pub struct VerifyBatchCols { pub sibling_base_pointer_read: MemoryReadAuxCols, pub index_base_pointer_read: MemoryReadAuxCols, pub commit_pointer_read: MemoryReadAuxCols, - + pub proof_index: T, - + pub read_initial_height_or_root_is_on_right: MemoryReadAuxCols, pub read_final_height_or_sibling_array_start: MemoryReadAuxCols, - + pub root_is_on_right: T, pub sibling_array_start: T, @@ -74,4 +75,4 @@ pub struct VerifyBatchCellCols { pub row_end: T, pub is_first_in_row: T, pub is_exhausted: T, -} \ No newline at end of file +} diff --git a/extensions/native/circuit/src/verify_batch/mod.rs b/extensions/native/circuit/src/verify_batch/mod.rs index 68878a459e..f347ae24ba 100644 --- a/extensions/native/circuit/src/verify_batch/mod.rs +++ b/extensions/native/circuit/src/verify_batch/mod.rs @@ -3,43 +3,40 @@ use std::{ sync::Arc, }; +use chip::{VerifyBatchChip, VerifyBatchRecord}; +use columns::VerifyBatchCols; use openvm_circuit::{ arch::InstructionExecutor, - system::memory::{ - MemoryAuxColsFactory, OfflineMemory, - }, + system::memory::{MemoryAuxColsFactory, OfflineMemory}, }; use openvm_circuit_primitives::{ - is_zero::IsZeroSubAir, - SubAir, - TraceSubRowGenerator, utils::next_power_of_two_or_zero, + is_zero::IsZeroSubAir, utils::next_power_of_two_or_zero, SubAir, TraceSubRowGenerator, }; use openvm_instructions::instruction::Instruction; use openvm_stark_backend::{ - Chip, - ChipUsageGetter, config::{StarkGenericConfig, Val}, interaction::InteractionBuilder, p3_air::{Air, AirBuilder, BaseAir}, p3_field::{Field, FieldAlgebra, PrimeField32}, p3_matrix::{dense::RowMajorMatrix, Matrix}, p3_maybe_rayon::prelude::*, - prover::types::AirProofInput, rap::{AnyRap, BaseAirWithPublicValues, PartitionedBaseAir}, + prover::types::AirProofInput, + rap::{AnyRap, BaseAirWithPublicValues, PartitionedBaseAir}, + Chip, ChipUsageGetter, }; -use chip::{VerifyBatchChip, VerifyBatchRecord}; -use columns::VerifyBatchCols; -use super::field_extension::{EXT_DEG, FieldExtension}; + +use super::field_extension::{FieldExtension, EXT_DEG}; use crate::NATIVE_POSEIDON2_CHUNK_SIZE; -#[cfg(test)] -mod tests; mod air; -mod columns; mod chip; +mod columns; +#[cfg(test)] +mod tests; const CHUNK: usize = NATIVE_POSEIDON2_CHUNK_SIZE; -impl ChipUsageGetter for VerifyBatchChip { +impl ChipUsageGetter for VerifyBatchChip { fn air_name(&self) -> String { "FriReducedOpeningAir".to_string() } diff --git a/extensions/native/circuit/src/verify_batch/tests.rs b/extensions/native/circuit/src/verify_batch/tests.rs index 99b56ad908..23ffea2ae0 100644 --- a/extensions/native/circuit/src/verify_batch/tests.rs +++ b/extensions/native/circuit/src/verify_batch/tests.rs @@ -9,11 +9,9 @@ use openvm_stark_backend::{ }; use openvm_stark_sdk::{p3_baby_bear::BabyBear, utils::create_seeded_rng}; use rand::Rng; -use crate::verify_batch::chip::VerifyBatchChip; -use crate::verify_batch::columns::VerifyBatchCols; -use super::{ - elem_to_ext, EXT_DEG, super::field_extension::FieldExtension, -}; + +use super::{super::field_extension::FieldExtension, elem_to_ext, EXT_DEG}; +use crate::verify_batch::{chip::VerifyBatchChip, columns::VerifyBatchCols}; fn compute_fri_mat_opening( alpha: [F; EXT_DEG], From 9a8a4805219cef84a06b623f01161854197623d8 Mon Sep 17 00:00:00 2001 From: TlatoaniHJ Date: Mon, 13 Jan 2025 15:55:36 -0500 Subject: [PATCH 07/50] Finish execute and add opcode --- .../native/circuit/src/verify_batch/air.rs | 31 ++-- .../native/circuit/src/verify_batch/chip.rs | 155 +++++++++--------- .../native/circuit/src/verify_batch/mod.rs | 28 ++-- extensions/native/compiler/src/lib.rs | 13 ++ 4 files changed, 125 insertions(+), 102 deletions(-) diff --git a/extensions/native/circuit/src/verify_batch/air.rs b/extensions/native/circuit/src/verify_batch/air.rs index f6181653b3..aa829c0288 100644 --- a/extensions/native/circuit/src/verify_batch/air.rs +++ b/extensions/native/circuit/src/verify_batch/air.rs @@ -1,12 +1,5 @@ use std::{borrow::Borrow, sync::Arc}; -use openvm_circuit::{ - arch::{ExecutionBridge, ExecutionState}, - system::memory::{offline_checker::MemoryBridge, MemoryAddress}, -}; -use openvm_circuit_primitives::utils::not; -use openvm_native_compiler::FriOpcode::FRI_REDUCED_OPENING; -use openvm_poseidon2_air::{Poseidon2SubAir, BABY_BEAR_POSEIDON2_HALF_FULL_ROUNDS}; use openvm_stark_backend::{ interaction::InteractionBuilder, p3_air::{Air, AirBuilder, BaseAir}, @@ -15,7 +8,15 @@ use openvm_stark_backend::{ rap::{BaseAirWithPublicValues, PartitionedBaseAir}, }; -use crate::verify_batch::{columns::VerifyBatchCols, CHUNK}; +use openvm_circuit::{ + arch::{ExecutionBridge, ExecutionState}, + system::memory::{MemoryAddress, offline_checker::MemoryBridge}, +}; +use openvm_circuit_primitives::utils::not; +use openvm_native_compiler::VerifyBatchOpcode::VERIFY_BATCH; +use openvm_poseidon2_air::{BABY_BEAR_POSEIDON2_HALF_FULL_ROUNDS, Poseidon2SubAir}; + +use crate::verify_batch::{CHUNK, columns::VerifyBatchCols}; #[derive(Clone, Debug)] pub struct VerifyBatchAir { @@ -265,7 +266,7 @@ impl Air .assert_eq(next.initial_opened_index, AB::F::ZERO); self.execution_bridge .execute_and_increment_pc( - AB::Expr::from_canonical_usize(FRI_REDUCED_OPENING as usize + self.offset), + AB::Expr::from_canonical_usize(VERIFY_BATCH as usize + self.offset), [ dim_register, opened_register, @@ -283,7 +284,7 @@ impl Air .read( MemoryAddress::new(address_space, dim_register), [dim_base_pointer], - start_timestamp, + very_first_timestamp, &dim_base_pointer_read, ) .eval(builder, end_top_level); @@ -291,7 +292,7 @@ impl Air .read( MemoryAddress::new(address_space, opened_register), [opened_base_pointer, opened_length], - start_timestamp + AB::F::ONE, + very_first_timestamp+ AB::F::ONE, &opened_base_pointer_and_length_read, ) .eval(builder, end_top_level); @@ -299,7 +300,7 @@ impl Air .read( MemoryAddress::new(address_space, sibling_register), [sibling_base_pointer], - start_timestamp + AB::F::TWO, + very_first_timestamp + AB::F::TWO, &sibling_base_pointer_read, ) .eval(builder, end_top_level); @@ -307,7 +308,7 @@ impl Air .read( MemoryAddress::new(address_space, index_register), [index_base_pointer], - start_timestamp + AB::F::from_canonical_usize(3), + very_first_timestamp + AB::F::from_canonical_usize(3), &index_base_pointer_read, ) .eval(builder, end_top_level); @@ -315,7 +316,7 @@ impl Air .read( MemoryAddress::new(address_space, commit_register), [commit_pointer], - start_timestamp + AB::F::from_canonical_usize(4), + very_first_timestamp+ AB::F::from_canonical_usize(4), &commit_pointer_read, ) .eval(builder, end_top_level); @@ -324,7 +325,7 @@ impl Air .read( MemoryAddress::new(address_space, commit_pointer), left_output, - start_timestamp + AB::F::from_canonical_usize(5), + very_first_timestamp + AB::F::from_canonical_usize(5), &commit_read, ) .eval(builder, end_top_level); diff --git a/extensions/native/circuit/src/verify_batch/chip.rs b/extensions/native/circuit/src/verify_batch/chip.rs index abaad400ab..730924e256 100644 --- a/extensions/native/circuit/src/verify_batch/chip.rs +++ b/extensions/native/circuit/src/verify_batch/chip.rs @@ -1,27 +1,25 @@ use std::sync::{Arc, Mutex}; +use openvm_stark_backend::p3_field::{Field, FieldAlgebra, PrimeField32}; + use openvm_circuit::{ arch::{ExecutionBridge, ExecutionBus, ExecutionError, ExecutionState, InstructionExecutor}, system::{ - memory::{offline_checker::MemoryBridge, MemoryController, OfflineMemory, RecordId}, + memory::{MemoryController, offline_checker::MemoryBridge, OfflineMemory, RecordId}, program::ProgramBus, }, }; use openvm_instructions::{instruction::Instruction, program::DEFAULT_PC_STEP}; -use openvm_native_compiler::FriOpcode::FRI_REDUCED_OPENING; +use openvm_native_compiler::VerifyBatchOpcode::VERIFY_BATCH; use openvm_poseidon2_air::{Poseidon2Config, Poseidon2SubAir, Poseidon2SubChip}; -use openvm_stark_backend::p3_field::{Field, FieldAlgebra, PrimeField32}; -use crate::{ - verify_batch::{ - air::{VerifyBatchAir, VerifyBatchBus}, - CHUNK, - }, - FieldExtension, EXT_DEG, +use crate::verify_batch::{ + air::{VerifyBatchAir, VerifyBatchBus}, + CHUNK, }; pub struct VerifyBatchRecord { - initial_state: ExecutionState, + from_state: ExecutionState, instruction: Instruction, dim_base_pointer: F, opened_base_pointer: F, @@ -29,10 +27,11 @@ pub struct VerifyBatchRecord { sibling_base_pointer: F, index_base_pointer: F, commit_pointer: F, - dim_read: RecordId, - opened_read: RecordId, - sibling_read: RecordId, - index_read: RecordId, + dim_base_pointer_read: RecordId, + opened_base_pointer_and_length_read: RecordId, + sibling_base_pointer_read: RecordId, + index_base_pointer_read: RecordId, + commit_pointer_read: RecordId, commit_read: RecordId, initial_height: usize, top_level: Vec>, @@ -42,7 +41,7 @@ struct TopLevelRecord { // must be present in first record incorporate_row: Option>, // must be present in all bust last record - incorporate_sibling_record: Option>, + incorporate_sibling: Option>, } struct IncorporateSiblingRecord { @@ -132,15 +131,16 @@ impl InstructionExecutor .. } = instruction; - let (dim_read, dim_base_pointer) = memory.read_cell(address_space, dim_register); - let (opened_read, [opened_base_pointer, opened_len]) = + let (dim_base_pointer_read, dim_base_pointer) = memory.read_cell(address_space, dim_register); + let (opened_base_pointer_and_length_read, [opened_base_pointer, opened_length]) = memory.read(address_space, opened_register); - let (sibling_read, sibling_base_pointer) = + let (sibling_base_pointer_read, sibling_base_pointer) = memory.read_cell(address_space, sibling_register); - let (index_read, index_base_pointer) = memory.read_cell(address_space, index_register); - let (commit_read, commit_pointer) = memory.read_cell(address_space, commit_register); + let (index_base_pointer_read, index_base_pointer) = memory.read_cell(address_space, index_register); + let (commit_pointer_read, commit_pointer) = memory.read_cell(address_space, commit_register); + let (commit_read, commit) = memory.read(address_space, commit_pointer); - let opened_len = opened_len.as_canonical_usize(); + let opened_length = opened_length.as_canonical_usize(); let initial_height = memory.unsafe_read_cell(address_space, dim_base_pointer).as_canonical_u32(); let mut height = initial_height; @@ -151,7 +151,7 @@ impl InstructionExecutor let mut root = [F::ZERO; CHUNK]; while height >= 1 { - let incorporate_row = if opened_index < opened_len + let incorporate_row = if opened_index < opened_length && memory.unsafe_read_cell( address_space, dim_base_pointer + F::from_canonical_usize(opened_index), @@ -169,7 +169,7 @@ impl InstructionExecutor let mut rolling_hash = [F::ZERO; 2 * CHUNK]; - while opened_index < opened_len + while opened_index < opened_length && memory.unsafe_read_cell( address_space, dim_base_pointer + F::from_canonical_usize(opened_index), @@ -179,7 +179,7 @@ impl InstructionExecutor for i in 0..CHUNK { let read_row_pointer_and_length = if row_pointer == row_end { opened_index += 1; - if opened_index == opened_len || memory.unsafe_read_cell( + if opened_index == opened_length || memory.unsafe_read_cell( address_space, dim_base_pointer + F::from_canonical_u32(opened_index), ) != F::from_canonical_u32(height) { @@ -239,63 +239,70 @@ impl InstructionExecutor None }; - height /= 2; - proof_index += 1; - } + let incorporate_sibling = if height == 0 { + None + } else { + for _ in 0..6 { + memory.increment_timestamp(); + } - let alpha_read = memory.read(addr_space, alpha_ptr); - let length_read = memory.read_cell(addr_space, length_ptr); - let a_ptr_read = memory.read_cell(addr_space, a_ptr_ptr); - let b_ptr_read = memory.read_cell(addr_space, b_ptr_ptr); + let (read_root_is_on_right, root_is_on_right) = memory.read_cell(address_space, index_base_pointer + F::from_canonical_usize(proof_index)); + let root_is_on_right = root_is_on_right == F::ONE; - let alpha = alpha_read.1; - let alpha_pow_original = from_fn(|i| { - memory.unsafe_read_cell(addr_space, alpha_pow_ptr + F::from_canonical_usize(i)) - }); - let mut alpha_pow = alpha_pow_original; - let length = length_read.1.as_canonical_u32() as usize; - let a_ptr = a_ptr_read.1; - let b_ptr = b_ptr_read.1; + let (read_sibling_array_start, sibling_array_start) = memory.unsafe_read_cell(address_space, sibling_base_pointer + F::from_canonical_usize(proof_index)); + let sibling_array_start = sibling_array_start.as_canonical_u32() as usize; - let mut a_reads = Vec::with_capacity(length); - let mut b_reads = Vec::with_capacity(length); - let mut result = [F::ZERO; EXT_DEG]; + let mut sibling = [F::ZERO; CHUNK]; + let mut reads = vec![]; + for i in 0..CHUNK { + let (read, value) = memory.read_cell(address_space, sibling_base_pointer + F::from_canonical_usize(sibling_array_start + i)); + sibling[i] = value; + reads.push(read); + } - for i in 0..length { - let a_read = memory.read_cell(addr_space, a_ptr + F::from_canonical_usize(i)); - let b_read = memory.read(addr_space, b_ptr + F::from_canonical_usize(4 * i)); - a_reads.push(a_read); - b_reads.push(b_read); - let a = a_read.1; - let b = b_read.1; - result = FieldExtension::add( - result, - FieldExtension::multiply(FieldExtension::subtract(b, elem_to_ext(a)), alpha_pow), - ); - alpha_pow = FieldExtension::multiply(alpha, alpha_pow); - } + root = if root_is_on_right { + self.compress(sibling, root) + } else { + self.compress(root, sibling) + }; - let (alpha_pow_write, prev_data) = memory.write(addr_space, alpha_pow_ptr, alpha_pow); - debug_assert_eq!(prev_data, alpha_pow_original); - let (result_write, _) = memory.write(addr_space, result_ptr, result); + Some(IncorporateSiblingRecord { + read_sibling_array_start, + read_root_is_on_right, + sibling, + reads, + }) + }; + + top_level.push(TopLevelRecord { + incorporate_row, + incorporate_sibling, + }); + height /= 2; + proof_index += 1; + } + + assert_eq!(commit, root); self.records.push(VerifyBatchRecord { - pc: F::from_canonical_u32(from_state.pc), - start_timestamp: F::from_canonical_u32(from_state.timestamp), - instruction: instruction.clone(), - alpha_read: alpha_read.0, - length_read: length_read.0, - a_ptr_read: a_ptr_read.0, - b_ptr_read: b_ptr_read.0, - a_reads: a_reads.into_iter().map(|r| r.0).collect(), - b_reads: b_reads.into_iter().map(|r| r.0).collect(), - alpha_pow_original, - alpha_pow_write, - result_write, + from_state, + instruction, + dim_base_pointer, + opened_base_pointer, + opened_length, + sibling_base_pointer, + index_base_pointer, + commit_pointer, + dim_base_pointer_read, + opened_base_pointer_and_length_read, + sibling_base_pointer_read, + index_base_pointer_read, + commit_pointer_read, + commit_read, + initial_height, + top_level, }); - self.height += length; - Ok(ExecutionState { pc: from_state.pc + DEFAULT_PC_STEP, timestamp: memory.timestamp(), @@ -303,7 +310,7 @@ impl InstructionExecutor } fn get_opcode_name(&self, opcode: usize) -> String { - assert_eq!(opcode, (FRI_REDUCED_OPENING as usize) + self.air.offset); - String::from("FRI_REDUCED_OPENING") + assert_eq!(opcode, (VERIFY_BATCH as usize) + self.air.offset); + String::from("VERIFY_BATCH") } } diff --git a/extensions/native/circuit/src/verify_batch/mod.rs b/extensions/native/circuit/src/verify_batch/mod.rs index f347ae24ba..33473a8131 100644 --- a/extensions/native/circuit/src/verify_batch/mod.rs +++ b/extensions/native/circuit/src/verify_batch/mod.rs @@ -3,6 +3,18 @@ use std::{ sync::Arc, }; +use openvm_stark_backend::{ + Chip, + ChipUsageGetter, + config::{StarkGenericConfig, Val}, + interaction::InteractionBuilder, + p3_air::{Air, AirBuilder, BaseAir}, + p3_field::{Field, FieldAlgebra, PrimeField32}, + p3_matrix::{dense::RowMajorMatrix, Matrix}, + p3_maybe_rayon::prelude::*, + prover::types::AirProofInput, rap::{AnyRap, BaseAirWithPublicValues, PartitionedBaseAir}, +}; + use chip::{VerifyBatchChip, VerifyBatchRecord}; use columns::VerifyBatchCols; use openvm_circuit::{ @@ -10,24 +22,14 @@ use openvm_circuit::{ system::memory::{MemoryAuxColsFactory, OfflineMemory}, }; use openvm_circuit_primitives::{ - is_zero::IsZeroSubAir, utils::next_power_of_two_or_zero, SubAir, TraceSubRowGenerator, + is_zero::IsZeroSubAir, SubAir, TraceSubRowGenerator, utils::next_power_of_two_or_zero, }; use openvm_instructions::instruction::Instruction; -use openvm_stark_backend::{ - config::{StarkGenericConfig, Val}, - interaction::InteractionBuilder, - p3_air::{Air, AirBuilder, BaseAir}, - p3_field::{Field, FieldAlgebra, PrimeField32}, - p3_matrix::{dense::RowMajorMatrix, Matrix}, - p3_maybe_rayon::prelude::*, - prover::types::AirProofInput, - rap::{AnyRap, BaseAirWithPublicValues, PartitionedBaseAir}, - Chip, ChipUsageGetter, -}; -use super::field_extension::{FieldExtension, EXT_DEG}; use crate::NATIVE_POSEIDON2_CHUNK_SIZE; +use super::field_extension::{EXT_DEG, FieldExtension}; + mod air; mod chip; mod columns; diff --git a/extensions/native/compiler/src/lib.rs b/extensions/native/compiler/src/lib.rs index bed189f3d2..d992f65e28 100644 --- a/extensions/native/compiler/src/lib.rs +++ b/extensions/native/compiler/src/lib.rs @@ -150,3 +150,16 @@ pub enum FriOpcode { /// per column polynomial, per opening point FRI_REDUCED_OPENING, } + +/// Opcodes for verify batch. +#[derive( + Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, EnumCount, EnumIter, FromRepr, UsizeOpcode, +)] +#[opcode_offset = 0x170] +#[repr(usize)] +#[allow(non_camel_case_types)] +pub enum VerifyBatchOpcode { + /// In FRI pcs opening verification, the reduced opening polynomial is computed one evaluation + /// per column polynomial, per opening point + VERIFY_BATCH, +} From c6313123cdd1e33e774bc95f02c1e2687d685275 Mon Sep 17 00:00:00 2001 From: TlatoaniHJ Date: Mon, 13 Jan 2025 17:50:05 -0500 Subject: [PATCH 08/50] Fill in interaction --- .../native/circuit/src/verify_batch/air.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/extensions/native/circuit/src/verify_batch/air.rs b/extensions/native/circuit/src/verify_batch/air.rs index aa829c0288..faafe4db32 100644 --- a/extensions/native/circuit/src/verify_batch/air.rs +++ b/extensions/native/circuit/src/verify_batch/air.rs @@ -7,7 +7,7 @@ use openvm_stark_backend::{ p3_matrix::Matrix, rap::{BaseAirWithPublicValues, PartitionedBaseAir}, }; - +use openvm_stark_backend::interaction::InteractionType; use openvm_circuit::{ arch::{ExecutionBridge, ExecutionState}, system::memory::{MemoryAddress, offline_checker::MemoryBridge}, @@ -487,6 +487,20 @@ impl VerifyBatchBus { final_opened_index: impl Into, hash: [impl Into; CHUNK], ) { + let mut fields = vec![ + start_timestamp.into(), + end_timestamp.into(), + opened_base_pointer.into(), + initial_opened_index.into(), + final_opened_index.into(), + ]; + fields.extend(hash.into_iter().map(Into::into)); + builder.push_interaction( + self.0, + fields, + multiplicity.into(), + if send { InteractionType::Send } else { InteractionType::Receive }, + ); } } From 92b29d84ba77f57cbbc6a82bf2ff7f1cdc54f563 Mon Sep 17 00:00:00 2001 From: TlatoaniHJ Date: Mon, 13 Jan 2025 18:26:14 -0500 Subject: [PATCH 09/50] Finish (almost) trace generation for incorporate sibling --- .../native/circuit/src/verify_batch/air.rs | 5 + .../native/circuit/src/verify_batch/chip.rs | 98 ++++---- .../native/circuit/src/verify_batch/mod.rs | 162 +----------- .../native/circuit/src/verify_batch/trace.rs | 235 ++++++++++++++++++ 4 files changed, 298 insertions(+), 202 deletions(-) create mode 100644 extensions/native/circuit/src/verify_batch/trace.rs diff --git a/extensions/native/circuit/src/verify_batch/air.rs b/extensions/native/circuit/src/verify_batch/air.rs index faafe4db32..c44245db44 100644 --- a/extensions/native/circuit/src/verify_batch/air.rs +++ b/extensions/native/circuit/src/verify_batch/air.rs @@ -142,6 +142,7 @@ impl Air opened_base_pointer, initial_opened_index, cells[CHUNK - 1].opened_index, + address_space, left_output, ); @@ -339,6 +340,7 @@ impl Air when_top_level_not_end.assert_eq(next.index_base_pointer, index_base_pointer); when_top_level_not_end.assert_eq(next.start_timestamp, end_timestamp); when_top_level_not_end.assert_eq(next.opened_length, opened_length); + when_top_level_not_end.assert_eq(next.address_space, address_space); when_top_level_not_end .assert_eq(next.initial_opened_index, final_opened_index + AB::F::ONE); @@ -383,6 +385,7 @@ impl Air opened_base_pointer, initial_opened_index, final_opened_index, + address_space, row_hash, ); @@ -485,6 +488,7 @@ impl VerifyBatchBus { opened_base_pointer: impl Into, initial_opened_index: impl Into, final_opened_index: impl Into, + address_space: impl Into, hash: [impl Into; CHUNK], ) { let mut fields = vec![ @@ -493,6 +497,7 @@ impl VerifyBatchBus { opened_base_pointer.into(), initial_opened_index.into(), final_opened_index.into(), + address_space.into(), ]; fields.extend(hash.into_iter().map(Into::into)); builder.push_interaction( diff --git a/extensions/native/circuit/src/verify_batch/chip.rs b/extensions/native/circuit/src/verify_batch/chip.rs index 730924e256..eb1ea2ce33 100644 --- a/extensions/native/circuit/src/verify_batch/chip.rs +++ b/extensions/native/circuit/src/verify_batch/chip.rs @@ -19,65 +19,75 @@ use crate::verify_batch::{ }; pub struct VerifyBatchRecord { - from_state: ExecutionState, - instruction: Instruction, - dim_base_pointer: F, - opened_base_pointer: F, - opened_length: usize, - sibling_base_pointer: F, - index_base_pointer: F, - commit_pointer: F, - dim_base_pointer_read: RecordId, - opened_base_pointer_and_length_read: RecordId, - sibling_base_pointer_read: RecordId, - index_base_pointer_read: RecordId, - commit_pointer_read: RecordId, - commit_read: RecordId, - initial_height: usize, - top_level: Vec>, + pub from_state: ExecutionState, + pub instruction: Instruction, + + pub dim_base_pointer: F, + pub opened_base_pointer: F, + pub opened_length: usize, + pub sibling_base_pointer: F, + pub index_base_pointer: F, + pub commit_pointer: F, + + pub dim_base_pointer_read: RecordId, + pub opened_base_pointer_and_length_read: RecordId, + pub sibling_base_pointer_read: RecordId, + pub index_base_pointer_read: RecordId, + pub commit_pointer_read: RecordId, + + pub commit_read: RecordId, + pub initial_height: usize, + pub top_level: Vec>, +} + +impl VerifyBatchRecord { + pub fn address_space(&self) -> usize { + self.instruction.f.as_canonical_u32() as usize + } } -struct TopLevelRecord { +pub(super) struct TopLevelRecord { // must be present in first record - incorporate_row: Option>, + pub incorporate_row: Option>, // must be present in all bust last record - incorporate_sibling: Option>, + pub incorporate_sibling: Option>, } -struct IncorporateSiblingRecord { - read_sibling_array_start: RecordId, - read_root_is_on_right: RecordId, - sibling: [F; CHUNK], - reads: [RecordId; CHUNK], +pub(super) struct IncorporateSiblingRecord { + pub read_sibling_array_start: RecordId, + pub read_root_is_on_right: RecordId, + pub root_is_on_right: bool, + pub sibling: [F; CHUNK], + pub reads: [RecordId; CHUNK], } -struct IncorporateRowRecord { - chunks: Vec>, - initial_opened_index: usize, - final_opened_index: usize, - initial_height_read: RecordId, - final_height_read: RecordId, +pub(super) struct IncorporateRowRecord { + pub chunks: Vec>, + pub initial_opened_index: usize, + pub final_opened_index: usize, + pub initial_height_read: RecordId, + pub final_height_read: RecordId, } -struct InsideRowRecord { - cells: Vec, - chunk: [F; CHUNK], +pub(super) struct InsideRowRecord { + pub cells: Vec, + pub chunk: [F; CHUNK], } -struct CellRecord { - read: RecordId, - opened_index: usize, - read_row_pointer_and_length: Option, - row_pointer: usize, - row_end: usize, +pub(super) struct CellRecord { + pub read: RecordId, + pub opened_index: usize, + pub read_row_pointer_and_length: Option, + pub row_pointer: usize, + pub row_end: usize, } pub struct VerifyBatchChip { air: VerifyBatchAir, - records: Vec>, - height: usize, + pub(super) records: Vec>, + pub(super) height: usize, offline_memory: Arc>>, - subchip: Poseidon2SubChip + pub(super) subchip: Poseidon2SubChip, } impl VerifyBatchChip { @@ -209,6 +219,7 @@ impl InstructionExecutor cells, chunk, }); + self.height += 1; for i in 0..CHUNK { rolling_hash[i] = chunk[i]; } @@ -228,6 +239,7 @@ impl InstructionExecutor self.compress(root, hash) }; + self.height += 1; Some(IncorporateRowRecord { chunks, initial_opened_index, @@ -266,9 +278,11 @@ impl InstructionExecutor self.compress(root, sibling) }; + self.height += 1; Some(IncorporateSiblingRecord { read_sibling_array_start, read_root_is_on_right, + root_is_on_right, sibling, reads, }) diff --git a/extensions/native/circuit/src/verify_batch/mod.rs b/extensions/native/circuit/src/verify_batch/mod.rs index 33473a8131..f541219844 100644 --- a/extensions/native/circuit/src/verify_batch/mod.rs +++ b/extensions/native/circuit/src/verify_batch/mod.rs @@ -35,164 +35,6 @@ mod chip; mod columns; #[cfg(test)] mod tests; +mod trace; -const CHUNK: usize = NATIVE_POSEIDON2_CHUNK_SIZE; - -impl ChipUsageGetter for VerifyBatchChip { - fn air_name(&self) -> String { - "FriReducedOpeningAir".to_string() - } - - fn current_trace_height(&self) -> usize { - self.height - } - - fn trace_width(&self) -> usize { - VerifyBatchCols::::width() - } -} - -impl VerifyBatchChip { - fn record_to_rows( - record: VerifyBatchRecord, - aux_cols_factory: &MemoryAuxColsFactory, - slice: &mut [F], - memory: &OfflineMemory, - ) { - let width = VerifyBatchCols::::width(); - - let Instruction { - a: a_ptr_ptr, - b: b_ptr_ptr, - c: result_ptr, - d: addr_space, - e: length_ptr, - f: alpha_ptr, - g: alpha_pow_ptr, - .. - } = record.instruction; - - let length_read = memory.record_by_id(record.length_read); - let alpha_read = memory.record_by_id(record.alpha_read); - let a_ptr_read = memory.record_by_id(record.a_ptr_read); - let b_ptr_read = memory.record_by_id(record.b_ptr_read); - - let length = length_read.data[0].as_canonical_u32() as usize; - let alpha: [F; EXT_DEG] = array::from_fn(|i| alpha_read.data[i]); - let a_ptr = a_ptr_read.data[0]; - let b_ptr = b_ptr_read.data[0]; - - let mut alpha_pow_current = record.alpha_pow_original; - let mut current = [F::ZERO; EXT_DEG]; - - let alpha_aux = aux_cols_factory.make_read_aux_cols(alpha_read); - let length_aux = aux_cols_factory.make_read_aux_cols(length_read); - let a_ptr_aux = aux_cols_factory.make_read_aux_cols(a_ptr_read); - let b_ptr_aux = aux_cols_factory.make_read_aux_cols(b_ptr_read); - - let alpha_pow_aux = aux_cols_factory - .make_write_aux_cols::(memory.record_by_id(record.alpha_pow_write)) - .get_base(); - let result_aux = - aux_cols_factory.make_write_aux_cols(memory.record_by_id(record.result_write)); - - for i in 0..length { - let a_read = memory.record_by_id(record.a_reads[i]); - let b_read = memory.record_by_id(record.b_reads[i]); - let a = a_read.data[0]; - let b: [F; EXT_DEG] = array::from_fn(|i| b_read.data[i]); - current = FieldExtension::add( - current, - FieldExtension::multiply( - FieldExtension::subtract(b, elem_to_ext(a)), - alpha_pow_current, - ), - ); - - let mut idx_is_zero = F::ZERO; - let mut is_zero_aux = F::ZERO; - - let idx = F::from_canonical_usize(i); - IsZeroSubAir.generate_subrow(idx, (&mut is_zero_aux, &mut idx_is_zero)); - - let cols: &mut VerifyBatchCols = slice[i * width..(i + 1) * width].borrow_mut(); - *cols = VerifyBatchCols { - enabled: F::ONE, - pc: record.pc, - a_ptr_ptr, - b_ptr_ptr, - result_ptr, - addr_space, - length_ptr, - alpha_ptr, - alpha_pow_ptr, - start_timestamp: record.start_timestamp, - a_ptr_aux, - b_ptr_aux, - a_aux: aux_cols_factory.make_read_aux_cols(a_read), - b_aux: aux_cols_factory.make_read_aux_cols(b_read), - alpha_aux, - length_aux, - alpha_pow_aux, - result_aux, - a_ptr, - b_ptr, - a, - b, - alpha, - alpha_pow_original: record.alpha_pow_original, - alpha_pow_current, - idx, - idx_is_zero, - is_zero_aux, - current, - }; - - alpha_pow_current = FieldExtension::multiply(alpha, alpha_pow_current); - } - } - - fn generate_trace(self) -> RowMajorMatrix { - let width = self.trace_width(); - let height = next_power_of_two_or_zero(self.height); - let mut flat_trace = F::zero_vec(width * height); - - let memory = self.offline_memory.lock().unwrap(); - - let aux_cols_factory = memory.aux_cols_factory(); - - let mut idx = 0; - for record in self.records { - let length = record.a_reads.len(); - Self::record_to_rows( - record, - &aux_cols_factory, - &mut flat_trace[idx..idx + (length * width)], - &memory, - ); - idx += length * width; - } - // In padding rows, need idx_is_zero = 1 so IsZero constraints pass, and also because next.idx_is_zero is used - // to determine the last row per instruction, so the last non-padding row needs next.idx_is_zero = 1 - flat_trace[self.height * width..] - .par_chunks_mut(width) - .for_each(|row| { - let row: &mut VerifyBatchCols = row.borrow_mut(); - row.idx_is_zero = F::ONE; - }); - - RowMajorMatrix::new(flat_trace, width) - } -} - -impl Chip for VerifyBatchChip> -where - Val: PrimeField32, -{ - fn air(&self) -> Arc> { - Arc::new(self.air) - } - fn generate_air_proof_input(self) -> AirProofInput { - AirProofInput::simple_no_pis(self.air(), self.generate_trace()) - } -} +const CHUNK: usize = NATIVE_POSEIDON2_CHUNK_SIZE; \ No newline at end of file diff --git a/extensions/native/circuit/src/verify_batch/trace.rs b/extensions/native/circuit/src/verify_batch/trace.rs new file mode 100644 index 0000000000..a713f66af4 --- /dev/null +++ b/extensions/native/circuit/src/verify_batch/trace.rs @@ -0,0 +1,235 @@ +use std::sync::Arc; +use openvm_stark_backend::ChipUsageGetter; +use openvm_stark_backend::p3_field::{Field, PrimeField32}; +use openvm_circuit::system::memory::{MemoryAuxColsFactory, OfflineMemory}; +use crate::verify_batch::chip::{IncorporateSiblingRecord, VerifyBatchChip, VerifyBatchRecord}; +use crate::verify_batch::CHUNK; +use crate::verify_batch::columns::VerifyBatchCols; + +impl ChipUsageGetter for VerifyBatchChip { + fn air_name(&self) -> String { + "VerifyBatchAir".to_string() + } + + fn current_trace_height(&self) -> usize { + self.height + } + + fn trace_width(&self) -> usize { + VerifyBatchCols::::width() + } +} + +impl VerifyBatchChip { + fn incorporate_sibling_record_to_rows( + record: &IncorporateSiblingRecord, + aux_cols_factory: &MemoryAuxColsFactory, + slice: &mut [F], + memory: &OfflineMemory, + parent: &VerifyBatchRecord, + proof_index: usize, + is_last: bool, + opened_index: usize, + height: usize, + ) { + let width = VerifyBatchCols::::width(); + let arbitrary = F::ZERO; + + let &IncorporateSiblingRecord { + read_sibling_array_start, + read_root_is_on_right, + root_is_on_right, + sibling, + reads, + } = record; + + let read_root_is_on_right = memory.record_by_id(read_root_is_on_right); + let read_sibling_array_start = memory.record_by_id(read_sibling_array_start); + + let cols: &mut VerifyBatchCols = slice[0..width].borrow_mut(); + *cols = VerifyBatchCols { + incorporate_row: F::ZERO, + incorporate_sibling: F::ONE, + inside_row: F::ZERO, + end_inside_row: F::ZERO, + end_top_level: F::from_bool(is_last), + start: F::ZERO, + pc: arbitrary, + very_first_timestamp: parent.from_state.timestamp, + start_timestamp: read_root_is_on_right.timestamp, + end_timestamp: F::from_canonical_usize(read_root_is_on_right.timestamp as usize + (2 + CHUNK)), + dim_register: arbitrary, + opened_register: arbitrary, + sibling_register: arbitrary, + index_register: arbitrary, + commit_register: arbitrary, + address_space: parent.address_space(), + inner: Poseidon2Cols {}, + cells: [], + initial_opened_index: F::from_canonical_usize(opened_index), + final_opened_index: F::from_canonical_usize(opened_index - 1), + height: F::from_canonical_usize(height), + opened_length: F::from_canonical_usize(parent.opened_length), + dim_base_pointer: F::from_canonical_u32(parent.dim_base_pointer), + opened_base_pointer: F::from_canonical_u32(parent.opened_base_pointer), + sibling_base_pointer: F::from_canonical_u32(parent.sibling_base_pointer), + index_base_pointer: F::from_canonical_u32(parent.index_base_pointer), + dim_base_pointer_read: aux_cols_factory.make_read_aux_cols(memory.record_by_id(parent.dim_base_pointer_read)), + opened_base_pointer_and_length_read: aux_cols_factory.make_read_aux_cols(memory.record_by_id(parent.opened_base_pointer_and_length_read)), + sibling_base_pointer_read: aux_cols_factory.make_read_aux_cols(memory.record_by_id(parent.sibling_base_pointer_read)), + index_base_pointer_read: aux_cols_factory.make_read_aux_cols(memory.record_by_id(parent.index_base_pointer_read)), + commit_pointer_read: aux_cols_factory.make_read_aux_cols(memory.record_by_id(parent.commit_pointer_read)), + commit_read: aux_cols_factory.make_read_aux_cols(memory.record_by_id(parent.commit_read)), + proof_index: F::from_canonical_usize(proof_index), + read_initial_height_or_root_is_on_right: aux_cols_factory.make_read_aux_cols(read_root_is_on_right), + read_final_height_or_sibling_array_start: aux_cols_factory.make_read_aux_cols(read_sibling_array_start), + root_is_on_right: F::from_bool(root_is_on_right), + sibling_array_start: read_sibling_array_start.data[0], + commit_pointer: arbitrary, + }; + } + fn record_to_rows( + record: VerifyBatchRecord, + aux_cols_factory: &MemoryAuxColsFactory, + slice: &mut [F], + memory: &OfflineMemory, + ) { + let width = VerifyBatchCols::::width(); + + + + let Instruction { + a: a_ptr_ptr, + b: b_ptr_ptr, + c: result_ptr, + d: addr_space, + e: length_ptr, + f: alpha_ptr, + g: alpha_pow_ptr, + .. + } = record.instruction; + + let length_read = memory.record_by_id(record.length_read); + let alpha_read = memory.record_by_id(record.alpha_read); + let a_ptr_read = memory.record_by_id(record.a_ptr_read); + let b_ptr_read = memory.record_by_id(record.b_ptr_read); + + let length = length_read.data[0].as_canonical_u32() as usize; + let alpha: [F; EXT_DEG] = array::from_fn(|i| alpha_read.data[i]); + let a_ptr = a_ptr_read.data[0]; + let b_ptr = b_ptr_read.data[0]; + + let mut alpha_pow_current = record.alpha_pow_original; + let mut current = [F::ZERO; EXT_DEG]; + + let alpha_aux = aux_cols_factory.make_read_aux_cols(alpha_read); + let length_aux = aux_cols_factory.make_read_aux_cols(length_read); + let a_ptr_aux = aux_cols_factory.make_read_aux_cols(a_ptr_read); + let b_ptr_aux = aux_cols_factory.make_read_aux_cols(b_ptr_read); + + let alpha_pow_aux = aux_cols_factory + .make_write_aux_cols::(memory.record_by_id(record.alpha_pow_write)) + .get_base(); + let result_aux = + aux_cols_factory.make_write_aux_cols(memory.record_by_id(record.result_write)); + + for i in 0..length { + let a_read = memory.record_by_id(record.a_reads[i]); + let b_read = memory.record_by_id(record.b_reads[i]); + let a = a_read.data[0]; + let b: [F; EXT_DEG] = array::from_fn(|i| b_read.data[i]); + current = FieldExtension::add( + current, + FieldExtension::multiply( + FieldExtension::subtract(b, elem_to_ext(a)), + alpha_pow_current, + ), + ); + + let mut idx_is_zero = F::ZERO; + let mut is_zero_aux = F::ZERO; + + let idx = F::from_canonical_usize(i); + IsZeroSubAir.generate_subrow(idx, (&mut is_zero_aux, &mut idx_is_zero)); + + let cols: &mut VerifyBatchCols = slice[i * width..(i + 1) * width].borrow_mut(); + *cols = VerifyBatchCols { + enabled: F::ONE, + pc: record.pc, + a_ptr_ptr, + b_ptr_ptr, + result_ptr, + addr_space, + length_ptr, + alpha_ptr, + alpha_pow_ptr, + start_timestamp: record.start_timestamp, + a_ptr_aux, + b_ptr_aux, + a_aux: aux_cols_factory.make_read_aux_cols(a_read), + b_aux: aux_cols_factory.make_read_aux_cols(b_read), + alpha_aux, + length_aux, + alpha_pow_aux, + result_aux, + a_ptr, + b_ptr, + a, + b, + alpha, + alpha_pow_original: record.alpha_pow_original, + alpha_pow_current, + idx, + idx_is_zero, + is_zero_aux, + current, + }; + + alpha_pow_current = FieldExtension::multiply(alpha, alpha_pow_current); + } + } + + fn generate_trace(self) -> RowMajorMatrix { + let width = self.trace_width(); + let height = next_power_of_two_or_zero(self.height); + let mut flat_trace = F::zero_vec(width * height); + + let memory = self.offline_memory.lock().unwrap(); + + let aux_cols_factory = memory.aux_cols_factory(); + + let mut idx = 0; + for record in self.records { + let length = record.a_reads.len(); + Self::record_to_rows( + record, + &aux_cols_factory, + &mut flat_trace[idx..idx + (length * width)], + &memory, + ); + idx += length * width; + } + // In padding rows, need idx_is_zero = 1 so IsZero constraints pass, and also because next.idx_is_zero is used + // to determine the last row per instruction, so the last non-padding row needs next.idx_is_zero = 1 + flat_trace[self.height * width..] + .par_chunks_mut(width) + .for_each(|row| { + let row: &mut VerifyBatchCols = row.borrow_mut(); + row.idx_is_zero = F::ONE; + }); + + RowMajorMatrix::new(flat_trace, width) + } +} + +impl Chip for VerifyBatchChip> +where + Val: PrimeField32, +{ + fn air(&self) -> Arc> { + Arc::new(self.air) + } + fn generate_air_proof_input(self) -> AirProofInput { + AirProofInput::simple_no_pis(self.air(), self.generate_trace()) + } +} From dfd50735a66b56f81a7082192faf2c5f295826ff Mon Sep 17 00:00:00 2001 From: TlatoaniHJ Date: Mon, 13 Jan 2025 19:35:51 -0500 Subject: [PATCH 10/50] Commit some stuff --- .../native/circuit/src/verify_batch/chip.rs | 28 +++++++++++------ .../native/circuit/src/verify_batch/trace.rs | 31 +++++++++++++++++-- 2 files changed, 47 insertions(+), 12 deletions(-) diff --git a/extensions/native/circuit/src/verify_batch/chip.rs b/extensions/native/circuit/src/verify_batch/chip.rs index eb1ea2ce33..d1b17847ad 100644 --- a/extensions/native/circuit/src/verify_batch/chip.rs +++ b/extensions/native/circuit/src/verify_batch/chip.rs @@ -59,6 +59,7 @@ pub(super) struct IncorporateSiblingRecord { pub root_is_on_right: bool, pub sibling: [F; CHUNK], pub reads: [RecordId; CHUNK], + pub p2_input: [RecordId; 2 * CHUNK], } pub(super) struct IncorporateRowRecord { @@ -67,11 +68,12 @@ pub(super) struct IncorporateRowRecord { pub final_opened_index: usize, pub initial_height_read: RecordId, pub final_height_read: RecordId, + pub p2_input: [RecordId; 2 * CHUNK], } pub(super) struct InsideRowRecord { pub cells: Vec, - pub chunk: [F; CHUNK], + pub p2_input: [RecordId; 2 * CHUNK], } pub(super) struct CellRecord { @@ -83,10 +85,10 @@ pub(super) struct CellRecord { } pub struct VerifyBatchChip { - air: VerifyBatchAir, + pub(super) air: VerifyBatchAir, pub(super) records: Vec>, pub(super) height: usize, - offline_memory: Arc>>, + pub(super) offline_memory: Arc>>, pub(super) subchip: Poseidon2SubChip, } @@ -115,10 +117,10 @@ impl VerifyBatchChip [F; CHUNK] { + fn compress(&self, left: [F; CHUNK], right: [F; CHUNK]) -> ([F; 2 * CHUNK], [F; CHUNK]) { let concatenated = std::array::from_fn(|i| if i < CHUNK { left[i] } else { right[i - CHUNK] }); let permuted = self.subchip.permute(concatenated); - std::array::from_fn(|i| permuted[i]) + (concatenated, std::array::from_fn(|i| permuted[i])) } } @@ -177,6 +179,7 @@ impl InstructionExecutor let mut row_pointer = 0; let mut row_end = 0; + let mut prev_rolling_hash: Option<[F; 2 * CHUNK]> = None; let mut rolling_hash = [F::ZERO; 2 * CHUNK]; while opened_index < opened_length @@ -217,12 +220,13 @@ impl InstructionExecutor } chunks.push(InsideRowRecord { cells, - chunk, + p2_input: rolling_hash, }); self.height += 1; for i in 0..CHUNK { rolling_hash[i] = chunk[i]; } + prev_rolling_hash = Some(rolling_hash); self.subchip.permute_mut(rolling_hash); } let final_opened_index = opened_index - 1; @@ -232,12 +236,13 @@ impl InstructionExecutor assert_eq!(height_check, F::from_canonical_u32(height)); let hash: [F; CHUNK] = std::array::from_fn(|i| rolling_hash[i]); - - root = if height == initial_height { - hash + + let (p2_input, new_root) = if height == initial_height { + (prev_rolling_hash.unwrap(), hash) } else { self.compress(root, hash) }; + root = new_root; self.height += 1; Some(IncorporateRowRecord { @@ -246,6 +251,7 @@ impl InstructionExecutor final_opened_index, initial_height_read, final_height_read, + p2_input, }) } else { None @@ -272,11 +278,12 @@ impl InstructionExecutor reads.push(read); } - root = if root_is_on_right { + let (p2_input, new_root) = if root_is_on_right { self.compress(sibling, root) } else { self.compress(root, sibling) }; + root = new_root; self.height += 1; Some(IncorporateSiblingRecord { @@ -285,6 +292,7 @@ impl InstructionExecutor root_is_on_right, sibling, reads, + p2_input, }) }; diff --git a/extensions/native/circuit/src/verify_batch/trace.rs b/extensions/native/circuit/src/verify_batch/trace.rs index a713f66af4..4eb6ddf175 100644 --- a/extensions/native/circuit/src/verify_batch/trace.rs +++ b/extensions/native/circuit/src/verify_batch/trace.rs @@ -1,10 +1,16 @@ use std::sync::Arc; + use openvm_stark_backend::ChipUsageGetter; +use openvm_stark_backend::p3_air::BaseAir; use openvm_stark_backend::p3_field::{Field, PrimeField32}; + use openvm_circuit::system::memory::{MemoryAuxColsFactory, OfflineMemory}; +use openvm_poseidon2_air::p3_poseidon2_air::Poseidon2Cols; +use openvm_poseidon2_air::Poseidon2SubCols; + use crate::verify_batch::chip::{IncorporateSiblingRecord, VerifyBatchChip, VerifyBatchRecord}; use crate::verify_batch::CHUNK; -use crate::verify_batch::columns::VerifyBatchCols; +use crate::verify_batch::columns::{VerifyBatchCellCols, VerifyBatchCols}; impl ChipUsageGetter for VerifyBatchChip { fn air_name(&self) -> String { @@ -21,7 +27,17 @@ impl ChipUsageGetter for VerifyBatchChip< } impl VerifyBatchChip { + fn generate_subair_cols(&self, input: [F; 2 * CHUNK]) -> Poseidon2SubCols { + let inner_trace = self.subchip.generate_trace(vec![input]); + let inner_width = self.air.subair.width(); + + let mut v = vec![F::ZERO; inner_width]; + v.copy_from_slice(&inner_trace.values.as_slice()); + let cols: &Poseidon2SubCols = inner_trace.values.as_slice().borrow(); + cols.clone() + } fn incorporate_sibling_record_to_rows( + &self, record: &IncorporateSiblingRecord, aux_cols_factory: &MemoryAuxColsFactory, slice: &mut [F], @@ -41,8 +57,11 @@ impl VerifyBatchChip VerifyBatchChip Date: Tue, 14 Jan 2025 12:55:48 -0500 Subject: [PATCH 11/50] Finish top level trace generation --- .../native/circuit/src/verify_batch/air.rs | 29 +-- .../native/circuit/src/verify_batch/chip.rs | 99 +++++--- .../circuit/src/verify_batch/columns.rs | 6 +- .../native/circuit/src/verify_batch/mod.rs | 30 ++- .../native/circuit/src/verify_batch/trace.rs | 212 ++++++++++++------ 5 files changed, 234 insertions(+), 142 deletions(-) diff --git a/extensions/native/circuit/src/verify_batch/air.rs b/extensions/native/circuit/src/verify_batch/air.rs index c44245db44..3e765e434b 100644 --- a/extensions/native/circuit/src/verify_batch/air.rs +++ b/extensions/native/circuit/src/verify_batch/air.rs @@ -1,22 +1,21 @@ use std::{borrow::Borrow, sync::Arc}; +use openvm_circuit::{ + arch::{ExecutionBridge, ExecutionState}, + system::memory::{offline_checker::MemoryBridge, MemoryAddress}, +}; +use openvm_circuit_primitives::utils::not; +use openvm_native_compiler::VerifyBatchOpcode::VERIFY_BATCH; +use openvm_poseidon2_air::{Poseidon2SubAir, BABY_BEAR_POSEIDON2_HALF_FULL_ROUNDS}; use openvm_stark_backend::{ - interaction::InteractionBuilder, + interaction::{InteractionBuilder, InteractionType}, p3_air::{Air, AirBuilder, BaseAir}, p3_field::{Field, FieldAlgebra}, p3_matrix::Matrix, rap::{BaseAirWithPublicValues, PartitionedBaseAir}, }; -use openvm_stark_backend::interaction::InteractionType; -use openvm_circuit::{ - arch::{ExecutionBridge, ExecutionState}, - system::memory::{MemoryAddress, offline_checker::MemoryBridge}, -}; -use openvm_circuit_primitives::utils::not; -use openvm_native_compiler::VerifyBatchOpcode::VERIFY_BATCH; -use openvm_poseidon2_air::{BABY_BEAR_POSEIDON2_HALF_FULL_ROUNDS, Poseidon2SubAir}; -use crate::verify_batch::{CHUNK, columns::VerifyBatchCols}; +use crate::verify_batch::{columns::VerifyBatchCols, CHUNK}; #[derive(Clone, Debug)] pub struct VerifyBatchAir { @@ -293,7 +292,7 @@ impl Air .read( MemoryAddress::new(address_space, opened_register), [opened_base_pointer, opened_length], - very_first_timestamp+ AB::F::ONE, + very_first_timestamp + AB::F::ONE, &opened_base_pointer_and_length_read, ) .eval(builder, end_top_level); @@ -317,7 +316,7 @@ impl Air .read( MemoryAddress::new(address_space, commit_register), [commit_pointer], - very_first_timestamp+ AB::F::from_canonical_usize(4), + very_first_timestamp + AB::F::from_canonical_usize(4), &commit_pointer_read, ) .eval(builder, end_top_level); @@ -504,7 +503,11 @@ impl VerifyBatchBus { self.0, fields, multiplicity.into(), - if send { InteractionType::Send } else { InteractionType::Receive }, + if send { + InteractionType::Send + } else { + InteractionType::Receive + }, ); } } diff --git a/extensions/native/circuit/src/verify_batch/chip.rs b/extensions/native/circuit/src/verify_batch/chip.rs index d1b17847ad..4e996528b9 100644 --- a/extensions/native/circuit/src/verify_batch/chip.rs +++ b/extensions/native/circuit/src/verify_batch/chip.rs @@ -1,17 +1,16 @@ use std::sync::{Arc, Mutex}; -use openvm_stark_backend::p3_field::{Field, FieldAlgebra, PrimeField32}; - use openvm_circuit::{ arch::{ExecutionBridge, ExecutionBus, ExecutionError, ExecutionState, InstructionExecutor}, system::{ - memory::{MemoryController, offline_checker::MemoryBridge, OfflineMemory, RecordId}, + memory::{offline_checker::MemoryBridge, MemoryController, OfflineMemory, RecordId}, program::ProgramBus, }, }; use openvm_instructions::{instruction::Instruction, program::DEFAULT_PC_STEP}; use openvm_native_compiler::VerifyBatchOpcode::VERIFY_BATCH; use openvm_poseidon2_air::{Poseidon2Config, Poseidon2SubAir, Poseidon2SubChip}; +use openvm_stark_backend::p3_field::{Field, FieldAlgebra, PrimeField32}; use crate::verify_batch::{ air::{VerifyBatchAir, VerifyBatchBus}, @@ -21,26 +20,26 @@ use crate::verify_batch::{ pub struct VerifyBatchRecord { pub from_state: ExecutionState, pub instruction: Instruction, - + pub dim_base_pointer: F, pub opened_base_pointer: F, pub opened_length: usize, pub sibling_base_pointer: F, pub index_base_pointer: F, pub commit_pointer: F, - + pub dim_base_pointer_read: RecordId, pub opened_base_pointer_and_length_read: RecordId, pub sibling_base_pointer_read: RecordId, pub index_base_pointer_read: RecordId, pub commit_pointer_read: RecordId, - + pub commit_read: RecordId, pub initial_height: usize, pub top_level: Vec>, } -impl VerifyBatchRecord { +impl VerifyBatchRecord { pub fn address_space(&self) -> usize { self.instruction.f.as_canonical_u32() as usize } @@ -116,9 +115,10 @@ impl VerifyBatchChip ([F; 2 * CHUNK], [F; CHUNK]) { - let concatenated = std::array::from_fn(|i| if i < CHUNK { left[i] } else { right[i - CHUNK] }); + let concatenated = + std::array::from_fn(|i| if i < CHUNK { left[i] } else { right[i - CHUNK] }); let permuted = self.subchip.permute(concatenated); (concatenated, std::array::from_fn(|i| permuted[i])) } @@ -143,23 +143,28 @@ impl InstructionExecutor .. } = instruction; - let (dim_base_pointer_read, dim_base_pointer) = memory.read_cell(address_space, dim_register); + let (dim_base_pointer_read, dim_base_pointer) = + memory.read_cell(address_space, dim_register); let (opened_base_pointer_and_length_read, [opened_base_pointer, opened_length]) = memory.read(address_space, opened_register); let (sibling_base_pointer_read, sibling_base_pointer) = memory.read_cell(address_space, sibling_register); - let (index_base_pointer_read, index_base_pointer) = memory.read_cell(address_space, index_register); - let (commit_pointer_read, commit_pointer) = memory.read_cell(address_space, commit_register); + let (index_base_pointer_read, index_base_pointer) = + memory.read_cell(address_space, index_register); + let (commit_pointer_read, commit_pointer) = + memory.read_cell(address_space, commit_register); let (commit_read, commit) = memory.read(address_space, commit_pointer); let opened_length = opened_length.as_canonical_usize(); - let initial_height = memory.unsafe_read_cell(address_space, dim_base_pointer).as_canonical_u32(); + let initial_height = memory + .unsafe_read_cell(address_space, dim_base_pointer) + .as_canonical_u32(); let mut height = initial_height; let mut proof_index = 0; let mut opened_index = 0; let mut top_level = vec![]; - + let mut root = [F::ZERO; CHUNK]; while height >= 1 { @@ -174,31 +179,37 @@ impl InstructionExecutor memory.increment_timestamp(); } let mut chunks = vec![]; - + opened_index -= 1; let mut row_pointer = 0; let mut row_end = 0; - + let mut prev_rolling_hash: Option<[F; 2 * CHUNK]> = None; let mut rolling_hash = [F::ZERO; 2 * CHUNK]; - + while opened_index < opened_length && memory.unsafe_read_cell( - address_space, - dim_base_pointer + F::from_canonical_usize(opened_index), - ) == F::from_canonical_u32(height) { + address_space, + dim_base_pointer + F::from_canonical_usize(opened_index), + ) == F::from_canonical_u32(height) + { let mut cells = vec![]; let mut chunk = [F::ZERO; CHUNK]; for i in 0..CHUNK { let read_row_pointer_and_length = if row_pointer == row_end { opened_index += 1; - if opened_index == opened_length || memory.unsafe_read_cell( - address_space, - dim_base_pointer + F::from_canonical_u32(opened_index), - ) != F::from_canonical_u32(height) { + if opened_index == opened_length + || memory.unsafe_read_cell( + address_space, + dim_base_pointer + F::from_canonical_u32(opened_index), + ) != F::from_canonical_u32(height) + { break; } - let (result, [new_row_pointer, row_len]) = memory.read(address_space, opened_base_pointer + F::from_canonical_u32(2 * opened_index)); + let (result, [new_row_pointer, row_len]) = memory.read( + address_space, + opened_base_pointer + F::from_canonical_u32(2 * opened_index), + ); row_pointer = new_row_pointer.as_canonical_u32() as usize; row_end = row_pointer + row_len.as_canonical_u32() as usize; Some(result) @@ -206,8 +217,9 @@ impl InstructionExecutor memory.increment_timestamp(); None }; - let (read, value) = memory.read_cell(address_space, F::from_canonical_usize(row_pointer)); - + let (read, value) = + memory.read_cell(address_space, F::from_canonical_usize(row_pointer)); + cells.push(CellRecord { read, opened_index, @@ -230,11 +242,17 @@ impl InstructionExecutor self.subchip.permute_mut(rolling_hash); } let final_opened_index = opened_index - 1; - let (initial_height_read, height_check) = memory.read_cell(address_space, dim_base_pointer + F::from_canonical_usize(initial_opened_index)); + let (initial_height_read, height_check) = memory.read_cell( + address_space, + dim_base_pointer + F::from_canonical_usize(initial_opened_index), + ); assert_eq!(height_check, F::from_canonical_u32(height)); - let (final_height_read, height_check) = memory.read_cell(address_space, dim_base_pointer + F::from_canonical_usize(final_opened_index)); + let (final_height_read, height_check) = memory.read_cell( + address_space, + dim_base_pointer + F::from_canonical_usize(final_opened_index), + ); assert_eq!(height_check, F::from_canonical_u32(height)); - + let hash: [F; CHUNK] = std::array::from_fn(|i| rolling_hash[i]); let (p2_input, new_root) = if height == initial_height { @@ -243,7 +261,7 @@ impl InstructionExecutor self.compress(root, hash) }; root = new_root; - + self.height += 1; Some(IncorporateRowRecord { chunks, @@ -264,16 +282,25 @@ impl InstructionExecutor memory.increment_timestamp(); } - let (read_root_is_on_right, root_is_on_right) = memory.read_cell(address_space, index_base_pointer + F::from_canonical_usize(proof_index)); + let (read_root_is_on_right, root_is_on_right) = memory.read_cell( + address_space, + index_base_pointer + F::from_canonical_usize(proof_index), + ); let root_is_on_right = root_is_on_right == F::ONE; - let (read_sibling_array_start, sibling_array_start) = memory.unsafe_read_cell(address_space, sibling_base_pointer + F::from_canonical_usize(proof_index)); + let (read_sibling_array_start, sibling_array_start) = memory.unsafe_read_cell( + address_space, + sibling_base_pointer + F::from_canonical_usize(proof_index), + ); let sibling_array_start = sibling_array_start.as_canonical_u32() as usize; let mut sibling = [F::ZERO; CHUNK]; let mut reads = vec![]; for i in 0..CHUNK { - let (read, value) = memory.read_cell(address_space, sibling_base_pointer + F::from_canonical_usize(sibling_array_start + i)); + let (read, value) = memory.read_cell( + address_space, + sibling_base_pointer + F::from_canonical_usize(sibling_array_start + i), + ); sibling[i] = value; reads.push(read); } @@ -295,7 +322,7 @@ impl InstructionExecutor p2_input, }) }; - + top_level.push(TopLevelRecord { incorporate_row, incorporate_sibling, @@ -304,7 +331,7 @@ impl InstructionExecutor height /= 2; proof_index += 1; } - + assert_eq!(commit, root); self.records.push(VerifyBatchRecord { from_state, diff --git a/extensions/native/circuit/src/verify_batch/columns.rs b/extensions/native/circuit/src/verify_batch/columns.rs index 5ecc6e8ff3..fa576e861a 100644 --- a/extensions/native/circuit/src/verify_batch/columns.rs +++ b/extensions/native/circuit/src/verify_batch/columns.rs @@ -7,6 +7,9 @@ use crate::verify_batch::CHUNK; #[repr(C)] #[derive(AlignedBorrow)] pub struct VerifyBatchCols { + // poseidon2 + pub inner: Poseidon2SubCols, + // flags - at most 1 is true, if none is true then row is disabled pub incorporate_row: T, pub incorporate_sibling: T, @@ -30,9 +33,6 @@ pub struct VerifyBatchCols { pub commit_register: T, pub address_space: T, - // poseidon2 - pub inner: Poseidon2SubCols, - pub cells: [VerifyBatchCellCols; CHUNK], // initial/final opened index for a subsegment with same height // initial is used in both, final is used only in top level diff --git a/extensions/native/circuit/src/verify_batch/mod.rs b/extensions/native/circuit/src/verify_batch/mod.rs index f541219844..0b45b4f8e3 100644 --- a/extensions/native/circuit/src/verify_batch/mod.rs +++ b/extensions/native/circuit/src/verify_batch/mod.rs @@ -3,18 +3,6 @@ use std::{ sync::Arc, }; -use openvm_stark_backend::{ - Chip, - ChipUsageGetter, - config::{StarkGenericConfig, Val}, - interaction::InteractionBuilder, - p3_air::{Air, AirBuilder, BaseAir}, - p3_field::{Field, FieldAlgebra, PrimeField32}, - p3_matrix::{dense::RowMajorMatrix, Matrix}, - p3_maybe_rayon::prelude::*, - prover::types::AirProofInput, rap::{AnyRap, BaseAirWithPublicValues, PartitionedBaseAir}, -}; - use chip::{VerifyBatchChip, VerifyBatchRecord}; use columns::VerifyBatchCols; use openvm_circuit::{ @@ -22,14 +10,24 @@ use openvm_circuit::{ system::memory::{MemoryAuxColsFactory, OfflineMemory}, }; use openvm_circuit_primitives::{ - is_zero::IsZeroSubAir, SubAir, TraceSubRowGenerator, utils::next_power_of_two_or_zero, + is_zero::IsZeroSubAir, utils::next_power_of_two_or_zero, SubAir, TraceSubRowGenerator, }; use openvm_instructions::instruction::Instruction; +use openvm_stark_backend::{ + config::{StarkGenericConfig, Val}, + interaction::InteractionBuilder, + p3_air::{Air, AirBuilder, BaseAir}, + p3_field::{Field, FieldAlgebra, PrimeField32}, + p3_matrix::{dense::RowMajorMatrix, Matrix}, + p3_maybe_rayon::prelude::*, + prover::types::AirProofInput, + rap::{AnyRap, BaseAirWithPublicValues, PartitionedBaseAir}, + Chip, ChipUsageGetter, +}; +use super::field_extension::{FieldExtension, EXT_DEG}; use crate::NATIVE_POSEIDON2_CHUNK_SIZE; -use super::field_extension::{EXT_DEG, FieldExtension}; - mod air; mod chip; mod columns; @@ -37,4 +35,4 @@ mod columns; mod tests; mod trace; -const CHUNK: usize = NATIVE_POSEIDON2_CHUNK_SIZE; \ No newline at end of file +const CHUNK: usize = NATIVE_POSEIDON2_CHUNK_SIZE; diff --git a/extensions/native/circuit/src/verify_batch/trace.rs b/extensions/native/circuit/src/verify_batch/trace.rs index 4eb6ddf175..d1cc2ae658 100644 --- a/extensions/native/circuit/src/verify_batch/trace.rs +++ b/extensions/native/circuit/src/verify_batch/trace.rs @@ -1,16 +1,19 @@ use std::sync::Arc; -use openvm_stark_backend::ChipUsageGetter; -use openvm_stark_backend::p3_air::BaseAir; -use openvm_stark_backend::p3_field::{Field, PrimeField32}; - +use itertools::Itertools; use openvm_circuit::system::memory::{MemoryAuxColsFactory, OfflineMemory}; -use openvm_poseidon2_air::p3_poseidon2_air::Poseidon2Cols; -use openvm_poseidon2_air::Poseidon2SubCols; +use openvm_poseidon2_air::{p3_poseidon2_air::Poseidon2Cols, Poseidon2SubCols}; +use openvm_stark_backend::{ + p3_air::BaseAir, + p3_field::{Field, PrimeField32}, + ChipUsageGetter, +}; -use crate::verify_batch::chip::{IncorporateSiblingRecord, VerifyBatchChip, VerifyBatchRecord}; -use crate::verify_batch::CHUNK; -use crate::verify_batch::columns::{VerifyBatchCellCols, VerifyBatchCols}; +use crate::verify_batch::{ + chip::{IncorporateRowRecord, IncorporateSiblingRecord, VerifyBatchChip, VerifyBatchRecord}, + columns::{VerifyBatchCellCols, VerifyBatchCols}, + CHUNK, +}; impl ChipUsageGetter for VerifyBatchChip { fn air_name(&self) -> String { @@ -27,14 +30,10 @@ impl ChipUsageGetter for VerifyBatchChip< } impl VerifyBatchChip { - fn generate_subair_cols(&self, input: [F; 2 * CHUNK]) -> Poseidon2SubCols { + fn generate_subair_cols(&self, input: [F; 2 * CHUNK], cols: &mut [F]) { let inner_trace = self.subchip.generate_trace(vec![input]); let inner_width = self.air.subair.width(); - - let mut v = vec![F::ZERO; inner_width]; - v.copy_from_slice(&inner_trace.values.as_slice()); - let cols: &Poseidon2SubCols = inner_trace.values.as_slice().borrow(); - cols.clone() + cols[..inner_width].copy_from_slice(&inner_trace.values.as_slice()); } fn incorporate_sibling_record_to_rows( &self, @@ -44,13 +43,9 @@ impl VerifyBatchChip, parent: &VerifyBatchRecord, proof_index: usize, - is_last: bool, opened_index: usize, height: usize, ) { - let width = VerifyBatchCols::::width(); - let arbitrary = F::ZERO; - let &IncorporateSiblingRecord { read_sibling_array_start, read_root_is_on_right, @@ -59,61 +54,132 @@ impl VerifyBatchChip = slice[0..width].borrow_mut(); - *cols = VerifyBatchCols { - incorporate_row: F::ZERO, - incorporate_sibling: F::ONE, - inside_row: F::ZERO, - end_inside_row: F::ZERO, - end_top_level: F::from_bool(is_last), - start: F::ZERO, - pc: arbitrary, - very_first_timestamp: parent.from_state.timestamp, - start_timestamp: read_root_is_on_right.timestamp, - end_timestamp: F::from_canonical_usize(read_root_is_on_right.timestamp as usize + (2 + CHUNK)), - dim_register: arbitrary, - opened_register: arbitrary, - sibling_register: arbitrary, - index_register: arbitrary, - commit_register: arbitrary, - address_space: parent.address_space(), - inner: Poseidon2Cols {}, - cells: reads.map(|read| VerifyBatchCellCols { - read: aux_cols_factory.make_read_aux_cols(memory.record_by_id(read)), - opened_index: arbitrary, - read_row_pointer_and_length: arbitrary, - row_pointer: arbitrary, - row_end: arbitrary, - is_first_in_row: arbitrary, - is_exhausted: arbitrary, - }), - initial_opened_index: F::from_canonical_usize(opened_index), - final_opened_index: F::from_canonical_usize(opened_index - 1), - height: F::from_canonical_usize(height), - opened_length: F::from_canonical_usize(parent.opened_length), - dim_base_pointer: F::from_canonical_u32(parent.dim_base_pointer), - opened_base_pointer: F::from_canonical_u32(parent.opened_base_pointer), - sibling_base_pointer: F::from_canonical_u32(parent.sibling_base_pointer), - index_base_pointer: F::from_canonical_u32(parent.index_base_pointer), - dim_base_pointer_read: aux_cols_factory.make_read_aux_cols(memory.record_by_id(parent.dim_base_pointer_read)), - opened_base_pointer_and_length_read: aux_cols_factory.make_read_aux_cols(memory.record_by_id(parent.opened_base_pointer_and_length_read)), - sibling_base_pointer_read: aux_cols_factory.make_read_aux_cols(memory.record_by_id(parent.sibling_base_pointer_read)), - index_base_pointer_read: aux_cols_factory.make_read_aux_cols(memory.record_by_id(parent.index_base_pointer_read)), - commit_pointer_read: aux_cols_factory.make_read_aux_cols(memory.record_by_id(parent.commit_pointer_read)), - commit_read: aux_cols_factory.make_read_aux_cols(memory.record_by_id(parent.commit_read)), - proof_index: F::from_canonical_usize(proof_index), - read_initial_height_or_root_is_on_right: aux_cols_factory.make_read_aux_cols(read_root_is_on_right), - read_final_height_or_sibling_array_start: aux_cols_factory.make_read_aux_cols(read_sibling_array_start), - root_is_on_right: F::from_bool(root_is_on_right), - sibling_array_start: read_sibling_array_start.data[0], - commit_pointer: arbitrary, - }; + + self.generate_subair_cols(p2_input, slice); + let cols: &mut VerifyBatchCols = slice.borrow_mut(); + cols.incorporate_row = F::ZERO; + cols.incorporate_sibling = F::ONE; + cols.inside_row = F::ZERO; + cols.end_inside_row = F::ZERO; + cols.end_top_level = F::ZERO; + cols.start = F::ZERO; + cols.very_first_timestamp = parent.from_state.timestamp; + cols.start_timestamp = F::from_canonical_u32(read_root_is_on_right.timestamp - 6); + cols.end_timestamp = + F::from_canonical_usize(read_root_is_on_right.timestamp as usize + (2 + CHUNK)); + cols.address_space = parent.address_space(); + for (read, cell) in reads.into_iter().zip_eq(cols.cells.iter_mut()) { + cell.read = aux_cols_factory.make_read_aux_cols(memory.record_by_id(read)); + } + cols.initial_opened_index = F::from_canonical_usize(opened_index); + cols.final_opened_index = F::from_canonical_usize(opened_index - 1); + cols.height = F::from_canonical_usize(height); + cols.opened_length = F::from_canonical_usize(parent.opened_length); + cols.dim_base_pointer = F::from_canonical_u32(parent.dim_base_pointer); + cols.opened_base_pointer = F::from_canonical_u32(parent.opened_base_pointer); + cols.sibling_base_pointer = F::from_canonical_u32(parent.sibling_base_pointer); + cols.index_base_pointer = F::from_canonical_u32(parent.index_base_pointer); + + cols.proof_index = F::from_canonical_usize(proof_index); + cols.read_initial_height_or_root_is_on_right = + aux_cols_factory.make_read_aux_cols(read_root_is_on_right); + cols.read_final_height_or_sibling_array_start = + aux_cols_factory.make_read_aux_cols(read_sibling_array_start); + cols.root_is_on_right = F::from_bool(root_is_on_right); + cols.sibling_array_start = read_sibling_array_start.data[0]; + } + fn correct_last_top_level_row( + &self, + record: &VerifyBatchRecord, + aux_cols_factory: &MemoryAuxColsFactory, + slice: &mut [F], + memory: &OfflineMemory, + ) { + let &VerifyBatchRecord { + from_state, + instruction, + commit_pointer, + dim_base_pointer_read, + opened_base_pointer_and_length_read, + sibling_base_pointer_read, + index_base_pointer_read, + commit_pointer_read, + commit_read, + .. + } = record; + let cols: &mut VerifyBatchCols = slice.borrow_mut(); + cols.pc = F::from_canonical_u32(from_state.pc); + cols.dim_register = instruction.a; + cols.opened_register = instruction.b; + cols.sibling_register = instruction.c; + cols.index_register = instruction.d; + cols.commit_register = instruction.e; + cols.commit_pointer = F::from_canonical_u32(commit_pointer); + cols.dim_base_pointer_read = aux_cols_factory.make_read_aux_cols(memory.record_by_id(dim_base_pointer_read)); + cols.opened_base_pointer_and_length_read = aux_cols_factory.make_read_aux_cols(memory.record_by_id(opened_base_pointer_and_length_read)); + cols.sibling_base_pointer_read = aux_cols_factory.make_read_aux_cols(memory.record_by_id(sibling_base_pointer_read)); + cols.index_base_pointer_read = aux_cols_factory.make_read_aux_cols(memory.record_by_id(index_base_pointer_read)); + cols.commit_pointer_read = aux_cols_factory.make_read_aux_cols(memory.record_by_id(commit_pointer_read)); + cols.commit_read = aux_cols_factory.make_read_aux_cols(memory.record_by_id(commit_read)); + } + fn incorporate_row_record_to_rows( + &self, + record: &IncorporateRowRecord, + aux_cols_factory: &MemoryAuxColsFactory, + slice: &mut [F], + memory: &OfflineMemory, + parent: &VerifyBatchRecord, + proof_index: usize, + is_first: bool, + height: usize, + ) { + let &IncorporateRowRecord { + chunks, + initial_opened_index, + final_opened_index, + initial_height_read, + final_height_read, + p2_input, + } = record; + + let initial_height_read = memory.record_by_id(initial_height_read); + let final_height_read = memory.record_by_id(final_height_read); + + self.generate_subair_cols(p2_input, slice); + let cols: &mut VerifyBatchCols = slice.borrow_mut(); + cols.incorporate_row = F::ONE; + cols.incorporate_sibling = F::ZERO; + cols.inside_row = F::ZERO; + cols.end_inside_row = F::ZERO; + cols.end_top_level = F::ZERO; + cols.start = F::from_bool(is_first); + cols.very_first_timestamp = parent.from_state.timestamp; + cols.start_timestamp = F::from_canonical_u32( + memory + .record_by_id(chunks[0].cells[0].read_row_pointer_and_length.unwrap()) + .timestamp + - 6, + ); + cols.end_timestamp = F::from_canonical_u32(final_height_read.timestamp + 1); + cols.address_space = parent.address_space(); + + cols.initial_opened_index = F::from_canonical_usize(initial_opened_index); + cols.final_opened_index = F::from_canonical_usize(final_opened_index); + cols.height = F::from_canonical_usize(height); + cols.opened_length = F::from_canonical_usize(parent.opened_length); + cols.dim_base_pointer = F::from_canonical_u32(parent.dim_base_pointer); + cols.opened_base_pointer = F::from_canonical_u32(parent.opened_base_pointer); + cols.sibling_base_pointer = F::from_canonical_u32(parent.sibling_base_pointer); + cols.index_base_pointer = F::from_canonical_u32(parent.index_base_pointer); + + cols.proof_index = F::from_canonical_usize(proof_index); + cols.read_initial_height_or_root_is_on_right = + aux_cols_factory.make_read_aux_cols(initial_height_read); + cols.read_final_height_or_sibling_array_start = + aux_cols_factory.make_read_aux_cols(final_height_read); } fn record_to_rows( record: VerifyBatchRecord, @@ -122,8 +188,6 @@ impl VerifyBatchChip, ) { let width = VerifyBatchCols::::width(); - - let Instruction { a: a_ptr_ptr, From 1c8c29d891e89120534450960a7b9167fc17bd51 Mon Sep 17 00:00:00 2001 From: TlatoaniHJ Date: Tue, 14 Jan 2025 13:12:11 -0500 Subject: [PATCH 12/50] Finish trace generation for inside row --- .../native/circuit/src/verify_batch/air.rs | 9 ++-- .../circuit/src/verify_batch/columns.rs | 4 +- .../native/circuit/src/verify_batch/trace.rs | 53 +++++++++++++++++-- 3 files changed, 54 insertions(+), 12 deletions(-) diff --git a/extensions/native/circuit/src/verify_batch/air.rs b/extensions/native/circuit/src/verify_batch/air.rs index 3e765e434b..480c5d51b9 100644 --- a/extensions/native/circuit/src/verify_batch/air.rs +++ b/extensions/native/circuit/src/verify_batch/air.rs @@ -58,7 +58,7 @@ impl Air inside_row, end_inside_row, end_top_level, - start, + start_top_level, pc, very_first_timestamp, start_timestamp, @@ -115,7 +115,7 @@ impl Air builder.when(end_top_level).assert_one(incorporate_sibling); let end = end_inside_row + end_top_level + (AB::Expr::ONE - enabled.clone()); - builder.assert_eq(next.start, end.clone()); + builder.when(end.clone()).when(next.incorporate_row).assert_one(next.start_top_level); self.subair.eval(builder); @@ -154,9 +154,6 @@ impl Air builder .when(inside_row - end_inside_row) .assert_eq(next.opened_base_pointer, opened_base_pointer); - builder - .when(inside_row - end_inside_row) - .assert_eq(next.dim_base_pointer, dim_base_pointer); builder .when(inside_row - end_inside_row) .assert_eq(next.initial_opened_index, initial_opened_index); @@ -372,7 +369,7 @@ impl Air .assert_one(next.incorporate_sibling); let row_hash = std::array::from_fn(|i| { - (start * left_output[i]) + ((AB::Expr::ONE - start) * right_input[i]) + (start_top_level * left_output[i]) + ((AB::Expr::ONE - start_top_level) * right_input[i]) }); self.internal_bus.interact( diff --git a/extensions/native/circuit/src/verify_batch/columns.rs b/extensions/native/circuit/src/verify_batch/columns.rs index fa576e861a..ecc8ede069 100644 --- a/extensions/native/circuit/src/verify_batch/columns.rs +++ b/extensions/native/circuit/src/verify_batch/columns.rs @@ -17,13 +17,13 @@ pub struct VerifyBatchCols { pub end_inside_row: T, pub end_top_level: T, - pub start: T, + pub start_top_level: T, // execution state pub pc: T, pub very_first_timestamp: T, pub start_timestamp: T, - pub end_timestamp: T, + pub end_timestamp: T, // only used for top level // instruction (a, b, c, d, e) pub dim_register: T, diff --git a/extensions/native/circuit/src/verify_batch/trace.rs b/extensions/native/circuit/src/verify_batch/trace.rs index d1cc2ae658..353e6de528 100644 --- a/extensions/native/circuit/src/verify_batch/trace.rs +++ b/extensions/native/circuit/src/verify_batch/trace.rs @@ -14,6 +14,7 @@ use crate::verify_batch::{ columns::{VerifyBatchCellCols, VerifyBatchCols}, CHUNK, }; +use crate::verify_batch::chip::{CellRecord, InsideRowRecord}; impl ChipUsageGetter for VerifyBatchChip { fn air_name(&self) -> String { @@ -35,7 +36,7 @@ impl VerifyBatchChip, aux_cols_factory: &MemoryAuxColsFactory, @@ -65,7 +66,7 @@ impl VerifyBatchChip VerifyBatchChip, aux_cols_factory: &MemoryAuxColsFactory, @@ -155,7 +156,7 @@ impl VerifyBatchChip VerifyBatchChip, + aux_cols_factory: &MemoryAuxColsFactory, + slice: &mut [F], + memory: &OfflineMemory, + parent: &IncorporateRowRecord, + grandparent: &VerifyBatchRecord, + is_last: bool, + ) { + let &InsideRowRecord { + cells, p2_input + } = record; + + self.generate_subair_cols(p2_input, slice); + let cols: &mut VerifyBatchCols = slice.borrow_mut(); + cols.incorporate_row = F::ZERO; + cols.incorporate_sibling = F::ZERO; + cols.inside_row = F::ONE; + cols.end_inside_row = F::from_bool(is_last); + cols.end_top_level = F::ZERO; + cols.very_first_timestamp = F::from_canonical_u32(memory.record_by_id(parent.chunks[0].cells[0].read_row_pointer_and_length.unwrap()).timestamp); + cols.start_timestamp = F::from_canonical_u32(memory.record_by_id(cells[0].read_row_pointer_and_length.unwrap()).timestamp); + cols.address_space = grandparent.address_space(); + + for (record, cell) in cells.into_iter().zip(cols.cells.iter_mut()) { + let CellRecord { read, opened_index, read_row_pointer_and_length, row_pointer, row_end } = record; + cell.read = aux_cols_factory.make_read_aux_cols(memory.record_by_id(read)); + cell.opened_index = F::from_canonical_usize(opened_index); + if let Some(read_row_pointer_and_length) = read_row_pointer_and_length { + cell.read_row_pointer_and_length = aux_cols_factory.make_read_aux_cols(memory.record_by_id(read_row_pointer_and_length)); + } + cell.row_pointer = F::from_canonical_usize(row_pointer); + cell.row_end = F::from_canonical_usize(row_end); + cell.is_first_in_row = F::from_bool(read_row_pointer_and_length.is_some()); + cell.is_exhausted = F::ZERO; + } + for cell in cols.cells.iter_mut().skip(cells.len()) { + cell.is_exhausted = F::ONE; + } + + cols.initial_opened_index = F::from_canonical_usize(parent.initial_opened_index); + cols.opened_base_pointer = F::from_canonical_u32(grandparent.opened_base_pointer); + } fn record_to_rows( record: VerifyBatchRecord, aux_cols_factory: &MemoryAuxColsFactory, From 4c84f4939d2251f0bd484755ceeacb1e6d7a5a73 Mon Sep 17 00:00:00 2001 From: TlatoaniHJ Date: Tue, 14 Jan 2025 13:40:26 -0500 Subject: [PATCH 13/50] Finish implementation --- .../native/circuit/src/verify_batch/air.rs | 18 +- .../native/circuit/src/verify_batch/chip.rs | 28 +- .../circuit/src/verify_batch/columns.rs | 3 + .../native/circuit/src/verify_batch/trace.rs | 292 +++++++++--------- 4 files changed, 174 insertions(+), 167 deletions(-) diff --git a/extensions/native/circuit/src/verify_batch/air.rs b/extensions/native/circuit/src/verify_batch/air.rs index 480c5d51b9..6d7aee6473 100644 --- a/extensions/native/circuit/src/verify_batch/air.rs +++ b/extensions/native/circuit/src/verify_batch/air.rs @@ -69,7 +69,6 @@ impl Air index_register, commit_register, address_space, - inner, cells, initial_opened_index, final_opened_index, @@ -91,15 +90,16 @@ impl Air commit_read, proof_index, sibling_array_start, + .. } = local; - let left_input = std::array::from_fn::<_, CHUNK, _>(|i| inner.inputs[i]); - let right_input = std::array::from_fn::<_, CHUNK, _>(|i| inner.inputs[i + CHUNK]); + let left_input = std::array::from_fn::<_, CHUNK, _>(|i| local.inner.inputs[i]); + let right_input = std::array::from_fn::<_, CHUNK, _>(|i| local.inner.inputs[i + CHUNK]); let left_output = std::array::from_fn::<_, CHUNK, _>(|i| { - inner.ending_full_rounds[BABY_BEAR_POSEIDON2_HALF_FULL_ROUNDS - 1].post[i] + local.inner.ending_full_rounds[BABY_BEAR_POSEIDON2_HALF_FULL_ROUNDS - 1].post[i] }); let right_output = std::array::from_fn::<_, CHUNK, _>(|i| { - inner.ending_full_rounds[BABY_BEAR_POSEIDON2_HALF_FULL_ROUNDS - 1].post[i + CHUNK] + local.inner.ending_full_rounds[BABY_BEAR_POSEIDON2_HALF_FULL_ROUNDS - 1].post[i + CHUNK] }); let next_left_input = std::array::from_fn::<_, CHUNK, _>(|i| next.inner.inputs[i]); let next_right_input = std::array::from_fn::<_, CHUNK, _>(|i| next.inner.inputs[i + CHUNK]); @@ -115,7 +115,10 @@ impl Air builder.when(end_top_level).assert_one(incorporate_sibling); let end = end_inside_row + end_top_level + (AB::Expr::ONE - enabled.clone()); - builder.when(end.clone()).when(next.incorporate_row).assert_one(next.start_top_level); + builder + .when(end.clone()) + .when(next.incorporate_row) + .assert_one(next.start_top_level); self.subair.eval(builder); @@ -369,7 +372,8 @@ impl Air .assert_one(next.incorporate_sibling); let row_hash = std::array::from_fn(|i| { - (start_top_level * left_output[i]) + ((AB::Expr::ONE - start_top_level) * right_input[i]) + (start_top_level * left_output[i]) + + ((AB::Expr::ONE - start_top_level) * right_input[i]) }); self.internal_bus.interact( diff --git a/extensions/native/circuit/src/verify_batch/chip.rs b/extensions/native/circuit/src/verify_batch/chip.rs index 4e996528b9..4fff6aa22d 100644 --- a/extensions/native/circuit/src/verify_batch/chip.rs +++ b/extensions/native/circuit/src/verify_batch/chip.rs @@ -39,7 +39,7 @@ pub struct VerifyBatchRecord { pub top_level: Vec>, } -impl VerifyBatchRecord { +impl VerifyBatchRecord { pub fn address_space(&self) -> usize { self.instruction.f.as_canonical_u32() as usize } @@ -58,7 +58,7 @@ pub(super) struct IncorporateSiblingRecord { pub root_is_on_right: bool, pub sibling: [F; CHUNK], pub reads: [RecordId; CHUNK], - pub p2_input: [RecordId; 2 * CHUNK], + pub p2_input: [F; 2 * CHUNK], } pub(super) struct IncorporateRowRecord { @@ -67,12 +67,12 @@ pub(super) struct IncorporateRowRecord { pub final_opened_index: usize, pub initial_height_read: RecordId, pub final_height_read: RecordId, - pub p2_input: [RecordId; 2 * CHUNK], + pub p2_input: [F; 2 * CHUNK], } pub(super) struct InsideRowRecord { pub cells: Vec, - pub p2_input: [RecordId; 2 * CHUNK], + pub p2_input: [F; 2 * CHUNK], } pub(super) struct CellRecord { @@ -155,7 +155,7 @@ impl InstructionExecutor memory.read_cell(address_space, commit_register); let (commit_read, commit) = memory.read(address_space, commit_pointer); - let opened_length = opened_length.as_canonical_usize(); + let opened_length = opened_length.as_canonical_u32() as usize; let initial_height = memory .unsafe_read_cell(address_space, dim_base_pointer) @@ -201,14 +201,14 @@ impl InstructionExecutor if opened_index == opened_length || memory.unsafe_read_cell( address_space, - dim_base_pointer + F::from_canonical_u32(opened_index), + dim_base_pointer + F::from_canonical_usize(opened_index), ) != F::from_canonical_u32(height) { break; } let (result, [new_row_pointer, row_len]) = memory.read( address_space, - opened_base_pointer + F::from_canonical_u32(2 * opened_index), + opened_base_pointer + F::from_canonical_usize(2 * opened_index), ); row_pointer = new_row_pointer.as_canonical_u32() as usize; row_end = row_pointer + row_len.as_canonical_u32() as usize; @@ -235,11 +235,9 @@ impl InstructionExecutor p2_input: rolling_hash, }); self.height += 1; - for i in 0..CHUNK { - rolling_hash[i] = chunk[i]; - } + rolling_hash[..CHUNK].copy_from_slice(&chunk[..CHUNK]); prev_rolling_hash = Some(rolling_hash); - self.subchip.permute_mut(rolling_hash); + self.subchip.permute_mut(&mut rolling_hash); } let final_opened_index = opened_index - 1; let (initial_height_read, height_check) = memory.read_cell( @@ -288,7 +286,7 @@ impl InstructionExecutor ); let root_is_on_right = root_is_on_right == F::ONE; - let (read_sibling_array_start, sibling_array_start) = memory.unsafe_read_cell( + let (read_sibling_array_start, sibling_array_start) = memory.read_cell( address_space, sibling_base_pointer + F::from_canonical_usize(proof_index), ); @@ -318,7 +316,7 @@ impl InstructionExecutor read_root_is_on_right, root_is_on_right, sibling, - reads, + reads: std::array::from_fn(|i| reads[i]), p2_input, }) }; @@ -335,7 +333,7 @@ impl InstructionExecutor assert_eq!(commit, root); self.records.push(VerifyBatchRecord { from_state, - instruction, + instruction: instruction.clone(), dim_base_pointer, opened_base_pointer, opened_length, @@ -348,7 +346,7 @@ impl InstructionExecutor index_base_pointer_read, commit_pointer_read, commit_read, - initial_height, + initial_height: initial_height as usize, top_level, }); diff --git a/extensions/native/circuit/src/verify_batch/columns.rs b/extensions/native/circuit/src/verify_batch/columns.rs index ecc8ede069..6addb47aaf 100644 --- a/extensions/native/circuit/src/verify_batch/columns.rs +++ b/extensions/native/circuit/src/verify_batch/columns.rs @@ -47,6 +47,7 @@ pub struct VerifyBatchCols { pub sibling_base_pointer: T, pub index_base_pointer: T, + // these can be optimized to be shared with cells[i].read_row_pointer_and_length (saves 15 cols) pub dim_base_pointer_read: MemoryReadAuxCols, pub opened_base_pointer_and_length_read: MemoryReadAuxCols, pub sibling_base_pointer_read: MemoryReadAuxCols, @@ -55,6 +56,7 @@ pub struct VerifyBatchCols { pub proof_index: T, + // these as well (saves 6 cols) pub read_initial_height_or_root_is_on_right: MemoryReadAuxCols, pub read_final_height_or_sibling_array_start: MemoryReadAuxCols, @@ -62,6 +64,7 @@ pub struct VerifyBatchCols { pub sibling_array_start: T, pub commit_pointer: T, + // this as well (saves 3 cols) pub commit_read: MemoryReadAuxCols, } diff --git a/extensions/native/circuit/src/verify_batch/trace.rs b/extensions/native/circuit/src/verify_batch/trace.rs index 353e6de528..0624058f40 100644 --- a/extensions/native/circuit/src/verify_batch/trace.rs +++ b/extensions/native/circuit/src/verify_batch/trace.rs @@ -1,20 +1,27 @@ -use std::sync::Arc; +use std::{borrow::BorrowMut, sync::Arc}; use itertools::Itertools; use openvm_circuit::system::memory::{MemoryAuxColsFactory, OfflineMemory}; -use openvm_poseidon2_air::{p3_poseidon2_air::Poseidon2Cols, Poseidon2SubCols}; +use openvm_circuit_primitives::utils::next_power_of_two_or_zero; use openvm_stark_backend::{ + config::{StarkGenericConfig, Val}, p3_air::BaseAir, p3_field::{Field, PrimeField32}, - ChipUsageGetter, + p3_matrix::dense::RowMajorMatrix, + prover::types::AirProofInput, + rap::AnyRap, + Chip, ChipUsageGetter, }; +use rayon::{iter::ParallelIterator, slice::ParallelSliceMut}; use crate::verify_batch::{ - chip::{IncorporateRowRecord, IncorporateSiblingRecord, VerifyBatchChip, VerifyBatchRecord}, - columns::{VerifyBatchCellCols, VerifyBatchCols}, + chip::{ + CellRecord, IncorporateRowRecord, IncorporateSiblingRecord, InsideRowRecord, + VerifyBatchChip, VerifyBatchRecord, + }, + columns::VerifyBatchCols, CHUNK, }; -use crate::verify_batch::chip::{CellRecord, InsideRowRecord}; impl ChipUsageGetter for VerifyBatchChip { fn air_name(&self) -> String { @@ -67,11 +74,11 @@ impl VerifyBatchChip VerifyBatchChip VerifyBatchChip VerifyBatchChip = slice.borrow_mut(); cols.pc = F::from_canonical_u32(from_state.pc); cols.dim_register = instruction.a; @@ -118,12 +125,17 @@ impl VerifyBatchChip VerifyBatchChip, parent: &VerifyBatchRecord, proof_index: usize, - is_first: bool, height: usize, ) { let &IncorporateRowRecord { - chunks, initial_opened_index, final_opened_index, initial_height_read, final_height_read, p2_input, + .. } = record; let initial_height_read = memory.record_by_id(initial_height_read); @@ -156,25 +167,29 @@ impl VerifyBatchChip VerifyBatchChip, is_last: bool, ) { - let &InsideRowRecord { - cells, p2_input - } = record; + let InsideRowRecord { cells, p2_input } = record; - self.generate_subair_cols(p2_input, slice); + self.generate_subair_cols(*p2_input, slice); let cols: &mut VerifyBatchCols = slice.borrow_mut(); cols.incorporate_row = F::ZERO; cols.incorporate_sibling = F::ZERO; cols.inside_row = F::ONE; cols.end_inside_row = F::from_bool(is_last); cols.end_top_level = F::ZERO; - cols.very_first_timestamp = F::from_canonical_u32(memory.record_by_id(parent.chunks[0].cells[0].read_row_pointer_and_length.unwrap()).timestamp); - cols.start_timestamp = F::from_canonical_u32(memory.record_by_id(cells[0].read_row_pointer_and_length.unwrap()).timestamp); - cols.address_space = grandparent.address_space(); - - for (record, cell) in cells.into_iter().zip(cols.cells.iter_mut()) { - let CellRecord { read, opened_index, read_row_pointer_and_length, row_pointer, row_end } = record; + cols.very_first_timestamp = F::from_canonical_u32( + memory + .record_by_id( + parent.chunks[0].cells[0] + .read_row_pointer_and_length + .unwrap(), + ) + .timestamp, + ); + cols.start_timestamp = F::from_canonical_u32( + memory + .record_by_id(cells[0].read_row_pointer_and_length.unwrap()) + .timestamp, + ); + cols.address_space = F::from_canonical_usize(grandparent.address_space()); + + for (record, cell) in cells.iter().zip(cols.cells.iter_mut()) { + let &CellRecord { + read, + opened_index, + read_row_pointer_and_length, + row_pointer, + row_end, + } = record; cell.read = aux_cols_factory.make_read_aux_cols(memory.record_by_id(read)); cell.opened_index = F::from_canonical_usize(opened_index); if let Some(read_row_pointer_and_length) = read_row_pointer_and_length { - cell.read_row_pointer_and_length = aux_cols_factory.make_read_aux_cols(memory.record_by_id(read_row_pointer_and_length)); + cell.read_row_pointer_and_length = aux_cols_factory + .make_read_aux_cols(memory.record_by_id(read_row_pointer_and_length)); } cell.row_pointer = F::from_canonical_usize(row_pointer); cell.row_end = F::from_canonical_usize(row_end); @@ -224,105 +256,77 @@ impl VerifyBatchChip, + &self, + record: &VerifyBatchRecord, aux_cols_factory: &MemoryAuxColsFactory, slice: &mut [F], memory: &OfflineMemory, - ) { + ) -> usize { let width = VerifyBatchCols::::width(); + let mut used_cells = 0; + + let mut proof_index = 0; + let mut height = record.initial_height; + let mut opened_index = 0; + for top_level in record.top_level.iter() { + if let Some(incorporate_row) = &top_level.incorporate_row { + self.incorporate_row_record_to_row( + &incorporate_row, + aux_cols_factory, + &mut slice[used_cells..used_cells + width], + memory, + &record, + proof_index, + height, + ); + opened_index = incorporate_row.final_opened_index + 1; + used_cells += width; + } + if let Some(incorporate_sibling) = &top_level.incorporate_sibling { + self.incorporate_sibling_record_to_row( + &incorporate_sibling, + aux_cols_factory, + &mut slice[used_cells..used_cells + width], + memory, + &record, + proof_index, + height, + opened_index, + ); + used_cells += width; + } + height /= 2; + proof_index += 1; + } + self.correct_last_top_level_row( + record, + aux_cols_factory, + &mut slice[used_cells - width..used_cells], + memory, + ); - let Instruction { - a: a_ptr_ptr, - b: b_ptr_ptr, - c: result_ptr, - d: addr_space, - e: length_ptr, - f: alpha_ptr, - g: alpha_pow_ptr, - .. - } = record.instruction; - - let length_read = memory.record_by_id(record.length_read); - let alpha_read = memory.record_by_id(record.alpha_read); - let a_ptr_read = memory.record_by_id(record.a_ptr_read); - let b_ptr_read = memory.record_by_id(record.b_ptr_read); - - let length = length_read.data[0].as_canonical_u32() as usize; - let alpha: [F; EXT_DEG] = array::from_fn(|i| alpha_read.data[i]); - let a_ptr = a_ptr_read.data[0]; - let b_ptr = b_ptr_read.data[0]; - - let mut alpha_pow_current = record.alpha_pow_original; - let mut current = [F::ZERO; EXT_DEG]; - - let alpha_aux = aux_cols_factory.make_read_aux_cols(alpha_read); - let length_aux = aux_cols_factory.make_read_aux_cols(length_read); - let a_ptr_aux = aux_cols_factory.make_read_aux_cols(a_ptr_read); - let b_ptr_aux = aux_cols_factory.make_read_aux_cols(b_ptr_read); - - let alpha_pow_aux = aux_cols_factory - .make_write_aux_cols::(memory.record_by_id(record.alpha_pow_write)) - .get_base(); - let result_aux = - aux_cols_factory.make_write_aux_cols(memory.record_by_id(record.result_write)); - - for i in 0..length { - let a_read = memory.record_by_id(record.a_reads[i]); - let b_read = memory.record_by_id(record.b_reads[i]); - let a = a_read.data[0]; - let b: [F; EXT_DEG] = array::from_fn(|i| b_read.data[i]); - current = FieldExtension::add( - current, - FieldExtension::multiply( - FieldExtension::subtract(b, elem_to_ext(a)), - alpha_pow_current, - ), - ); - - let mut idx_is_zero = F::ZERO; - let mut is_zero_aux = F::ZERO; - - let idx = F::from_canonical_usize(i); - IsZeroSubAir.generate_subrow(idx, (&mut is_zero_aux, &mut idx_is_zero)); - - let cols: &mut VerifyBatchCols = slice[i * width..(i + 1) * width].borrow_mut(); - *cols = VerifyBatchCols { - enabled: F::ONE, - pc: record.pc, - a_ptr_ptr, - b_ptr_ptr, - result_ptr, - addr_space, - length_ptr, - alpha_ptr, - alpha_pow_ptr, - start_timestamp: record.start_timestamp, - a_ptr_aux, - b_ptr_aux, - a_aux: aux_cols_factory.make_read_aux_cols(a_read), - b_aux: aux_cols_factory.make_read_aux_cols(b_read), - alpha_aux, - length_aux, - alpha_pow_aux, - result_aux, - a_ptr, - b_ptr, - a, - b, - alpha, - alpha_pow_original: record.alpha_pow_original, - alpha_pow_current, - idx, - idx_is_zero, - is_zero_aux, - current, - }; - - alpha_pow_current = FieldExtension::multiply(alpha, alpha_pow_current); + for top_level in record.top_level.iter() { + if let Some(incorporate_row) = &top_level.incorporate_row { + for (i, chunk) in incorporate_row.chunks.iter().enumerate() { + self.inside_row_record_to_row( + &chunk, + aux_cols_factory, + &mut slice[used_cells..used_cells + width], + memory, + &incorporate_row, + &record, + i == incorporate_row.chunks.len() - 1, + ); + used_cells += width; + } + } } + + used_cells } fn generate_trace(self) -> RowMajorMatrix { @@ -334,36 +338,34 @@ impl VerifyBatchChip = row.borrow_mut(); - row.idx_is_zero = F::ONE; + self.generate_subair_cols([F::ZERO; 2 * CHUNK], row); }); RowMajorMatrix::new(flat_trace, width) } } -impl Chip for VerifyBatchChip> +impl Chip + for VerifyBatchChip, SBOX_REGISTERS> where Val: PrimeField32, { fn air(&self) -> Arc> { - Arc::new(self.air) + Arc::new(self.air.clone()) } fn generate_air_proof_input(self) -> AirProofInput { AirProofInput::simple_no_pis(self.air(), self.generate_trace()) From 0353599c4bf3d5c4fc9d03f80cd01e6a0d1dd97e Mon Sep 17 00:00:00 2001 From: TlatoaniHJ Date: Tue, 14 Jan 2025 14:33:45 -0500 Subject: [PATCH 14/50] Implement verify_batch for test --- .../native/circuit/src/verify_batch/tests.rs | 72 ++++++++++++++----- 1 file changed, 54 insertions(+), 18 deletions(-) diff --git a/extensions/native/circuit/src/verify_batch/tests.rs b/extensions/native/circuit/src/verify_batch/tests.rs index 23ffea2ae0..8c87f289df 100644 --- a/extensions/native/circuit/src/verify_batch/tests.rs +++ b/extensions/native/circuit/src/verify_batch/tests.rs @@ -1,7 +1,4 @@ use itertools::Itertools; -use openvm_circuit::arch::testing::{memory::gen_pointer, VmChipTestBuilder}; -use openvm_instructions::{instruction::Instruction, UsizeOpcode, VmOpcode}; -use openvm_native_compiler::FriOpcode::{self, FRI_REDUCED_OPENING}; use openvm_stark_backend::{ p3_field::{Field, FieldAlgebra}, utils::disable_debug_builder, @@ -10,24 +7,63 @@ use openvm_stark_backend::{ use openvm_stark_sdk::{p3_baby_bear::BabyBear, utils::create_seeded_rng}; use rand::Rng; -use super::{super::field_extension::FieldExtension, elem_to_ext, EXT_DEG}; +use openvm_circuit::arch::testing::{memory::gen_pointer, VmChipTestBuilder}; +use openvm_circuit::system::memory::CHUNK; +use openvm_instructions::{instruction::Instruction, UsizeOpcode, VmOpcode}; +use openvm_native_compiler::FriOpcode::{self, FRI_REDUCED_OPENING}; + use crate::verify_batch::{chip::VerifyBatchChip, columns::VerifyBatchCols}; -fn compute_fri_mat_opening( - alpha: [F; EXT_DEG], - mut alpha_pow: [F; EXT_DEG], - a: &[F], - b: &[[F; EXT_DEG]], -) -> ([F; EXT_DEG], [F; EXT_DEG]) { - let mut result = [F::ZERO; EXT_DEG]; - for (&a, &b) in a.iter().zip_eq(b) { - result = FieldExtension::add( - result, - FieldExtension::multiply(FieldExtension::subtract(b, elem_to_ext(a)), alpha_pow), - ); - alpha_pow = FieldExtension::multiply(alpha, alpha_pow); +use super::EXT_DEG; + +fn compute_commit( + dim: &Vec, + opened: &Vec>, + proof: &Vec<[F; CHUNK]>, + root_is_on_right: &Vec, + hash_function: impl Fn([F; CHUNK], [F; CHUNK]) -> ([F; CHUNK], [F; CHUNK]), +) -> [F; CHUNK] { + let mut height = dim[0]; + let mut proof_index = 0; + let mut opened_index = 0; + let mut root = [F::ZERO; CHUNK]; + while height >= 1 { + let mut concat = vec![]; + while opened_index < opened.len() && dim[opened_index] == height { + concat.extend(opened[opened_index].clone()); + opened_index += 1; + } + while concat.len() % CHUNK != 0 { + concat.push(F::ZERO); + } + if !concat.is_empty() { + let mut left = [F::ZERO; CHUNK]; + let mut right = [F::ZERO; CHUNK]; + for i in (0..concat.len()).step_by(CHUNK) { + let chunk = std::array::from_fn(|j| concat[i + j]); + let (new_left, new_right) = hash_function(chunk, right); + left = new_left; + right = new_right; + } + root = if height == dim[0] { + left + } else { + hash_function(root, left).0 + } + } + if height > 1 { + let sibling = proof[proof_index]; + let (left, right) = if root_is_on_right[proof_index] { + (sibling, root) + } else { + (root, sibling) + }; + root = hash_function(left, right).0; + } + height /= 2; + proof_index += 1; } - (alpha_pow, result) + root } #[test] From 6fac595122dbdff94e4898497bb14f76415bb540 Mon Sep 17 00:00:00 2001 From: TlatoaniHJ Date: Tue, 14 Jan 2025 15:26:14 -0500 Subject: [PATCH 15/50] Write test (only positive for now) --- crates/vm/src/arch/testing/mod.rs | 4 + .../circuit/src/verify_batch/columns.rs | 11 +- .../native/circuit/src/verify_batch/tests.rs | 206 ++++++++++-------- 3 files changed, 132 insertions(+), 89 deletions(-) diff --git a/crates/vm/src/arch/testing/mod.rs b/crates/vm/src/arch/testing/mod.rs index 2dd1d2963e..808b47d522 100644 --- a/crates/vm/src/arch/testing/mod.rs +++ b/crates/vm/src/arch/testing/mod.rs @@ -142,6 +142,10 @@ impl VmChipTestBuilder { self.memory.write(address_space, pointer, value); } + pub fn write_usize(&mut self, address_space: usize, pointer: usize, value: [usize; N]) { + self.memory.write(address_space, pointer, value.map(F::from_canonical_usize)); + } + pub fn write_heap( &mut self, register: usize, diff --git a/extensions/native/circuit/src/verify_batch/columns.rs b/extensions/native/circuit/src/verify_batch/columns.rs index 6addb47aaf..760dea3561 100644 --- a/extensions/native/circuit/src/verify_batch/columns.rs +++ b/extensions/native/circuit/src/verify_batch/columns.rs @@ -19,13 +19,15 @@ pub struct VerifyBatchCols { pub end_top_level: T, pub start_top_level: T, - // execution state + // execution state (pc can be shared (saves 1 col), very_first_timestamp is used in a different way inside row) pub pc: T, pub very_first_timestamp: T, + pub start_timestamp: T, - pub end_timestamp: T, // only used for top level + pub end_timestamp: T, // only used for top level (so can be shared (saves 1 col) // instruction (a, b, c, d, e) + // all other than address_space can be shared (saves 5 cols) pub dim_register: T, pub opened_register: T, pub sibling_register: T, @@ -37,8 +39,10 @@ pub struct VerifyBatchCols { // initial/final opened index for a subsegment with same height // initial is used in both, final is used only in top level pub initial_opened_index: T, + // so then this one can be shared as well (saves 1 col) pub final_opened_index: T, + // these two ig? (saves 2 cols) pub height: T, pub opened_length: T, @@ -54,15 +58,18 @@ pub struct VerifyBatchCols { pub index_base_pointer_read: MemoryReadAuxCols, pub commit_pointer_read: MemoryReadAuxCols, + // this with the other ones (saves 1 col) pub proof_index: T, // these as well (saves 6 cols) pub read_initial_height_or_root_is_on_right: MemoryReadAuxCols, pub read_final_height_or_sibling_array_start: MemoryReadAuxCols, + // i guess these can be shared with like the other ones (saves 2 cols) pub root_is_on_right: T, pub sibling_array_start: T, + // plus this (saves 1 col) pub commit_pointer: T, // this as well (saves 3 cols) pub commit_read: MemoryReadAuxCols, diff --git a/extensions/native/circuit/src/verify_batch/tests.rs b/extensions/native/circuit/src/verify_batch/tests.rs index 8c87f289df..d04283baa2 100644 --- a/extensions/native/circuit/src/verify_batch/tests.rs +++ b/extensions/native/circuit/src/verify_batch/tests.rs @@ -6,11 +6,15 @@ use openvm_stark_backend::{ }; use openvm_stark_sdk::{p3_baby_bear::BabyBear, utils::create_seeded_rng}; use rand::Rng; +use rand::rngs::StdRng; use openvm_circuit::arch::testing::{memory::gen_pointer, VmChipTestBuilder}; use openvm_circuit::system::memory::CHUNK; use openvm_instructions::{instruction::Instruction, UsizeOpcode, VmOpcode}; -use openvm_native_compiler::FriOpcode::{self, FRI_REDUCED_OPENING}; +use openvm_native_compiler::FriOpcode::FRI_REDUCED_OPENING; +use openvm_native_compiler::VerifyBatchOpcode; +use openvm_native_compiler::VerifyBatchOpcode::VERIFY_BATCH; +use openvm_poseidon2_air::Poseidon2Config; use crate::verify_batch::{chip::VerifyBatchChip, columns::VerifyBatchCols}; @@ -66,109 +70,137 @@ fn compute_commit( root } -#[test] -fn fri_mat_opening_air_test() { - let num_ops = 3; // non-power-of-2 to also test padding - let elem_range = || 1..=100; - let address_space_range = || 1usize..=2; - let length_range = || 1..=49; +type F = BabyBear; - let offset = FriOpcode::default_offset(); +struct VerifyBatchInstance { + dim: Vec, + opened: Vec>, + proof: Vec<[F; CHUNK]>, + root_is_on_right: Vec, + commit: [F; CHUNK], +} + +fn random_instance(rng: &mut StdRng, row_lengths: Vec>, hash_function: impl Fn([F; CHUNK], [F; CHUNK]) -> ([F; CHUNK], [F; CHUNK])) -> VerifyBatchInstance { + let mut dims = vec![]; + let mut opened = vec![]; + let mut proof = vec![]; + let mut root_is_on_right = vec![]; + for (lg_height, row_lengths) in row_lengths.iter().enumerate() { + let height = 1 << lg_height; + for &row_length in row_lengths { + dims.push(height); + let mut opened_row = vec![]; + for _ in 0..row_length { + opened_row.push(rng.gen()); + } + opened.push(opened_row); + } + if height > 1 { + proof.push(std::array::from_fn(|_| rng.gen())); + root_is_on_right.push(rng.gen()); + } + } + + dims.reverse(); + opened.reverse(); + proof.reverse(); + root_is_on_right.reverse(); + + let commit = compute_commit(&dims, &opened, &proof, &root_is_on_right, hash_function); + + VerifyBatchInstance { + dim: dims, + opened, + proof, + root_is_on_right, + commit, + } +} + +#[test] +fn verify_batch_air_test() { + // single op + let address_space = 5; + let row_lengths = vec![vec![3], vec![], vec![9, 2, 1, 13, 4], vec![16]]; + + let offset = VerifyBatchOpcode::default_offset(); let mut tester = VmChipTestBuilder::default(); - let mut chip = VerifyBatchChip::new( + let mut chip = VerifyBatchChip::::new( tester.execution_bus(), tester.program_bus(), tester.memory_bridge(), offset, tester.offline_memory_mutex_arc(), + Poseidon2Config::default(), ); let mut rng = create_seeded_rng(); - - macro_rules! gen_ext { - () => { - std::array::from_fn::<_, EXT_DEG, _>(|_| { - BabyBear::from_canonical_u32(rng.gen_range(elem_range())) - }) - }; + let instance = random_instance(&mut rng, row_lengths, |left, right| { + let concatenated = + std::array::from_fn(|i| if i < CHUNK { left[i] } else { right[i - CHUNK] }); + let permuted = chip.subchip.permute(concatenated); + (std::array::from_fn(|i| permuted[i]), std::array::from_fn(|i| permuted[i + CHUNK])) + }); + let VerifyBatchInstance { dim, opened, proof, root_is_on_right, commit } = instance; + + let dim_register = gen_pointer(&mut rng, 2); + let opened_register = gen_pointer(&mut rng, 2); + let sibling_register = gen_pointer(&mut rng, 2); + let index_register = gen_pointer(&mut rng, 2); + let commit_register = gen_pointer(&mut rng, 2); + + let dim_base_pointer = gen_pointer(&mut rng, 1); + let opened_base_pointer = gen_pointer(&mut rng, 2); + let sibling_base_pointer = gen_pointer(&mut rng, 1); + let index_base_pointer = gen_pointer(&mut rng, 1); + let commit_pointer = gen_pointer(&mut rng, 1); + + tester.write_usize(address_space, dim_register, [dim_base_pointer, dim.len()]); + tester.write_usize(address_space, opened_register, [opened_base_pointer, opened.len()]); + tester.write_usize(address_space, sibling_register, [sibling_base_pointer, proof.len()]); + tester.write_usize(address_space, index_register, [index_base_pointer, root_is_on_right.len()]); + tester.write_usize(address_space, commit_register, [commit_pointer, CHUNK]); + + for (i, &dim_value) in dim.iter().enumerate() { + tester.write_usize(address_space, dim_base_pointer + i, [dim_value]); } - - for _ in 0..num_ops { - let alpha = gen_ext!(); - let length = rng.gen_range(length_range()); - let alpha_pow_initial = gen_ext!(); - let a = (0..length) - .map(|_| BabyBear::from_canonical_u32(rng.gen_range(elem_range()))) - .collect_vec(); - let b = (0..length).map(|_| gen_ext!()).collect_vec(); - - let (alpha_pow_final, result) = compute_fri_mat_opening(alpha, alpha_pow_initial, &a, &b); - - let alpha_pointer = gen_pointer(&mut rng, 4); - let length_pointer = gen_pointer(&mut rng, 1); - let a_pointer_pointer = gen_pointer(&mut rng, 1); - let b_pointer_pointer = gen_pointer(&mut rng, 1); - let alpha_pow_pointer = gen_pointer(&mut rng, 4); - let result_pointer = gen_pointer(&mut rng, 4); - let a_pointer = gen_pointer(&mut rng, 1); - let b_pointer = gen_pointer(&mut rng, 4); - - let address_space = rng.gen_range(address_space_range()); - - /*tracing::debug!( - "{opcode:?} d = {}, e = {}, f = {}, result_addr = {}, addr1 = {}, addr2 = {}, z = {}, x = {}, y = {}", - result_as, as1, as2, result_pointer, address1, address2, result, operand1, operand2, - );*/ - - tester.write(address_space, alpha_pointer, alpha); - tester.write_cell( - address_space, - length_pointer, - BabyBear::from_canonical_usize(length), - ); - tester.write_cell( - address_space, - a_pointer_pointer, - BabyBear::from_canonical_usize(a_pointer), - ); - tester.write_cell( - address_space, - b_pointer_pointer, - BabyBear::from_canonical_usize(b_pointer), - ); - tester.write(address_space, alpha_pow_pointer, alpha_pow_initial); - for i in 0..length { - tester.write_cell(address_space, a_pointer + i, a[i]); - tester.write(address_space, b_pointer + (4 * i), b[i]); + for (i, opened_row) in opened.iter().enumerate() { + let row_pointer = gen_pointer(&mut rng, 1); + tester.write_usize(address_space, opened_base_pointer + (2 * i), [row_pointer, opened_row.len()]); + for (j, &opened_value) in opened_row.iter().enumerate() { + tester.write_cell(address_space, row_pointer + j, opened_value); } - - tester.execute( - &mut chip, - &Instruction::from_usize( - VmOpcode::from_usize(FRI_REDUCED_OPENING as usize + offset), - [ - a_pointer_pointer, - b_pointer_pointer, - result_pointer, - address_space, - length_pointer, - alpha_pointer, - alpha_pow_pointer, - ], - ), - ); - assert_eq!( - alpha_pow_final, - tester.read(address_space, alpha_pow_pointer) - ); - assert_eq!(result, tester.read(address_space, result_pointer)); } + for (i, &sibling) in proof.iter().enumerate() { + let row_pointer = gen_pointer(&mut rng, 1); + tester.write_usize(address_space, sibling_base_pointer + i, [row_pointer]); + tester.write(address_space, row_pointer, sibling); + } + for (i, &bit) in root_is_on_right.iter().enumerate() { + tester.write_cell(address_space, index_base_pointer + i, F::from_bool(bit)); + } + tester.write(address_space, commit_pointer, commit); + + tester.execute( + &mut chip, + &Instruction::from_usize( + VmOpcode::from_usize(VERIFY_BATCH as usize + offset), + [ + dim_register, + opened_register, + sibling_register, + index_register, + commit_register, + address_space, + ], + ), + ); let mut tester = tester.build().load(chip).finalize(); tester.simple_test().expect("Verification failed"); - disable_debug_builder(); + /*disable_debug_builder(); // negative test pranking each value for height in 0..num_ops { // TODO: better way to modify existing traces in tester @@ -189,5 +221,5 @@ fn fri_mat_opening_air_test() { ); tester.air_proof_inputs[2].raw.common_main = Some(old_trace); - } + }*/ } From 78ba3f6661d871b9b8b4047fd24a17f0ed29e14a Mon Sep 17 00:00:00 2001 From: TlatoaniHJ Date: Tue, 14 Jan 2025 15:29:32 -0500 Subject: [PATCH 16/50] Some cleanup --- crates/vm/src/arch/testing/mod.rs | 10 ++- .../native/circuit/src/verify_batch/chip.rs | 4 +- .../circuit/src/verify_batch/columns.rs | 2 +- .../native/circuit/src/verify_batch/mod.rs | 28 ------ .../native/circuit/src/verify_batch/tests.rs | 86 ++++++++++++------- .../native/circuit/src/verify_batch/trace.rs | 1 - 6 files changed, 63 insertions(+), 68 deletions(-) diff --git a/crates/vm/src/arch/testing/mod.rs b/crates/vm/src/arch/testing/mod.rs index 808b47d522..a89968d2f8 100644 --- a/crates/vm/src/arch/testing/mod.rs +++ b/crates/vm/src/arch/testing/mod.rs @@ -142,8 +142,14 @@ impl VmChipTestBuilder { self.memory.write(address_space, pointer, value); } - pub fn write_usize(&mut self, address_space: usize, pointer: usize, value: [usize; N]) { - self.memory.write(address_space, pointer, value.map(F::from_canonical_usize)); + pub fn write_usize( + &mut self, + address_space: usize, + pointer: usize, + value: [usize; N], + ) { + self.memory + .write(address_space, pointer, value.map(F::from_canonical_usize)); } pub fn write_heap( diff --git a/extensions/native/circuit/src/verify_batch/chip.rs b/extensions/native/circuit/src/verify_batch/chip.rs index 4fff6aa22d..0ea3ccabe3 100644 --- a/extensions/native/circuit/src/verify_batch/chip.rs +++ b/extensions/native/circuit/src/verify_batch/chip.rs @@ -10,7 +10,7 @@ use openvm_circuit::{ use openvm_instructions::{instruction::Instruction, program::DEFAULT_PC_STEP}; use openvm_native_compiler::VerifyBatchOpcode::VERIFY_BATCH; use openvm_poseidon2_air::{Poseidon2Config, Poseidon2SubAir, Poseidon2SubChip}; -use openvm_stark_backend::p3_field::{Field, FieldAlgebra, PrimeField32}; +use openvm_stark_backend::p3_field::{Field, PrimeField32}; use crate::verify_batch::{ air::{VerifyBatchAir, VerifyBatchBus}, @@ -56,7 +56,6 @@ pub(super) struct IncorporateSiblingRecord { pub read_sibling_array_start: RecordId, pub read_root_is_on_right: RecordId, pub root_is_on_right: bool, - pub sibling: [F; CHUNK], pub reads: [RecordId; CHUNK], pub p2_input: [F; 2 * CHUNK], } @@ -315,7 +314,6 @@ impl InstructionExecutor read_sibling_array_start, read_root_is_on_right, root_is_on_right, - sibling, reads: std::array::from_fn(|i| reads[i]), p2_input, }) diff --git a/extensions/native/circuit/src/verify_batch/columns.rs b/extensions/native/circuit/src/verify_batch/columns.rs index 760dea3561..8ec3c7c9cd 100644 --- a/extensions/native/circuit/src/verify_batch/columns.rs +++ b/extensions/native/circuit/src/verify_batch/columns.rs @@ -22,7 +22,7 @@ pub struct VerifyBatchCols { // execution state (pc can be shared (saves 1 col), very_first_timestamp is used in a different way inside row) pub pc: T, pub very_first_timestamp: T, - + pub start_timestamp: T, pub end_timestamp: T, // only used for top level (so can be shared (saves 1 col) diff --git a/extensions/native/circuit/src/verify_batch/mod.rs b/extensions/native/circuit/src/verify_batch/mod.rs index 0b45b4f8e3..5e30a52f10 100644 --- a/extensions/native/circuit/src/verify_batch/mod.rs +++ b/extensions/native/circuit/src/verify_batch/mod.rs @@ -1,31 +1,3 @@ -use std::{ - borrow::{Borrow, BorrowMut}, - sync::Arc, -}; - -use chip::{VerifyBatchChip, VerifyBatchRecord}; -use columns::VerifyBatchCols; -use openvm_circuit::{ - arch::InstructionExecutor, - system::memory::{MemoryAuxColsFactory, OfflineMemory}, -}; -use openvm_circuit_primitives::{ - is_zero::IsZeroSubAir, utils::next_power_of_two_or_zero, SubAir, TraceSubRowGenerator, -}; -use openvm_instructions::instruction::Instruction; -use openvm_stark_backend::{ - config::{StarkGenericConfig, Val}, - interaction::InteractionBuilder, - p3_air::{Air, AirBuilder, BaseAir}, - p3_field::{Field, FieldAlgebra, PrimeField32}, - p3_matrix::{dense::RowMajorMatrix, Matrix}, - p3_maybe_rayon::prelude::*, - prover::types::AirProofInput, - rap::{AnyRap, BaseAirWithPublicValues, PartitionedBaseAir}, - Chip, ChipUsageGetter, -}; - -use super::field_extension::{FieldExtension, EXT_DEG}; use crate::NATIVE_POSEIDON2_CHUNK_SIZE; mod air; diff --git a/extensions/native/circuit/src/verify_batch/tests.rs b/extensions/native/circuit/src/verify_batch/tests.rs index d04283baa2..ca61566e90 100644 --- a/extensions/native/circuit/src/verify_batch/tests.rs +++ b/extensions/native/circuit/src/verify_batch/tests.rs @@ -1,24 +1,15 @@ -use itertools::Itertools; -use openvm_stark_backend::{ - p3_field::{Field, FieldAlgebra}, - utils::disable_debug_builder, - verifier::VerificationError, +use openvm_circuit::{ + arch::testing::{memory::gen_pointer, VmChipTestBuilder}, + system::memory::CHUNK, }; -use openvm_stark_sdk::{p3_baby_bear::BabyBear, utils::create_seeded_rng}; -use rand::Rng; -use rand::rngs::StdRng; - -use openvm_circuit::arch::testing::{memory::gen_pointer, VmChipTestBuilder}; -use openvm_circuit::system::memory::CHUNK; use openvm_instructions::{instruction::Instruction, UsizeOpcode, VmOpcode}; -use openvm_native_compiler::FriOpcode::FRI_REDUCED_OPENING; -use openvm_native_compiler::VerifyBatchOpcode; -use openvm_native_compiler::VerifyBatchOpcode::VERIFY_BATCH; +use openvm_native_compiler::{VerifyBatchOpcode, VerifyBatchOpcode::VERIFY_BATCH}; use openvm_poseidon2_air::Poseidon2Config; +use openvm_stark_backend::p3_field::{Field, FieldAlgebra}; +use openvm_stark_sdk::{p3_baby_bear::BabyBear, utils::create_seeded_rng}; +use rand::{rngs::StdRng, Rng}; -use crate::verify_batch::{chip::VerifyBatchChip, columns::VerifyBatchCols}; - -use super::EXT_DEG; +use crate::verify_batch::chip::VerifyBatchChip; fn compute_commit( dim: &Vec, @@ -80,7 +71,11 @@ struct VerifyBatchInstance { commit: [F; CHUNK], } -fn random_instance(rng: &mut StdRng, row_lengths: Vec>, hash_function: impl Fn([F; CHUNK], [F; CHUNK]) -> ([F; CHUNK], [F; CHUNK])) -> VerifyBatchInstance { +fn random_instance( + rng: &mut StdRng, + row_lengths: Vec>, + hash_function: impl Fn([F; CHUNK], [F; CHUNK]) -> ([F; CHUNK], [F; CHUNK]), +) -> VerifyBatchInstance { let mut dims = vec![]; let mut opened = vec![]; let mut proof = vec![]; @@ -100,14 +95,14 @@ fn random_instance(rng: &mut StdRng, row_lengths: Vec>, hash_function root_is_on_right.push(rng.gen()); } } - + dims.reverse(); opened.reverse(); proof.reverse(); root_is_on_right.reverse(); - + let commit = compute_commit(&dims, &opened, &proof, &root_is_on_right, hash_function); - + VerifyBatchInstance { dim: dims, opened, @@ -122,7 +117,7 @@ fn verify_batch_air_test() { // single op let address_space = 5; let row_lengths = vec![vec![3], vec![], vec![9, 2, 1, 13, 4], vec![16]]; - + let offset = VerifyBatchOpcode::default_offset(); let mut tester = VmChipTestBuilder::default(); @@ -140,34 +135,59 @@ fn verify_batch_air_test() { let concatenated = std::array::from_fn(|i| if i < CHUNK { left[i] } else { right[i - CHUNK] }); let permuted = chip.subchip.permute(concatenated); - (std::array::from_fn(|i| permuted[i]), std::array::from_fn(|i| permuted[i + CHUNK])) + ( + std::array::from_fn(|i| permuted[i]), + std::array::from_fn(|i| permuted[i + CHUNK]), + ) }); - let VerifyBatchInstance { dim, opened, proof, root_is_on_right, commit } = instance; - + let VerifyBatchInstance { + dim, + opened, + proof, + root_is_on_right, + commit, + } = instance; + let dim_register = gen_pointer(&mut rng, 2); let opened_register = gen_pointer(&mut rng, 2); let sibling_register = gen_pointer(&mut rng, 2); let index_register = gen_pointer(&mut rng, 2); let commit_register = gen_pointer(&mut rng, 2); - + let dim_base_pointer = gen_pointer(&mut rng, 1); let opened_base_pointer = gen_pointer(&mut rng, 2); let sibling_base_pointer = gen_pointer(&mut rng, 1); let index_base_pointer = gen_pointer(&mut rng, 1); let commit_pointer = gen_pointer(&mut rng, 1); - + tester.write_usize(address_space, dim_register, [dim_base_pointer, dim.len()]); - tester.write_usize(address_space, opened_register, [opened_base_pointer, opened.len()]); - tester.write_usize(address_space, sibling_register, [sibling_base_pointer, proof.len()]); - tester.write_usize(address_space, index_register, [index_base_pointer, root_is_on_right.len()]); + tester.write_usize( + address_space, + opened_register, + [opened_base_pointer, opened.len()], + ); + tester.write_usize( + address_space, + sibling_register, + [sibling_base_pointer, proof.len()], + ); + tester.write_usize( + address_space, + index_register, + [index_base_pointer, root_is_on_right.len()], + ); tester.write_usize(address_space, commit_register, [commit_pointer, CHUNK]); - + for (i, &dim_value) in dim.iter().enumerate() { tester.write_usize(address_space, dim_base_pointer + i, [dim_value]); } for (i, opened_row) in opened.iter().enumerate() { let row_pointer = gen_pointer(&mut rng, 1); - tester.write_usize(address_space, opened_base_pointer + (2 * i), [row_pointer, opened_row.len()]); + tester.write_usize( + address_space, + opened_base_pointer + (2 * i), + [row_pointer, opened_row.len()], + ); for (j, &opened_value) in opened_row.iter().enumerate() { tester.write_cell(address_space, row_pointer + j, opened_value); } @@ -197,7 +217,7 @@ fn verify_batch_air_test() { ), ); - let mut tester = tester.build().load(chip).finalize(); + let tester = tester.build().load(chip).finalize(); tester.simple_test().expect("Verification failed"); /*disable_debug_builder(); diff --git a/extensions/native/circuit/src/verify_batch/trace.rs b/extensions/native/circuit/src/verify_batch/trace.rs index 0624058f40..642ad23b1d 100644 --- a/extensions/native/circuit/src/verify_batch/trace.rs +++ b/extensions/native/circuit/src/verify_batch/trace.rs @@ -58,7 +58,6 @@ impl VerifyBatchChip Date: Tue, 14 Jan 2025 15:48:36 -0500 Subject: [PATCH 17/50] Fix handling of initial cell --- .../native/circuit/src/verify_batch/chip.rs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/extensions/native/circuit/src/verify_batch/chip.rs b/extensions/native/circuit/src/verify_batch/chip.rs index 0ea3ccabe3..8d718e834c 100644 --- a/extensions/native/circuit/src/verify_batch/chip.rs +++ b/extensions/native/circuit/src/verify_batch/chip.rs @@ -179,12 +179,13 @@ impl InstructionExecutor } let mut chunks = vec![]; - opened_index -= 1; let mut row_pointer = 0; let mut row_end = 0; let mut prev_rolling_hash: Option<[F; 2 * CHUNK]> = None; let mut rolling_hash = [F::ZERO; 2 * CHUNK]; + + let mut is_first_in_segment = true; while opened_index < opened_length && memory.unsafe_read_cell( @@ -195,15 +196,19 @@ impl InstructionExecutor let mut cells = vec![]; let mut chunk = [F::ZERO; CHUNK]; for i in 0..CHUNK { - let read_row_pointer_and_length = if row_pointer == row_end { - opened_index += 1; - if opened_index == opened_length - || memory.unsafe_read_cell( + let read_row_pointer_and_length = if is_first_in_segment || row_pointer == row_end { + if is_first_in_segment { + is_first_in_segment = false; + } else { + opened_index += 1; + if opened_index == opened_length + || memory.unsafe_read_cell( address_space, dim_base_pointer + F::from_canonical_usize(opened_index), ) != F::from_canonical_u32(height) - { - break; + { + break; + } } let (result, [new_row_pointer, row_len]) = memory.read( address_space, From 851a8bd45d11fe3b77872cd84bf6ad010d4757bb Mon Sep 17 00:00:00 2001 From: TlatoaniHJ Date: Tue, 14 Jan 2025 18:08:40 -0500 Subject: [PATCH 18/50] Get execute and trace generation working --- .../native/circuit/src/verify_batch/air.rs | 11 +++-- .../native/circuit/src/verify_batch/chip.rs | 49 +++++++++++++------ .../native/circuit/src/verify_batch/tests.rs | 22 +++++++-- .../native/circuit/src/verify_batch/trace.rs | 4 +- 4 files changed, 61 insertions(+), 25 deletions(-) diff --git a/extensions/native/circuit/src/verify_batch/air.rs b/extensions/native/circuit/src/verify_batch/air.rs index 6d7aee6473..bb890685eb 100644 --- a/extensions/native/circuit/src/verify_batch/air.rs +++ b/extensions/native/circuit/src/verify_batch/air.rs @@ -14,7 +14,7 @@ use openvm_stark_backend::{ p3_matrix::Matrix, rap::{BaseAirWithPublicValues, PartitionedBaseAir}, }; - +use openvm_stark_backend::air_builders::sub::SubAirBuilder; use crate::verify_batch::{columns::VerifyBatchCols, CHUNK}; #[derive(Clone, Debug)] @@ -120,7 +120,12 @@ impl Air .when(next.incorporate_row) .assert_one(next.start_top_level); - self.subair.eval(builder); + let mut sub_builder = + SubAirBuilder::, AB::F>::new( + builder, + 0..self.subair.width(), + ); + self.subair.eval(&mut sub_builder); //// inside row constraints @@ -248,7 +253,7 @@ impl Air builder .when(inside_row) .when(is_last_in_row) - .assert_eq(cell.row_pointer, cell.row_end); + .assert_eq(cell.row_pointer + AB::F::ONE, cell.row_end); } //// top level constraints diff --git a/extensions/native/circuit/src/verify_batch/chip.rs b/extensions/native/circuit/src/verify_batch/chip.rs index 8d718e834c..cb4ad3b7bb 100644 --- a/extensions/native/circuit/src/verify_batch/chip.rs +++ b/extensions/native/circuit/src/verify_batch/chip.rs @@ -17,6 +17,7 @@ use crate::verify_batch::{ CHUNK, }; +#[derive(Debug, Clone)] pub struct VerifyBatchRecord { pub from_state: ExecutionState, pub instruction: Instruction, @@ -45,6 +46,7 @@ impl VerifyBatchRecord { } } +#[derive(Debug, Clone)] pub(super) struct TopLevelRecord { // must be present in first record pub incorporate_row: Option>, @@ -52,6 +54,7 @@ pub(super) struct TopLevelRecord { pub incorporate_sibling: Option>, } +#[derive(Debug, Clone)] pub(super) struct IncorporateSiblingRecord { pub read_sibling_array_start: RecordId, pub read_root_is_on_right: RecordId, @@ -60,6 +63,7 @@ pub(super) struct IncorporateSiblingRecord { pub p2_input: [F; 2 * CHUNK], } +#[derive(Debug, Clone)] pub(super) struct IncorporateRowRecord { pub chunks: Vec>, pub initial_opened_index: usize, @@ -69,11 +73,13 @@ pub(super) struct IncorporateRowRecord { pub p2_input: [F; 2 * CHUNK], } +#[derive(Debug, Clone)] pub(super) struct InsideRowRecord { pub cells: Vec, pub p2_input: [F; 2 * CHUNK], } +#[derive(Debug, Clone)] pub(super) struct CellRecord { pub read: RecordId, pub opened_index: usize, @@ -119,6 +125,10 @@ impl VerifyBatchChip InstructionExecutor let mut prev_rolling_hash: Option<[F; 2 * CHUNK]> = None; let mut rolling_hash = [F::ZERO; 2 * CHUNK]; - + let mut is_first_in_segment = true; - while opened_index < opened_length - && memory.unsafe_read_cell( - address_space, - dim_base_pointer + F::from_canonical_usize(opened_index), - ) == F::from_canonical_u32(height) - { + loop { let mut cells = vec![]; let mut chunk = [F::ZERO; CHUNK]; for i in 0..CHUNK { - let read_row_pointer_and_length = if is_first_in_segment || row_pointer == row_end { + let read_row_pointer_and_length = if is_first_in_segment + || row_pointer == row_end + { if is_first_in_segment { is_first_in_segment = false; } else { opened_index += 1; if opened_index == opened_length || memory.unsafe_read_cell( - address_space, - dim_base_pointer + F::from_canonical_usize(opened_index), - ) != F::from_canonical_u32(height) + address_space, + dim_base_pointer + F::from_canonical_usize(opened_index), + ) != F::from_canonical_u32(height) { break; } @@ -223,6 +230,8 @@ impl InstructionExecutor }; let (read, value) = memory.read_cell(address_space, F::from_canonical_usize(row_pointer)); + println!("\topened_index = {opened_index}, row_pointer = {row_pointer}, row_end = {row_end}"); + cells.push(CellRecord { read, @@ -234,14 +243,24 @@ impl InstructionExecutor chunk[i] = value; row_pointer += 1; } + println!("opened_index = {opened_index}, row_pointer = {row_pointer}, row_end = {row_end}"); + if cells.is_empty() { + break; + } + let cells_len = cells.len(); + rolling_hash[..CHUNK].copy_from_slice(&chunk[..CHUNK]); chunks.push(InsideRowRecord { cells, p2_input: rolling_hash, }); self.height += 1; - rolling_hash[..CHUNK].copy_from_slice(&chunk[..CHUNK]); prev_rolling_hash = Some(rolling_hash); self.subchip.permute_mut(&mut rolling_hash); + println!("rolling_hash was {:?}", prev_rolling_hash); + println!("\tnow = {:?}", rolling_hash); + if cells_len < CHUNK { + break; + } } let final_opened_index = opened_index - 1; let (initial_height_read, height_check) = memory.read_cell( @@ -277,7 +296,7 @@ impl InstructionExecutor None }; - let incorporate_sibling = if height == 0 { + let incorporate_sibling = if height == 1 { None } else { for _ in 0..6 { @@ -295,13 +314,13 @@ impl InstructionExecutor sibling_base_pointer + F::from_canonical_usize(proof_index), ); let sibling_array_start = sibling_array_start.as_canonical_u32() as usize; - + let mut sibling = [F::ZERO; CHUNK]; let mut reads = vec![]; for i in 0..CHUNK { let (read, value) = memory.read_cell( address_space, - sibling_base_pointer + F::from_canonical_usize(sibling_array_start + i), + F::from_canonical_usize(sibling_array_start + i), ); sibling[i] = value; reads.push(read); diff --git a/extensions/native/circuit/src/verify_batch/tests.rs b/extensions/native/circuit/src/verify_batch/tests.rs index ca61566e90..b26dee42d5 100644 --- a/extensions/native/circuit/src/verify_batch/tests.rs +++ b/extensions/native/circuit/src/verify_batch/tests.rs @@ -12,10 +12,10 @@ use rand::{rngs::StdRng, Rng}; use crate::verify_batch::chip::VerifyBatchChip; fn compute_commit( - dim: &Vec, - opened: &Vec>, - proof: &Vec<[F; CHUNK]>, - root_is_on_right: &Vec, + dim: &[usize], + opened: &[Vec], + proof: &[[F; CHUNK]], + root_is_on_right: &[bool], hash_function: impl Fn([F; CHUNK], [F; CHUNK]) -> ([F; CHUNK], [F; CHUNK]), ) -> [F; CHUNK] { let mut height = dim[0]; @@ -63,6 +63,7 @@ fn compute_commit( type F = BabyBear; +#[derive(Debug, Clone)] struct VerifyBatchInstance { dim: Vec, opened: Vec>, @@ -114,6 +115,10 @@ fn random_instance( #[test] fn verify_batch_air_test() { + unsafe { + std::env::set_var("RUST_BACKTRACE", "1"); + } + // single op let address_space = 5; let row_lengths = vec![vec![3], vec![], vec![9, 2, 1, 13, 4], vec![16]]; @@ -134,12 +139,15 @@ fn verify_batch_air_test() { let instance = random_instance(&mut rng, row_lengths, |left, right| { let concatenated = std::array::from_fn(|i| if i < CHUNK { left[i] } else { right[i - CHUNK] }); + println!("concatenated = {:?}", concatenated); let permuted = chip.subchip.permute(concatenated); + println!("\t-> {:?}", permuted); ( std::array::from_fn(|i| permuted[i]), std::array::from_fn(|i| permuted[i + CHUNK]), ) }); + println!("instance = {:?}", instance); let VerifyBatchInstance { dim, opened, @@ -153,12 +161,14 @@ fn verify_batch_air_test() { let sibling_register = gen_pointer(&mut rng, 2); let index_register = gen_pointer(&mut rng, 2); let commit_register = gen_pointer(&mut rng, 2); + println!("dim_register = {dim_register}, opened_register = {opened_register}, sibling_register = {sibling_register}, index_register = {index_register}, commit_register = {commit_register}"); let dim_base_pointer = gen_pointer(&mut rng, 1); let opened_base_pointer = gen_pointer(&mut rng, 2); let sibling_base_pointer = gen_pointer(&mut rng, 1); let index_base_pointer = gen_pointer(&mut rng, 1); let commit_pointer = gen_pointer(&mut rng, 1); + println!("dim_base_pointer = {dim_base_pointer}, opened_base_pointer = {opened_base_pointer}, sibling_base_pointer = {sibling_base_pointer}, index_base_pointer = {index_base_pointer}, commit_pointer = {commit_pointer}"); tester.write_usize(address_space, dim_register, [dim_base_pointer, dim.len()]); tester.write_usize( @@ -183,6 +193,7 @@ fn verify_batch_air_test() { } for (i, opened_row) in opened.iter().enumerate() { let row_pointer = gen_pointer(&mut rng, 1); + println!("opened_row_pointer[{i}] = {row_pointer}"); tester.write_usize( address_space, opened_base_pointer + (2 * i), @@ -194,6 +205,7 @@ fn verify_batch_air_test() { } for (i, &sibling) in proof.iter().enumerate() { let row_pointer = gen_pointer(&mut rng, 1); + println!("sibling_row_pointer[{i}] = {row_pointer}"); tester.write_usize(address_space, sibling_base_pointer + i, [row_pointer]); tester.write(address_space, row_pointer, sibling); } @@ -242,4 +254,4 @@ fn verify_batch_air_test() { tester.air_proof_inputs[2].raw.common_main = Some(old_trace); }*/ -} +} \ No newline at end of file diff --git a/extensions/native/circuit/src/verify_batch/trace.rs b/extensions/native/circuit/src/verify_batch/trace.rs index 642ad23b1d..c6db5a58b4 100644 --- a/extensions/native/circuit/src/verify_batch/trace.rs +++ b/extensions/native/circuit/src/verify_batch/trace.rs @@ -226,8 +226,8 @@ impl VerifyBatchChip Date: Tue, 14 Jan 2025 18:26:04 -0500 Subject: [PATCH 19/50] Fix some bugs causing interaction to be unbalanced --- .../native/circuit/src/verify_batch/air.rs | 32 +++++++++++++++++-- .../native/circuit/src/verify_batch/chip.rs | 4 +++ .../native/circuit/src/verify_batch/trace.rs | 1 + 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/extensions/native/circuit/src/verify_batch/air.rs b/extensions/native/circuit/src/verify_batch/air.rs index bb890685eb..5d06bffd78 100644 --- a/extensions/native/circuit/src/verify_batch/air.rs +++ b/extensions/native/circuit/src/verify_batch/air.rs @@ -153,6 +153,29 @@ impl Air left_output, ); + let row_hash = std::array::from_fn(|i| { + (start_top_level * left_output[i]) + + ((AB::Expr::ONE - start_top_level) * right_input[i]) + }); + + self.internal_bus.interact( + builder, + true, + incorporate_row, + start_timestamp + AB::F::from_canonical_usize(6), + end_timestamp - AB::F::TWO, + opened_base_pointer, + initial_opened_index, + final_opened_index, + address_space, + row_hash, + ); + + return; + + + // everything abov here is ok + // things that stay the same (roughly) builder.when(inside_row - end_inside_row).assert_eq( @@ -211,7 +234,7 @@ impl Air .when(cell.is_exhausted) .assert_eq(left_input[i], AB::F::ZERO); - let mut when_inside_row_not_last = builder.when(inside_row - end_inside_row); + let mut when_inside_row_not_last = if i == CHUNK - 1 { builder.when(inside_row - end_inside_row) } else { builder.when(inside_row) }; // update state for normal cell when_inside_row_not_last @@ -236,14 +259,19 @@ impl Air &cell.read_row_pointer_and_length, ) .eval(builder, inside_row * cell.is_first_in_row); - let mut when_inside_row_not_last = builder.when(inside_row - end_inside_row); + let mut when_inside_row_not_last = if i == CHUNK - 1 { builder.when(inside_row - end_inside_row) } else { builder.when(inside_row) }; when_inside_row_not_last .when(next_cell.is_first_in_row) .assert_eq(next_cell.opened_index, cell.opened_index + AB::F::ONE); + when_inside_row_not_last + .when(next_cell.is_exhausted) + .assert_eq(next_cell.opened_index, cell.opened_index); + when_inside_row_not_last .when(cell.is_exhausted) .assert_eq(next_cell.is_exhausted, AB::F::ONE); + let is_last_in_row = if i == CHUNK - 1 { end_inside_row.into() diff --git a/extensions/native/circuit/src/verify_batch/chip.rs b/extensions/native/circuit/src/verify_batch/chip.rs index cb4ad3b7bb..b65519f58b 100644 --- a/extensions/native/circuit/src/verify_batch/chip.rs +++ b/extensions/native/circuit/src/verify_batch/chip.rs @@ -259,6 +259,10 @@ impl InstructionExecutor println!("rolling_hash was {:?}", prev_rolling_hash); println!("\tnow = {:?}", rolling_hash); if cells_len < CHUNK { + for _ in 0..CHUNK - cells_len { + memory.increment_timestamp(); + memory.increment_timestamp(); + } break; } } diff --git a/extensions/native/circuit/src/verify_batch/trace.rs b/extensions/native/circuit/src/verify_batch/trace.rs index c6db5a58b4..2b432b7301 100644 --- a/extensions/native/circuit/src/verify_batch/trace.rs +++ b/extensions/native/circuit/src/verify_batch/trace.rs @@ -252,6 +252,7 @@ impl VerifyBatchChip Date: Tue, 14 Jan 2025 19:11:41 -0500 Subject: [PATCH 20/50] Fix constraints --- .../native/circuit/src/verify_batch/air.rs | 60 +++++++------------ .../native/circuit/src/verify_batch/trace.rs | 5 +- 2 files changed, 25 insertions(+), 40 deletions(-) diff --git a/extensions/native/circuit/src/verify_batch/air.rs b/extensions/native/circuit/src/verify_batch/air.rs index 5d06bffd78..d3429423af 100644 --- a/extensions/native/circuit/src/verify_batch/air.rs +++ b/extensions/native/circuit/src/verify_batch/air.rs @@ -112,7 +112,6 @@ impl Air builder.assert_bool(end_inside_row); builder.when(end_inside_row).assert_one(inside_row); builder.assert_bool(end_top_level); - builder.when(end_top_level).assert_one(incorporate_sibling); let end = end_inside_row + end_top_level + (AB::Expr::ONE - enabled.clone()); builder @@ -153,29 +152,6 @@ impl Air left_output, ); - let row_hash = std::array::from_fn(|i| { - (start_top_level * left_output[i]) - + ((AB::Expr::ONE - start_top_level) * right_input[i]) - }); - - self.internal_bus.interact( - builder, - true, - incorporate_row, - start_timestamp + AB::F::from_canonical_usize(6), - end_timestamp - AB::F::TWO, - opened_base_pointer, - initial_opened_index, - final_opened_index, - address_space, - row_hash, - ); - - return; - - - // everything abov here is ok - // things that stay the same (roughly) builder.when(inside_row - end_inside_row).assert_eq( @@ -233,7 +209,7 @@ impl Air builder .when(cell.is_exhausted) .assert_eq(left_input[i], AB::F::ZERO); - + let mut when_inside_row_not_last = if i == CHUNK - 1 { builder.when(inside_row - end_inside_row) } else { builder.when(inside_row) }; // update state for normal cell @@ -271,13 +247,12 @@ impl Air when_inside_row_not_last .when(cell.is_exhausted) .assert_eq(next_cell.is_exhausted, AB::F::ONE); - let is_last_in_row = if i == CHUNK - 1 { end_inside_row.into() } else { next_cell.is_first_in_row + next_cell.is_exhausted - }; + } - cell.is_exhausted; builder .when(inside_row) .when(is_last_in_row) @@ -363,10 +338,12 @@ impl Air ) .eval(builder, end_top_level); + let mut when_top_level_not_end = builder.when(incorporate_row + incorporate_sibling - end_top_level); when_top_level_not_end.assert_eq(next.dim_base_pointer, dim_base_pointer); + when_top_level_not_end.assert_eq(next.opened_base_pointer, opened_base_pointer); when_top_level_not_end.assert_eq(next.sibling_base_pointer, sibling_base_pointer); when_top_level_not_end.assert_eq(next.index_base_pointer, index_base_pointer); @@ -377,16 +354,20 @@ impl Air .assert_eq(next.initial_opened_index, final_opened_index + AB::F::ONE); builder - .when(incorporate_sibling - end_top_level) + .when(incorporate_sibling) + .when(AB::Expr::ONE - end_top_level) .assert_eq(next.height * AB::F::TWO, height); builder - .when(incorporate_row - end_top_level) + .when(incorporate_row) + .when(AB::Expr::ONE - end_top_level) .assert_eq(next.height, height); builder - .when(incorporate_sibling - end_top_level) + .when(incorporate_sibling) + .when(AB::Expr::ONE - end_top_level) .assert_eq(next.proof_index, proof_index + AB::F::ONE); builder - .when(incorporate_row - end_top_level) + .when(incorporate_row) + .when(AB::Expr::ONE - end_top_level) .assert_eq(next.proof_index, proof_index); builder @@ -447,14 +428,15 @@ impl Air MemoryAddress::new(address_space, dim_base_pointer + final_opened_index), [height], end_timestamp - AB::F::ONE, - &read_initial_height_or_root_is_on_right, + &read_final_height_or_sibling_array_start, ) .eval(builder, incorporate_row); // incorporate sibling builder - .when(incorporate_sibling - end_top_level) + .when(incorporate_sibling) + .when(AB::Expr::ONE - end_top_level) .assert_one(next.incorporate_row + next.incorporate_sibling); builder .when(end_top_level) @@ -472,7 +454,7 @@ impl Air timestamp_after_end_operations.clone(), &read_initial_height_or_root_is_on_right, ) - .eval(builder, incorporate_row); + .eval(builder, incorporate_sibling); self.memory_bridge .read( MemoryAddress::new(address_space, sibling_base_pointer + proof_index), @@ -480,8 +462,8 @@ impl Air timestamp_after_end_operations.clone() + AB::F::ONE, &read_final_height_or_sibling_array_start, ) - .eval(builder, incorporate_row); - + .eval(builder, incorporate_sibling); + for i in 0..CHUNK { builder .when(next.incorporate_sibling) @@ -498,12 +480,12 @@ impl Air [(root_is_on_right * left_input[i]) + ((AB::Expr::ONE - root_is_on_right) * right_input[i])], timestamp_after_end_operations.clone() + AB::F::from_canonical_usize(2 + i), - &read_initial_height_or_root_is_on_right, + &cells[i].read, ) - .eval(builder, incorporate_row); + .eval(builder, incorporate_sibling); } - builder.assert_eq( + builder.when(incorporate_sibling).assert_eq( end_timestamp, timestamp_after_end_operations + AB::F::from_canonical_usize(2 + CHUNK), ); diff --git a/extensions/native/circuit/src/verify_batch/trace.rs b/extensions/native/circuit/src/verify_batch/trace.rs index 2b432b7301..e4a89547e4 100644 --- a/extensions/native/circuit/src/verify_batch/trace.rs +++ b/extensions/native/circuit/src/verify_batch/trace.rs @@ -84,6 +84,7 @@ impl VerifyBatchChip VerifyBatchChip = slice.borrow_mut(); + cols.end_top_level = F::ONE; cols.pc = F::from_canonical_u32(from_state.pc); cols.dim_register = instruction.a; cols.opened_register = instruction.b; @@ -184,6 +186,7 @@ impl VerifyBatchChip VerifyBatchChip Date: Tue, 14 Jan 2025 19:38:13 -0500 Subject: [PATCH 21/50] Test passing --- extensions/native/circuit/src/verify_batch/air.rs | 2 +- extensions/native/circuit/src/verify_batch/chip.rs | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/extensions/native/circuit/src/verify_batch/air.rs b/extensions/native/circuit/src/verify_batch/air.rs index d3429423af..036cc58b9f 100644 --- a/extensions/native/circuit/src/verify_batch/air.rs +++ b/extensions/native/circuit/src/verify_batch/air.rs @@ -476,7 +476,7 @@ impl Air self.memory_bridge .read( - MemoryAddress::new(address_space, sibling_array_start + proof_index), + MemoryAddress::new(address_space, sibling_array_start + AB::F::from_canonical_usize(i)), [(root_is_on_right * left_input[i]) + ((AB::Expr::ONE - root_is_on_right) * right_input[i])], timestamp_after_end_operations.clone() + AB::F::from_canonical_usize(2 + i), diff --git a/extensions/native/circuit/src/verify_batch/chip.rs b/extensions/native/circuit/src/verify_batch/chip.rs index b65519f58b..e941a6717e 100644 --- a/extensions/native/circuit/src/verify_batch/chip.rs +++ b/extensions/native/circuit/src/verify_batch/chip.rs @@ -322,10 +322,12 @@ impl InstructionExecutor let mut sibling = [F::ZERO; CHUNK]; let mut reads = vec![]; for i in 0..CHUNK { + println!("[timestamp = {}] reading {sibling_array_start} + {i} -> ", memory.timestamp()); let (read, value) = memory.read_cell( address_space, F::from_canonical_usize(sibling_array_start + i), ); + println!("\t-> {:?}", value); sibling[i] = value; reads.push(read); } From 648ee8df7fa5f13bf561d20ab402000a9879811d Mon Sep 17 00:00:00 2001 From: TlatoaniHJ Date: Tue, 14 Jan 2025 19:40:06 -0500 Subject: [PATCH 22/50] Remove debugging statements --- extensions/native/circuit/src/verify_batch/chip.rs | 12 ------------ extensions/native/circuit/src/verify_batch/tests.rs | 7 ------- extensions/native/circuit/src/verify_batch/trace.rs | 2 -- 3 files changed, 21 deletions(-) diff --git a/extensions/native/circuit/src/verify_batch/chip.rs b/extensions/native/circuit/src/verify_batch/chip.rs index e941a6717e..28dd6fd070 100644 --- a/extensions/native/circuit/src/verify_batch/chip.rs +++ b/extensions/native/circuit/src/verify_batch/chip.rs @@ -125,10 +125,6 @@ impl VerifyBatchChip InstructionExecutor }; let (read, value) = memory.read_cell(address_space, F::from_canonical_usize(row_pointer)); - println!("\topened_index = {opened_index}, row_pointer = {row_pointer}, row_end = {row_end}"); - - cells.push(CellRecord { read, opened_index, @@ -243,7 +236,6 @@ impl InstructionExecutor chunk[i] = value; row_pointer += 1; } - println!("opened_index = {opened_index}, row_pointer = {row_pointer}, row_end = {row_end}"); if cells.is_empty() { break; } @@ -256,8 +248,6 @@ impl InstructionExecutor self.height += 1; prev_rolling_hash = Some(rolling_hash); self.subchip.permute_mut(&mut rolling_hash); - println!("rolling_hash was {:?}", prev_rolling_hash); - println!("\tnow = {:?}", rolling_hash); if cells_len < CHUNK { for _ in 0..CHUNK - cells_len { memory.increment_timestamp(); @@ -322,12 +312,10 @@ impl InstructionExecutor let mut sibling = [F::ZERO; CHUNK]; let mut reads = vec![]; for i in 0..CHUNK { - println!("[timestamp = {}] reading {sibling_array_start} + {i} -> ", memory.timestamp()); let (read, value) = memory.read_cell( address_space, F::from_canonical_usize(sibling_array_start + i), ); - println!("\t-> {:?}", value); sibling[i] = value; reads.push(read); } diff --git a/extensions/native/circuit/src/verify_batch/tests.rs b/extensions/native/circuit/src/verify_batch/tests.rs index b26dee42d5..e22cbd8f2d 100644 --- a/extensions/native/circuit/src/verify_batch/tests.rs +++ b/extensions/native/circuit/src/verify_batch/tests.rs @@ -139,15 +139,12 @@ fn verify_batch_air_test() { let instance = random_instance(&mut rng, row_lengths, |left, right| { let concatenated = std::array::from_fn(|i| if i < CHUNK { left[i] } else { right[i - CHUNK] }); - println!("concatenated = {:?}", concatenated); let permuted = chip.subchip.permute(concatenated); - println!("\t-> {:?}", permuted); ( std::array::from_fn(|i| permuted[i]), std::array::from_fn(|i| permuted[i + CHUNK]), ) }); - println!("instance = {:?}", instance); let VerifyBatchInstance { dim, opened, @@ -161,14 +158,12 @@ fn verify_batch_air_test() { let sibling_register = gen_pointer(&mut rng, 2); let index_register = gen_pointer(&mut rng, 2); let commit_register = gen_pointer(&mut rng, 2); - println!("dim_register = {dim_register}, opened_register = {opened_register}, sibling_register = {sibling_register}, index_register = {index_register}, commit_register = {commit_register}"); let dim_base_pointer = gen_pointer(&mut rng, 1); let opened_base_pointer = gen_pointer(&mut rng, 2); let sibling_base_pointer = gen_pointer(&mut rng, 1); let index_base_pointer = gen_pointer(&mut rng, 1); let commit_pointer = gen_pointer(&mut rng, 1); - println!("dim_base_pointer = {dim_base_pointer}, opened_base_pointer = {opened_base_pointer}, sibling_base_pointer = {sibling_base_pointer}, index_base_pointer = {index_base_pointer}, commit_pointer = {commit_pointer}"); tester.write_usize(address_space, dim_register, [dim_base_pointer, dim.len()]); tester.write_usize( @@ -193,7 +188,6 @@ fn verify_batch_air_test() { } for (i, opened_row) in opened.iter().enumerate() { let row_pointer = gen_pointer(&mut rng, 1); - println!("opened_row_pointer[{i}] = {row_pointer}"); tester.write_usize( address_space, opened_base_pointer + (2 * i), @@ -205,7 +199,6 @@ fn verify_batch_air_test() { } for (i, &sibling) in proof.iter().enumerate() { let row_pointer = gen_pointer(&mut rng, 1); - println!("sibling_row_pointer[{i}] = {row_pointer}"); tester.write_usize(address_space, sibling_base_pointer + i, [row_pointer]); tester.write(address_space, row_pointer, sibling); } diff --git a/extensions/native/circuit/src/verify_batch/trace.rs b/extensions/native/circuit/src/verify_batch/trace.rs index e4a89547e4..f07f5a3394 100644 --- a/extensions/native/circuit/src/verify_batch/trace.rs +++ b/extensions/native/circuit/src/verify_batch/trace.rs @@ -84,7 +84,6 @@ impl VerifyBatchChip VerifyBatchChip Date: Tue, 14 Jan 2025 19:43:14 -0500 Subject: [PATCH 23/50] Cleanup --- .../native/circuit/src/verify_batch/tests.rs | 34 ++++--------------- 1 file changed, 7 insertions(+), 27 deletions(-) diff --git a/extensions/native/circuit/src/verify_batch/tests.rs b/extensions/native/circuit/src/verify_batch/tests.rs index e22cbd8f2d..03a1e59dfc 100644 --- a/extensions/native/circuit/src/verify_batch/tests.rs +++ b/extensions/native/circuit/src/verify_batch/tests.rs @@ -1,3 +1,7 @@ +use openvm_stark_backend::p3_field::{Field, FieldAlgebra}; +use openvm_stark_sdk::{p3_baby_bear::BabyBear, utils::create_seeded_rng}; +use rand::{Rng, rngs::StdRng}; + use openvm_circuit::{ arch::testing::{memory::gen_pointer, VmChipTestBuilder}, system::memory::CHUNK, @@ -5,9 +9,6 @@ use openvm_circuit::{ use openvm_instructions::{instruction::Instruction, UsizeOpcode, VmOpcode}; use openvm_native_compiler::{VerifyBatchOpcode, VerifyBatchOpcode::VERIFY_BATCH}; use openvm_poseidon2_air::Poseidon2Config; -use openvm_stark_backend::p3_field::{Field, FieldAlgebra}; -use openvm_stark_sdk::{p3_baby_bear::BabyBear, utils::create_seeded_rng}; -use rand::{rngs::StdRng, Rng}; use crate::verify_batch::chip::VerifyBatchChip; @@ -113,6 +114,8 @@ fn random_instance( } } +const SBOX_REGISTERS: usize = 1; + #[test] fn verify_batch_air_test() { unsafe { @@ -126,7 +129,7 @@ fn verify_batch_air_test() { let offset = VerifyBatchOpcode::default_offset(); let mut tester = VmChipTestBuilder::default(); - let mut chip = VerifyBatchChip::::new( + let mut chip = VerifyBatchChip::::new( tester.execution_bus(), tester.program_bus(), tester.memory_bridge(), @@ -224,27 +227,4 @@ fn verify_batch_air_test() { let tester = tester.build().load(chip).finalize(); tester.simple_test().expect("Verification failed"); - - /*disable_debug_builder(); - // negative test pranking each value - for height in 0..num_ops { - // TODO: better way to modify existing traces in tester - let trace = tester.air_proof_inputs[2].raw.common_main.as_mut().unwrap(); - let old_trace = trace.clone(); - for width in 0..VerifyBatchCols::::width() - /* num operands */ - { - let prank_value = BabyBear::from_canonical_u32(rng.gen_range(1..=100)); - trace.row_mut(height)[width] = prank_value; - } - - // Run a test after pranking each row - assert_eq!( - tester.simple_test().err(), - Some(VerificationError::OodEvaluationMismatch), - "Expected constraint to fail" - ); - - tester.air_proof_inputs[2].raw.common_main = Some(old_trace); - }*/ } \ No newline at end of file From ffb62866478c0dcff5926e80ce9b5741adbb0214 Mon Sep 17 00:00:00 2001 From: TlatoaniHJ Date: Wed, 15 Jan 2025 11:55:58 -0500 Subject: [PATCH 24/50] Add negative test --- .../native/circuit/src/verify_batch/air.rs | 2 +- .../native/circuit/src/verify_batch/tests.rs | 24 +++++++++++++++++-- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/extensions/native/circuit/src/verify_batch/air.rs b/extensions/native/circuit/src/verify_batch/air.rs index 036cc58b9f..3ec840f26d 100644 --- a/extensions/native/circuit/src/verify_batch/air.rs +++ b/extensions/native/circuit/src/verify_batch/air.rs @@ -119,6 +119,7 @@ impl Air .when(next.incorporate_row) .assert_one(next.start_top_level); + // poseidon2 constraints are always checked let mut sub_builder = SubAirBuilder::, AB::F>::new( builder, @@ -338,7 +339,6 @@ impl Air ) .eval(builder, end_top_level); - let mut when_top_level_not_end = builder.when(incorporate_row + incorporate_sibling - end_top_level); diff --git a/extensions/native/circuit/src/verify_batch/tests.rs b/extensions/native/circuit/src/verify_batch/tests.rs index 03a1e59dfc..21d6065531 100644 --- a/extensions/native/circuit/src/verify_batch/tests.rs +++ b/extensions/native/circuit/src/verify_batch/tests.rs @@ -1,4 +1,7 @@ +use openvm_stark_backend::p3_air::BaseAir; use openvm_stark_backend::p3_field::{Field, FieldAlgebra}; +use openvm_stark_backend::utils::disable_debug_builder; +use openvm_stark_backend::verifier::VerificationError; use openvm_stark_sdk::{p3_baby_bear::BabyBear, utils::create_seeded_rng}; use rand::{Rng, rngs::StdRng}; @@ -8,7 +11,7 @@ use openvm_circuit::{ }; use openvm_instructions::{instruction::Instruction, UsizeOpcode, VmOpcode}; use openvm_native_compiler::{VerifyBatchOpcode, VerifyBatchOpcode::VERIFY_BATCH}; -use openvm_poseidon2_air::Poseidon2Config; +use openvm_poseidon2_air::{Poseidon2Config, Poseidon2SubChip}; use crate::verify_batch::chip::VerifyBatchChip; @@ -225,6 +228,23 @@ fn verify_batch_air_test() { ), ); - let tester = tester.build().load(chip).finalize(); + let mut tester = tester.build().load(chip).finalize(); tester.simple_test().expect("Verification failed"); + + disable_debug_builder(); + let trace = tester.air_proof_inputs[2].raw.common_main.as_mut().unwrap(); + let row_index = 0; + trace.row_mut(row_index); + + let p2_chip = Poseidon2SubChip::::new(Poseidon2Config::default().constants); + let inner_trace = p2_chip.generate_trace(vec![[F::ZERO; 2 * CHUNK]]); + let inner_width = p2_chip.air.width(); + + trace.row_mut(row_index)[..inner_width].copy_from_slice(&inner_trace.values); + // Run a test after pranking the poseidon2 stuff + assert_eq!( + tester.simple_test().err(), + Some(VerificationError::OodEvaluationMismatch), + "Expected constraint to fail" + ); } \ No newline at end of file From 7866767cd67634ed6dad983caaba349e7458c60d Mon Sep 17 00:00:00 2001 From: TlatoaniHJ Date: Wed, 15 Jan 2025 12:02:06 -0500 Subject: [PATCH 25/50] Add a test with multiple operations (failing) --- .../native/circuit/src/verify_batch/air.rs | 24 ++- .../native/circuit/src/verify_batch/chip.rs | 2 +- .../native/circuit/src/verify_batch/tests.rs | 199 ++++++++++-------- .../native/circuit/src/verify_batch/trace.rs | 7 +- 4 files changed, 129 insertions(+), 103 deletions(-) diff --git a/extensions/native/circuit/src/verify_batch/air.rs b/extensions/native/circuit/src/verify_batch/air.rs index 3ec840f26d..807f078905 100644 --- a/extensions/native/circuit/src/verify_batch/air.rs +++ b/extensions/native/circuit/src/verify_batch/air.rs @@ -8,13 +8,14 @@ use openvm_circuit_primitives::utils::not; use openvm_native_compiler::VerifyBatchOpcode::VERIFY_BATCH; use openvm_poseidon2_air::{Poseidon2SubAir, BABY_BEAR_POSEIDON2_HALF_FULL_ROUNDS}; use openvm_stark_backend::{ + air_builders::sub::SubAirBuilder, interaction::{InteractionBuilder, InteractionType}, p3_air::{Air, AirBuilder, BaseAir}, p3_field::{Field, FieldAlgebra}, p3_matrix::Matrix, rap::{BaseAirWithPublicValues, PartitionedBaseAir}, }; -use openvm_stark_backend::air_builders::sub::SubAirBuilder; + use crate::verify_batch::{columns::VerifyBatchCols, CHUNK}; #[derive(Clone, Debug)] @@ -210,8 +211,12 @@ impl Air builder .when(cell.is_exhausted) .assert_eq(left_input[i], AB::F::ZERO); - - let mut when_inside_row_not_last = if i == CHUNK - 1 { builder.when(inside_row - end_inside_row) } else { builder.when(inside_row) }; + + let mut when_inside_row_not_last = if i == CHUNK - 1 { + builder.when(inside_row - end_inside_row) + } else { + builder.when(inside_row) + }; // update state for normal cell when_inside_row_not_last @@ -236,7 +241,11 @@ impl Air &cell.read_row_pointer_and_length, ) .eval(builder, inside_row * cell.is_first_in_row); - let mut when_inside_row_not_last = if i == CHUNK - 1 { builder.when(inside_row - end_inside_row) } else { builder.when(inside_row) }; + let mut when_inside_row_not_last = if i == CHUNK - 1 { + builder.when(inside_row - end_inside_row) + } else { + builder.when(inside_row) + }; when_inside_row_not_last .when(next_cell.is_first_in_row) .assert_eq(next_cell.opened_index, cell.opened_index + AB::F::ONE); @@ -463,7 +472,7 @@ impl Air &read_final_height_or_sibling_array_start, ) .eval(builder, incorporate_sibling); - + for i in 0..CHUNK { builder .when(next.incorporate_sibling) @@ -476,7 +485,10 @@ impl Air self.memory_bridge .read( - MemoryAddress::new(address_space, sibling_array_start + AB::F::from_canonical_usize(i)), + MemoryAddress::new( + address_space, + sibling_array_start + AB::F::from_canonical_usize(i), + ), [(root_is_on_right * left_input[i]) + ((AB::Expr::ONE - root_is_on_right) * right_input[i])], timestamp_after_end_operations.clone() + AB::F::from_canonical_usize(2 + i), diff --git a/extensions/native/circuit/src/verify_batch/chip.rs b/extensions/native/circuit/src/verify_batch/chip.rs index 28dd6fd070..124a7b0ed7 100644 --- a/extensions/native/circuit/src/verify_batch/chip.rs +++ b/extensions/native/circuit/src/verify_batch/chip.rs @@ -308,7 +308,7 @@ impl InstructionExecutor sibling_base_pointer + F::from_canonical_usize(proof_index), ); let sibling_array_start = sibling_array_start.as_canonical_u32() as usize; - + let mut sibling = [F::ZERO; CHUNK]; let mut reads = vec![]; for i in 0..CHUNK { diff --git a/extensions/native/circuit/src/verify_batch/tests.rs b/extensions/native/circuit/src/verify_batch/tests.rs index 21d6065531..42a06f614c 100644 --- a/extensions/native/circuit/src/verify_batch/tests.rs +++ b/extensions/native/circuit/src/verify_batch/tests.rs @@ -1,10 +1,3 @@ -use openvm_stark_backend::p3_air::BaseAir; -use openvm_stark_backend::p3_field::{Field, FieldAlgebra}; -use openvm_stark_backend::utils::disable_debug_builder; -use openvm_stark_backend::verifier::VerificationError; -use openvm_stark_sdk::{p3_baby_bear::BabyBear, utils::create_seeded_rng}; -use rand::{Rng, rngs::StdRng}; - use openvm_circuit::{ arch::testing::{memory::gen_pointer, VmChipTestBuilder}, system::memory::CHUNK, @@ -12,6 +5,14 @@ use openvm_circuit::{ use openvm_instructions::{instruction::Instruction, UsizeOpcode, VmOpcode}; use openvm_native_compiler::{VerifyBatchOpcode, VerifyBatchOpcode::VERIFY_BATCH}; use openvm_poseidon2_air::{Poseidon2Config, Poseidon2SubChip}; +use openvm_stark_backend::{ + p3_air::BaseAir, + p3_field::{Field, FieldAlgebra}, + utils::disable_debug_builder, + verifier::VerificationError, +}; +use openvm_stark_sdk::{p3_baby_bear::BabyBear, utils::create_seeded_rng}; +use rand::{rngs::StdRng, Rng}; use crate::verify_batch::chip::VerifyBatchChip; @@ -119,15 +120,15 @@ fn random_instance( const SBOX_REGISTERS: usize = 1; -#[test] -fn verify_batch_air_test() { +type Case = Vec>; + +fn test(cases: [Case; N]) { unsafe { std::env::set_var("RUST_BACKTRACE", "1"); } // single op let address_space = 5; - let row_lengths = vec![vec![3], vec![], vec![9, 2, 1, 13, 4], vec![16]]; let offset = VerifyBatchOpcode::default_offset(); @@ -142,91 +143,93 @@ fn verify_batch_air_test() { ); let mut rng = create_seeded_rng(); - let instance = random_instance(&mut rng, row_lengths, |left, right| { - let concatenated = - std::array::from_fn(|i| if i < CHUNK { left[i] } else { right[i - CHUNK] }); - let permuted = chip.subchip.permute(concatenated); - ( - std::array::from_fn(|i| permuted[i]), - std::array::from_fn(|i| permuted[i + CHUNK]), - ) - }); - let VerifyBatchInstance { - dim, - opened, - proof, - root_is_on_right, - commit, - } = instance; - - let dim_register = gen_pointer(&mut rng, 2); - let opened_register = gen_pointer(&mut rng, 2); - let sibling_register = gen_pointer(&mut rng, 2); - let index_register = gen_pointer(&mut rng, 2); - let commit_register = gen_pointer(&mut rng, 2); - - let dim_base_pointer = gen_pointer(&mut rng, 1); - let opened_base_pointer = gen_pointer(&mut rng, 2); - let sibling_base_pointer = gen_pointer(&mut rng, 1); - let index_base_pointer = gen_pointer(&mut rng, 1); - let commit_pointer = gen_pointer(&mut rng, 1); - - tester.write_usize(address_space, dim_register, [dim_base_pointer, dim.len()]); - tester.write_usize( - address_space, - opened_register, - [opened_base_pointer, opened.len()], - ); - tester.write_usize( - address_space, - sibling_register, - [sibling_base_pointer, proof.len()], - ); - tester.write_usize( - address_space, - index_register, - [index_base_pointer, root_is_on_right.len()], - ); - tester.write_usize(address_space, commit_register, [commit_pointer, CHUNK]); + for case in cases { + let instance = random_instance(&mut rng, case, |left, right| { + let concatenated = + std::array::from_fn(|i| if i < CHUNK { left[i] } else { right[i - CHUNK] }); + let permuted = chip.subchip.permute(concatenated); + ( + std::array::from_fn(|i| permuted[i]), + std::array::from_fn(|i| permuted[i + CHUNK]), + ) + }); + let VerifyBatchInstance { + dim, + opened, + proof, + root_is_on_right, + commit, + } = instance; - for (i, &dim_value) in dim.iter().enumerate() { - tester.write_usize(address_space, dim_base_pointer + i, [dim_value]); - } - for (i, opened_row) in opened.iter().enumerate() { - let row_pointer = gen_pointer(&mut rng, 1); + let dim_register = gen_pointer(&mut rng, 2); + let opened_register = gen_pointer(&mut rng, 2); + let sibling_register = gen_pointer(&mut rng, 2); + let index_register = gen_pointer(&mut rng, 2); + let commit_register = gen_pointer(&mut rng, 2); + + let dim_base_pointer = gen_pointer(&mut rng, 1); + let opened_base_pointer = gen_pointer(&mut rng, 2); + let sibling_base_pointer = gen_pointer(&mut rng, 1); + let index_base_pointer = gen_pointer(&mut rng, 1); + let commit_pointer = gen_pointer(&mut rng, 1); + + tester.write_usize(address_space, dim_register, [dim_base_pointer, dim.len()]); + tester.write_usize( + address_space, + opened_register, + [opened_base_pointer, opened.len()], + ); + tester.write_usize( + address_space, + sibling_register, + [sibling_base_pointer, proof.len()], + ); tester.write_usize( address_space, - opened_base_pointer + (2 * i), - [row_pointer, opened_row.len()], + index_register, + [index_base_pointer, root_is_on_right.len()], ); - for (j, &opened_value) in opened_row.iter().enumerate() { - tester.write_cell(address_space, row_pointer + j, opened_value); + tester.write_usize(address_space, commit_register, [commit_pointer, CHUNK]); + + for (i, &dim_value) in dim.iter().enumerate() { + tester.write_usize(address_space, dim_base_pointer + i, [dim_value]); } - } - for (i, &sibling) in proof.iter().enumerate() { - let row_pointer = gen_pointer(&mut rng, 1); - tester.write_usize(address_space, sibling_base_pointer + i, [row_pointer]); - tester.write(address_space, row_pointer, sibling); - } - for (i, &bit) in root_is_on_right.iter().enumerate() { - tester.write_cell(address_space, index_base_pointer + i, F::from_bool(bit)); - } - tester.write(address_space, commit_pointer, commit); - - tester.execute( - &mut chip, - &Instruction::from_usize( - VmOpcode::from_usize(VERIFY_BATCH as usize + offset), - [ - dim_register, - opened_register, - sibling_register, - index_register, - commit_register, + for (i, opened_row) in opened.iter().enumerate() { + let row_pointer = gen_pointer(&mut rng, 1); + tester.write_usize( address_space, - ], - ), - ); + opened_base_pointer + (2 * i), + [row_pointer, opened_row.len()], + ); + for (j, &opened_value) in opened_row.iter().enumerate() { + tester.write_cell(address_space, row_pointer + j, opened_value); + } + } + for (i, &sibling) in proof.iter().enumerate() { + let row_pointer = gen_pointer(&mut rng, 1); + tester.write_usize(address_space, sibling_base_pointer + i, [row_pointer]); + tester.write(address_space, row_pointer, sibling); + } + for (i, &bit) in root_is_on_right.iter().enumerate() { + tester.write_cell(address_space, index_base_pointer + i, F::from_bool(bit)); + } + tester.write(address_space, commit_pointer, commit); + + tester.execute( + &mut chip, + &Instruction::from_usize( + VmOpcode::from_usize(VERIFY_BATCH as usize + offset), + [ + dim_register, + opened_register, + sibling_register, + index_register, + commit_register, + address_space, + ], + ), + ); + } let mut tester = tester.build().load(chip).finalize(); tester.simple_test().expect("Verification failed"); @@ -235,7 +238,7 @@ fn verify_batch_air_test() { let trace = tester.air_proof_inputs[2].raw.common_main.as_mut().unwrap(); let row_index = 0; trace.row_mut(row_index); - + let p2_chip = Poseidon2SubChip::::new(Poseidon2Config::default().constants); let inner_trace = p2_chip.generate_trace(vec![[F::ZERO; 2 * CHUNK]]); let inner_width = p2_chip.air.width(); @@ -247,4 +250,18 @@ fn verify_batch_air_test() { Some(VerificationError::OodEvaluationMismatch), "Expected constraint to fail" ); -} \ No newline at end of file +} + +#[test] +fn verify_batch_test_1() { + test([vec![vec![3], vec![], vec![9, 2, 1, 13, 4], vec![16]]]); +} + +#[test] +fn verify_batch_test_multiple() { + test([ + vec![vec![1, 1, 1, 2, 3], vec![9], vec![8]], + vec![vec![], vec![], vec![], vec![1]], + vec![vec![8], vec![7], vec![6]], + ]) +} diff --git a/extensions/native/circuit/src/verify_batch/trace.rs b/extensions/native/circuit/src/verify_batch/trace.rs index f07f5a3394..d8edf7eae9 100644 --- a/extensions/native/circuit/src/verify_batch/trace.rs +++ b/extensions/native/circuit/src/verify_batch/trace.rs @@ -225,11 +225,8 @@ impl VerifyBatchChip Date: Wed, 15 Jan 2025 12:05:12 -0500 Subject: [PATCH 26/50] Fix failing test --- extensions/native/circuit/src/verify_batch/air.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/extensions/native/circuit/src/verify_batch/air.rs b/extensions/native/circuit/src/verify_batch/air.rs index 807f078905..8483045229 100644 --- a/extensions/native/circuit/src/verify_batch/air.rs +++ b/extensions/native/circuit/src/verify_batch/air.rs @@ -391,7 +391,8 @@ impl Air // incorporate row builder - .when(incorporate_row - end_top_level) + .when(incorporate_row) + .when(AB::Expr::ONE - end_top_level) .assert_one(next.incorporate_sibling); let row_hash = std::array::from_fn(|i| { From 38cd6319518f8a1916e0365615a667c2aa82e0b4 Mon Sep 17 00:00:00 2001 From: TlatoaniHJ Date: Wed, 15 Jan 2025 12:45:34 -0500 Subject: [PATCH 27/50] Add compiler stuff --- .../native/compiler/src/asm/instruction.rs | 3 +++ .../native/compiler/src/conversion/mod.rs | 12 ++++++++++- .../native/compiler/src/ir/instructions.rs | 10 +++++++++- extensions/native/compiler/src/ir/mod.rs | 1 + .../native/compiler/src/ir/verify_batch.rs | 20 +++++++++++++++++++ 5 files changed, 44 insertions(+), 2 deletions(-) create mode 100644 extensions/native/compiler/src/ir/verify_batch.rs diff --git a/extensions/native/compiler/src/asm/instruction.rs b/extensions/native/compiler/src/asm/instruction.rs index 98c8f11fc7..5e1765c4dd 100644 --- a/extensions/native/compiler/src/asm/instruction.rs +++ b/extensions/native/compiler/src/asm/instruction.rs @@ -114,6 +114,9 @@ pub enum AsmInstruction { /// (a, b, res, len, alpha, alpha_pow) FriReducedOpening(i32, i32, i32, i32, i32, i32), + // (dim, opened, sibling, index, commit) + VerifyBatch(i32, i32, i32, i32, i32), + /// Print a variable. PrintV(i32), diff --git a/extensions/native/compiler/src/conversion/mod.rs b/extensions/native/compiler/src/conversion/mod.rs index e92e6c3b66..18836080bd 100644 --- a/extensions/native/compiler/src/conversion/mod.rs +++ b/extensions/native/compiler/src/conversion/mod.rs @@ -12,7 +12,7 @@ use serde::{Deserialize, Serialize}; use crate::{ asm::{AsmInstruction, AssemblyCode}, FieldArithmeticOpcode, FieldExtensionOpcode, FriOpcode, NativeBranchEqualOpcode, - NativeJalOpcode, NativeLoadStoreOpcode, NativePhantom, + NativeJalOpcode, NativeLoadStoreOpcode, NativePhantom, VerifyBatchOpcode, }; #[derive(Clone, Copy, Debug, Serialize, Deserialize)] @@ -649,6 +649,16 @@ fn convert_instruction>( f: i32_f(alpha), g: i32_f(alpha_pow), }], + AsmInstruction::VerifyBatch(dim, opened, sibling, index, commit) => vec![Instruction { + opcode: options.opcode_with_offset(VerifyBatchOpcode::VERIFY_BATCH), + a: i32_f(dim), + b: i32_f(opened), + c: i32_f(sibling), + d: i32_f(index), + e: i32_f(commit), + f: AS::Native.to_field(), + g: F::ZERO, + }], }; let debug_infos = vec![debug_info; instructions.len()]; diff --git a/extensions/native/compiler/src/ir/instructions.rs b/extensions/native/compiler/src/ir/instructions.rs index d636b00ab0..b0ee5538e8 100644 --- a/extensions/native/compiler/src/ir/instructions.rs +++ b/extensions/native/compiler/src/ir/instructions.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; -use super::{Array, Config, Ext, Felt, MemIndex, Ptr, RVar, TracedVec, Var}; +use super::{Array, Config, Ext, Felt, MemIndex, Ptr, RVar, TracedVec, Usize, Var}; /// An intermeddiate instruction set for implementing programs. /// @@ -277,6 +277,14 @@ pub enum DslIr { Array>, Ext, ), + /// VerifyBatch(dim, opened, sibling, index, commit) + VerifyBatch( + Array>, + Array>>, + Array>>, + Array>, + Array>, + ), // Debugging instructions. /// Executes less than (var = var < var). This operation is NOT constrained. diff --git a/extensions/native/compiler/src/ir/mod.rs b/extensions/native/compiler/src/ir/mod.rs index ba44a4964d..9ce2afc2ee 100644 --- a/extensions/native/compiler/src/ir/mod.rs +++ b/extensions/native/compiler/src/ir/mod.rs @@ -24,6 +24,7 @@ mod symbolic; mod types; mod utils; mod var; +mod verify_batch; pub trait Config: Clone + Default { type N: PrimeField; diff --git a/extensions/native/compiler/src/ir/verify_batch.rs b/extensions/native/compiler/src/ir/verify_batch.rs new file mode 100644 index 0000000000..53180f0644 --- /dev/null +++ b/extensions/native/compiler/src/ir/verify_batch.rs @@ -0,0 +1,20 @@ +use crate::ir::{Array, Builder, Config, DslIr, Felt, Usize, Var}; + +impl Builder { + pub fn verify_batch( + &mut self, + dimensions: &Array>, + opened_values: &Array>>, + proof: &Array>>, + index_bits: &Array>, + commit: &Array>, + ) { + self.push(DslIr::VerifyBatch( + dimensions.clone(), + opened_values.clone(), + proof.clone(), + index_bits.clone(), + commit.clone(), + )); + } +} From 02feb4f56e222f233b06515869df4069d7e02af7 Mon Sep 17 00:00:00 2001 From: TlatoaniHJ Date: Wed, 15 Jan 2025 15:01:56 -0500 Subject: [PATCH 28/50] Make fibonacci_small pass (without ext verify_batch) --- extensions/native/circuit/src/extension.rs | 18 ++- extensions/native/circuit/src/lib.rs | 2 +- .../native/circuit/src/verify_batch/air.rs | 41 ++++--- .../native/circuit/src/verify_batch/chip.rs | 46 +++++--- .../circuit/src/verify_batch/columns.rs | 4 +- .../native/circuit/src/verify_batch/mod.rs | 2 +- .../native/circuit/src/verify_batch/tests.rs | 25 ++-- .../native/circuit/src/verify_batch/trace.rs | 22 ++-- .../native/compiler/src/asm/compiler.rs | 15 +++ .../native/compiler/src/asm/instruction.rs | 11 +- .../native/compiler/src/conversion/mod.rs | 12 +- extensions/native/recursion/src/fri/mod.rs | 107 ++++-------------- 12 files changed, 156 insertions(+), 149 deletions(-) diff --git a/extensions/native/circuit/src/extension.rs b/extensions/native/circuit/src/extension.rs index b6314bb8b3..3c84809e0f 100644 --- a/extensions/native/circuit/src/extension.rs +++ b/extensions/native/circuit/src/extension.rs @@ -17,7 +17,7 @@ use openvm_instructions::{ }; use openvm_native_compiler::{ FieldArithmeticOpcode, FieldExtensionOpcode, FriOpcode, NativeBranchEqualOpcode, - NativeJalOpcode, NativeLoadStoreOpcode, NativePhantom, + NativeJalOpcode, NativeLoadStoreOpcode, NativePhantom, VerifyBatchOpcode, }; use openvm_poseidon2_air::Poseidon2Config; use openvm_rv32im_circuit::BranchEqualCoreChip; @@ -25,7 +25,7 @@ use openvm_stark_backend::p3_field::PrimeField32; use serde::{Deserialize, Serialize}; use strum::IntoEnumIterator; -use crate::{adapters::*, phantom::*, *}; +use crate::{adapters::*, chip::VerifyBatchChip, phantom::*, *}; #[derive(Clone, Debug, Serialize, Deserialize, VmConfig, derive_new::new)] pub struct NativeConfig { @@ -78,6 +78,7 @@ pub enum NativeExecutor { FieldExtension(FieldExtensionChip), Poseidon2(NativePoseidon2Chip), FriReducedOpening(FriReducedOpeningChip), + VerifyBatch(VerifyBatchChip), } #[derive(From, ChipUsageGetter, Chip, AnyEnum)] @@ -170,6 +171,19 @@ impl VmExtension for Native { FriOpcode::iter().map(VmOpcode::with_default_offset), )?; + let verify_batch_chip = VerifyBatchChip::new( + execution_bus, + program_bus, + memory_bridge, + VerifyBatchOpcode::default_offset(), + offline_memory.clone(), + Poseidon2Config::default(), + ); + inventory.add_executor( + verify_batch_chip, + VerifyBatchOpcode::iter().map(VmOpcode::with_default_offset), + )?; + let poseidon2_chip = NativePoseidon2Chip::new( execution_bus, program_bus, diff --git a/extensions/native/circuit/src/lib.rs b/extensions/native/circuit/src/lib.rs index 762fbba1b2..60c902b831 100644 --- a/extensions/native/circuit/src/lib.rs +++ b/extensions/native/circuit/src/lib.rs @@ -8,7 +8,6 @@ mod fri; mod jal; mod loadstore; mod poseidon2; - mod verify_batch; pub use branch_eq::*; @@ -19,6 +18,7 @@ pub use fri::*; pub use jal::*; pub use loadstore::*; pub use poseidon2::*; +pub use verify_batch::*; mod extension; pub use extension::*; diff --git a/extensions/native/circuit/src/verify_batch/air.rs b/extensions/native/circuit/src/verify_batch/air.rs index 8483045229..1c1eaf86e3 100644 --- a/extensions/native/circuit/src/verify_batch/air.rs +++ b/extensions/native/circuit/src/verify_batch/air.rs @@ -15,7 +15,7 @@ use openvm_stark_backend::{ p3_matrix::Matrix, rap::{BaseAirWithPublicValues, PartitionedBaseAir}, }; - +use crate::chip::NUM_INITIAL_READS; use crate::verify_batch::{columns::VerifyBatchCols, CHUNK}; #[derive(Clone, Debug)] @@ -66,6 +66,7 @@ impl Air end_timestamp, dim_register, opened_register, + opened_length_register, sibling_register, index_register, commit_register, @@ -82,7 +83,8 @@ impl Air read_initial_height_or_root_is_on_right, read_final_height_or_sibling_array_start, dim_base_pointer_read, - opened_base_pointer_and_length_read, + opened_base_pointer_read, + opened_length_read, sibling_base_pointer_read, index_base_pointer_read, commit_pointer_read, @@ -276,7 +278,7 @@ impl Air .when(next.incorporate_row + next.incorporate_sibling) .assert_eq(next.proof_index, AB::F::ZERO); - let timestamp_after_end_operations = start_timestamp + AB::F::from_canonical_usize(5 + 1); + let timestamp_after_initial_reads = start_timestamp + AB::F::from_canonical_usize(NUM_INITIAL_READS); builder .when(end.clone()) @@ -288,6 +290,7 @@ impl Air [ dim_register, opened_register, + opened_length_register, sibling_register, index_register, commit_register, @@ -309,16 +312,24 @@ impl Air self.memory_bridge .read( MemoryAddress::new(address_space, opened_register), - [opened_base_pointer, opened_length], + [opened_base_pointer], very_first_timestamp + AB::F::ONE, - &opened_base_pointer_and_length_read, + &opened_base_pointer_read, + ) + .eval(builder, end_top_level); + self.memory_bridge + .read( + MemoryAddress::new(address_space, opened_length_register), + [opened_length], + very_first_timestamp + AB::F::TWO, + &opened_length_read, ) .eval(builder, end_top_level); self.memory_bridge .read( MemoryAddress::new(address_space, sibling_register), [sibling_base_pointer], - very_first_timestamp + AB::F::TWO, + very_first_timestamp + AB::F::from_canonical_usize(3), &sibling_base_pointer_read, ) .eval(builder, end_top_level); @@ -326,7 +337,7 @@ impl Air .read( MemoryAddress::new(address_space, index_register), [index_base_pointer], - very_first_timestamp + AB::F::from_canonical_usize(3), + very_first_timestamp + AB::F::from_canonical_usize(4), &index_base_pointer_read, ) .eval(builder, end_top_level); @@ -334,7 +345,7 @@ impl Air .read( MemoryAddress::new(address_space, commit_register), [commit_pointer], - very_first_timestamp + AB::F::from_canonical_usize(4), + very_first_timestamp + AB::F::from_canonical_usize(5), &commit_pointer_read, ) .eval(builder, end_top_level); @@ -343,7 +354,7 @@ impl Air .read( MemoryAddress::new(address_space, commit_pointer), left_output, - very_first_timestamp + AB::F::from_canonical_usize(5), + very_first_timestamp + AB::F::from_canonical_usize(6), &commit_read, ) .eval(builder, end_top_level); @@ -404,7 +415,7 @@ impl Air builder, true, incorporate_row, - timestamp_after_end_operations.clone(), + timestamp_after_initial_reads.clone(), end_timestamp - AB::F::TWO, opened_base_pointer, initial_opened_index, @@ -461,15 +472,15 @@ impl Air .read( MemoryAddress::new(address_space, index_base_pointer + proof_index), [root_is_on_right], - timestamp_after_end_operations.clone(), + timestamp_after_initial_reads.clone(), &read_initial_height_or_root_is_on_right, ) .eval(builder, incorporate_sibling); self.memory_bridge .read( - MemoryAddress::new(address_space, sibling_base_pointer + proof_index), + MemoryAddress::new(address_space, sibling_base_pointer + (proof_index * AB::F::TWO)), [sibling_array_start], - timestamp_after_end_operations.clone() + AB::F::ONE, + timestamp_after_initial_reads.clone() + AB::F::ONE, &read_final_height_or_sibling_array_start, ) .eval(builder, incorporate_sibling); @@ -492,7 +503,7 @@ impl Air ), [(root_is_on_right * left_input[i]) + ((AB::Expr::ONE - root_is_on_right) * right_input[i])], - timestamp_after_end_operations.clone() + AB::F::from_canonical_usize(2 + i), + timestamp_after_initial_reads.clone() + AB::F::from_canonical_usize(2 + i), &cells[i].read, ) .eval(builder, incorporate_sibling); @@ -500,7 +511,7 @@ impl Air builder.when(incorporate_sibling).assert_eq( end_timestamp, - timestamp_after_end_operations + AB::F::from_canonical_usize(2 + CHUNK), + timestamp_after_initial_reads + AB::F::from_canonical_usize(2 + CHUNK), ); } } diff --git a/extensions/native/circuit/src/verify_batch/chip.rs b/extensions/native/circuit/src/verify_batch/chip.rs index 124a7b0ed7..4a52000e9f 100644 --- a/extensions/native/circuit/src/verify_batch/chip.rs +++ b/extensions/native/circuit/src/verify_batch/chip.rs @@ -30,7 +30,8 @@ pub struct VerifyBatchRecord { pub commit_pointer: F, pub dim_base_pointer_read: RecordId, - pub opened_base_pointer_and_length_read: RecordId, + pub opened_base_pointer_read: RecordId, + pub opened_length_read: RecordId, pub sibling_base_pointer_read: RecordId, pub index_base_pointer_read: RecordId, pub commit_pointer_read: RecordId, @@ -42,7 +43,7 @@ pub struct VerifyBatchRecord { impl VerifyBatchRecord { pub fn address_space(&self) -> usize { - self.instruction.f.as_canonical_u32() as usize + self.instruction.g.as_canonical_u32() as usize } } @@ -129,6 +130,8 @@ impl VerifyBatchChip InstructionExecutor for VerifyBatchChip { @@ -141,17 +144,24 @@ impl InstructionExecutor let &Instruction { a: dim_register, b: opened_register, - c: sibling_register, - d: index_register, - e: commit_register, - f: address_space, + c: opened_length_register, + d: sibling_register, + e: index_register, + f: commit_register, + g: address_space, .. } = instruction; + println!("dim_register = {dim_register}, opened_register = {opened_register}, sibling_register = {sibling_register}, index_register = {index_register}, commit_register = {commit_register}, address_space = {address_space}"); + let (dim_base_pointer_read, dim_base_pointer) = memory.read_cell(address_space, dim_register); - let (opened_base_pointer_and_length_read, [opened_base_pointer, opened_length]) = - memory.read(address_space, opened_register); + /*let (opened_base_pointer_and_length_read, [opened_base_pointer, opened_length]) = + memory.read(address_space, opened_register);*/ + let (opened_base_pointer_read, opened_base_pointer) = + memory.read_cell(address_space, opened_register); + let (opened_length_read, opened_length) = + memory.read_cell(address_space, opened_length_register); let (sibling_base_pointer_read, sibling_base_pointer) = memory.read_cell(address_space, sibling_register); let (index_base_pointer_read, index_base_pointer) = @@ -160,6 +170,8 @@ impl InstructionExecutor memory.read_cell(address_space, commit_register); let (commit_read, commit) = memory.read(address_space, commit_pointer); + println!("dim_base_pointer = {dim_base_pointer}, opened_base_pointer = {opened_base_pointer} [opened_length = {opened_length}], sibling_base_pointer = {sibling_base_pointer}, index_base_pointer = {index_base_pointer}, commit_pointer = {commit_pointer}"); + let opened_length = opened_length.as_canonical_u32() as usize; let initial_height = memory @@ -180,7 +192,7 @@ impl InstructionExecutor ) == F::from_canonical_u32(height) { let initial_opened_index = opened_index; - for _ in 0..6 { + for _ in 0..NUM_INITIAL_READS { memory.increment_timestamp(); } let mut chunks = vec![]; @@ -217,6 +229,7 @@ impl InstructionExecutor address_space, opened_base_pointer + F::from_canonical_usize(2 * opened_index), ); + println!("chunks = {}, i = {i} | opened_index = {opened_index}, row_pointer = {new_row_pointer}, row_len = {row_len}", chunks.len()); row_pointer = new_row_pointer.as_canonical_u32() as usize; row_end = row_pointer + row_len.as_canonical_u32() as usize; Some(result) @@ -262,11 +275,13 @@ impl InstructionExecutor dim_base_pointer + F::from_canonical_usize(initial_opened_index), ); assert_eq!(height_check, F::from_canonical_u32(height)); + println!("initial| dim[{initial_opened_index}] = {height_check}"); let (final_height_read, height_check) = memory.read_cell( address_space, dim_base_pointer + F::from_canonical_usize(final_opened_index), ); assert_eq!(height_check, F::from_canonical_u32(height)); + println!("final| dim[{final_opened_index}] = {height_check}"); let hash: [F; CHUNK] = std::array::from_fn(|i| rolling_hash[i]); @@ -293,7 +308,8 @@ impl InstructionExecutor let incorporate_sibling = if height == 1 { None } else { - for _ in 0..6 { + println!("height = {height}, proof_index = {proof_index}"); + for _ in 0..NUM_INITIAL_READS { memory.increment_timestamp(); } @@ -301,13 +317,15 @@ impl InstructionExecutor address_space, index_base_pointer + F::from_canonical_usize(proof_index), ); + println!("\troot_is_on_right = {root_is_on_right}"); let root_is_on_right = root_is_on_right == F::ONE; let (read_sibling_array_start, sibling_array_start) = memory.read_cell( address_space, - sibling_base_pointer + F::from_canonical_usize(proof_index), + sibling_base_pointer + F::from_canonical_usize(2 * proof_index), ); let sibling_array_start = sibling_array_start.as_canonical_u32() as usize; + println!("\troot_is_on_right = {root_is_on_right}, sibling_array_start = {sibling_array_start}"); let mut sibling = [F::ZERO; CHUNK]; let mut reads = vec![]; @@ -319,7 +337,8 @@ impl InstructionExecutor sibling[i] = value; reads.push(read); } - + println!("\tsibling = {:?}", sibling); + let (p2_input, new_root) = if root_is_on_right { self.compress(sibling, root) } else { @@ -357,7 +376,8 @@ impl InstructionExecutor index_base_pointer, commit_pointer, dim_base_pointer_read, - opened_base_pointer_and_length_read, + opened_base_pointer_read, + opened_length_read, sibling_base_pointer_read, index_base_pointer_read, commit_pointer_read, diff --git a/extensions/native/circuit/src/verify_batch/columns.rs b/extensions/native/circuit/src/verify_batch/columns.rs index 8ec3c7c9cd..7919f5b9d1 100644 --- a/extensions/native/circuit/src/verify_batch/columns.rs +++ b/extensions/native/circuit/src/verify_batch/columns.rs @@ -30,6 +30,7 @@ pub struct VerifyBatchCols { // all other than address_space can be shared (saves 5 cols) pub dim_register: T, pub opened_register: T, + pub opened_length_register: T, pub sibling_register: T, pub index_register: T, pub commit_register: T, @@ -53,7 +54,8 @@ pub struct VerifyBatchCols { // these can be optimized to be shared with cells[i].read_row_pointer_and_length (saves 15 cols) pub dim_base_pointer_read: MemoryReadAuxCols, - pub opened_base_pointer_and_length_read: MemoryReadAuxCols, + pub opened_base_pointer_read: MemoryReadAuxCols, + pub opened_length_read: MemoryReadAuxCols, pub sibling_base_pointer_read: MemoryReadAuxCols, pub index_base_pointer_read: MemoryReadAuxCols, pub commit_pointer_read: MemoryReadAuxCols, diff --git a/extensions/native/circuit/src/verify_batch/mod.rs b/extensions/native/circuit/src/verify_batch/mod.rs index 5e30a52f10..682226edfc 100644 --- a/extensions/native/circuit/src/verify_batch/mod.rs +++ b/extensions/native/circuit/src/verify_batch/mod.rs @@ -1,7 +1,7 @@ use crate::NATIVE_POSEIDON2_CHUNK_SIZE; mod air; -mod chip; +pub mod chip; mod columns; #[cfg(test)] mod tests; diff --git a/extensions/native/circuit/src/verify_batch/tests.rs b/extensions/native/circuit/src/verify_batch/tests.rs index 42a06f614c..f04f478bde 100644 --- a/extensions/native/circuit/src/verify_batch/tests.rs +++ b/extensions/native/circuit/src/verify_batch/tests.rs @@ -161,11 +161,12 @@ fn test(cases: [Case; N]) { commit, } = instance; - let dim_register = gen_pointer(&mut rng, 2); - let opened_register = gen_pointer(&mut rng, 2); - let sibling_register = gen_pointer(&mut rng, 2); - let index_register = gen_pointer(&mut rng, 2); - let commit_register = gen_pointer(&mut rng, 2); + let dim_register = gen_pointer(&mut rng, 1); + let opened_register = gen_pointer(&mut rng, 1); + let opened_length_register = gen_pointer(&mut rng, 1); + let sibling_register = gen_pointer(&mut rng, 1); + let index_register = gen_pointer(&mut rng, 1); + let commit_register = gen_pointer(&mut rng, 1); let dim_base_pointer = gen_pointer(&mut rng, 1); let opened_base_pointer = gen_pointer(&mut rng, 2); @@ -173,23 +174,24 @@ fn test(cases: [Case; N]) { let index_base_pointer = gen_pointer(&mut rng, 1); let commit_pointer = gen_pointer(&mut rng, 1); - tester.write_usize(address_space, dim_register, [dim_base_pointer, dim.len()]); + tester.write_usize(address_space, dim_register, [dim_base_pointer]); tester.write_usize( address_space, opened_register, - [opened_base_pointer, opened.len()], + [opened_base_pointer], ); + tester.write_usize(address_space, opened_length_register, [opened.len()]); tester.write_usize( address_space, sibling_register, - [sibling_base_pointer, proof.len()], + [sibling_base_pointer], ); tester.write_usize( address_space, index_register, - [index_base_pointer, root_is_on_right.len()], + [index_base_pointer], ); - tester.write_usize(address_space, commit_register, [commit_pointer, CHUNK]); + tester.write_usize(address_space, commit_register, [commit_pointer]); for (i, &dim_value) in dim.iter().enumerate() { tester.write_usize(address_space, dim_base_pointer + i, [dim_value]); @@ -207,7 +209,7 @@ fn test(cases: [Case; N]) { } for (i, &sibling) in proof.iter().enumerate() { let row_pointer = gen_pointer(&mut rng, 1); - tester.write_usize(address_space, sibling_base_pointer + i, [row_pointer]); + tester.write_usize(address_space, sibling_base_pointer + (2 * i), [row_pointer, CHUNK]); tester.write(address_space, row_pointer, sibling); } for (i, &bit) in root_is_on_right.iter().enumerate() { @@ -222,6 +224,7 @@ fn test(cases: [Case; N]) { [ dim_register, opened_register, + opened_length_register, sibling_register, index_register, commit_register, diff --git a/extensions/native/circuit/src/verify_batch/trace.rs b/extensions/native/circuit/src/verify_batch/trace.rs index d8edf7eae9..a05051c58a 100644 --- a/extensions/native/circuit/src/verify_batch/trace.rs +++ b/extensions/native/circuit/src/verify_batch/trace.rs @@ -13,7 +13,7 @@ use openvm_stark_backend::{ Chip, ChipUsageGetter, }; use rayon::{iter::ParallelIterator, slice::ParallelSliceMut}; - +use crate::chip::NUM_INITIAL_READS; use crate::verify_batch::{ chip::{ CellRecord, IncorporateRowRecord, IncorporateSiblingRecord, InsideRowRecord, @@ -74,7 +74,7 @@ impl VerifyBatchChip VerifyBatchChip VerifyBatchChip VerifyBatchChip + TwoAdicField> AsmCo debug_info, ); } + DslIr::VerifyBatch(dim, opened, sibling, index, commit) => { + println!("[DslIr::VerifyBatch] dim.ptr.fp() = {}, opened.ptr().fp() = {}, sibling.ptr().fp() = {}, index.ptr().fp() = {}, commit.ptr().fp() = {}", dim.ptr().fp(), opened.ptr().fp(), sibling.ptr().fp(), index.ptr().fp(), commit.ptr().fp()); + println!("[DslIr::VerifyBatch] dim.len().fp() = {}, opened.len().fp() = {}, sibling.len().fp() = {}, index.len().fp() = {}, commit.len().fp() = {}", dim.len().get_var().fp(), opened.len().get_var().fp(), sibling.len().get_var().fp(), index.len().get_var().fp(), commit.len().get_var().fp()); + self.push( + AsmInstruction::VerifyBatch( + dim.ptr().fp(), + opened.ptr().fp(), + opened.len().get_var().fp(), + sibling.ptr().fp(), + index.ptr().fp(), + commit.ptr().fp(), + ), + debug_info, + ); + } _ => unimplemented!(), } } diff --git a/extensions/native/compiler/src/asm/instruction.rs b/extensions/native/compiler/src/asm/instruction.rs index 5e1765c4dd..02f2d1f785 100644 --- a/extensions/native/compiler/src/asm/instruction.rs +++ b/extensions/native/compiler/src/asm/instruction.rs @@ -114,8 +114,8 @@ pub enum AsmInstruction { /// (a, b, res, len, alpha, alpha_pow) FriReducedOpening(i32, i32, i32, i32, i32, i32), - // (dim, opened, sibling, index, commit) - VerifyBatch(i32, i32, i32, i32, i32), + // (dim, opened, opened_length, sibling, index, commit) + VerifyBatch(i32, i32, i32, i32, i32, i32), /// Print a variable. PrintV(i32), @@ -350,6 +350,13 @@ impl> AsmInstruction { a, b, res, len, alpha, alpha_pow ) } + AsmInstruction::VerifyBatch(dim, opened, opened_length, sibling, index, commit) => { + write!( + f, + "verify_batch ({})fp, ({})fp, ({})fp, ({})fp, ({})fp, ({})fp", + dim, opened, opened_length, sibling, index, commit + ) + } } } } diff --git a/extensions/native/compiler/src/conversion/mod.rs b/extensions/native/compiler/src/conversion/mod.rs index 18836080bd..9c43e6e335 100644 --- a/extensions/native/compiler/src/conversion/mod.rs +++ b/extensions/native/compiler/src/conversion/mod.rs @@ -649,15 +649,15 @@ fn convert_instruction>( f: i32_f(alpha), g: i32_f(alpha_pow), }], - AsmInstruction::VerifyBatch(dim, opened, sibling, index, commit) => vec![Instruction { + AsmInstruction::VerifyBatch(dim, opened, opened_length, sibling, index, commit) => vec![Instruction { opcode: options.opcode_with_offset(VerifyBatchOpcode::VERIFY_BATCH), a: i32_f(dim), b: i32_f(opened), - c: i32_f(sibling), - d: i32_f(index), - e: i32_f(commit), - f: AS::Native.to_field(), - g: F::ZERO, + c: i32_f(opened_length), + d: i32_f(sibling), + e: i32_f(index), + f: i32_f(commit), + g: AS::Native.to_field(), }], }; diff --git a/extensions/native/recursion/src/fri/mod.rs b/extensions/native/recursion/src/fri/mod.rs index 615ac11a60..966da42043 100644 --- a/extensions/native/recursion/src/fri/mod.rs +++ b/extensions/native/recursion/src/fri/mod.rs @@ -101,14 +101,14 @@ where let opened_values = builder.array(1); builder.set_value(&opened_values, 0, evals); builder.cycle_tracker_start("verify-batch-ext"); - verify_batch::( + /*verify_batch::( builder, &commit, dims_slice, index_pair, &NestedOpenedValues::Ext(opened_values), &step.opening_proof, - ); + );*/ builder.cycle_tracker_end("verify-batch-ext"); let two_adic_generator_one = config.get_two_adic_generator(builder, Usize::from(1)); @@ -149,6 +149,8 @@ pub fn verify_batch( opened_values: &NestedOpenedValues, proof: &Array>, ) { + //println!("verify_batch"); + //panic!(); if builder.flags.static_only { verify_batch_static( builder, @@ -160,95 +162,24 @@ pub fn verify_batch( ); return; } - let reducer = opened_values.create_reducer(builder); - let commit = if let DigestVariable::Felt(commit) = commit { - commit - } else { - panic!("Expected a Felt commitment"); + let dimensions = match dimensions { + Array::Dyn(ptr, len) => Array::Dyn(ptr, len.clone()), + _ => panic!("Expected a dynamic array of felts"), }; - // Cast DigestVariable into the concrete type. - let proof: Array>> = if let Array::Dyn(ptr, len) = proof { - Array::Dyn(*ptr, len.clone()) - } else { - panic!("Expected a dynamic array of Felt commitments"); + let opened_values = match opened_values { + NestedOpenedValues::Felt(arr) => arr, + _ => panic!("Expected a dynamic array of felts"), }; - - // The index of which table to process next. - let index: Usize = builder.eval(C::N::ZERO); - // The height of the current layer (padded). - let current_height = builder.get(&dimensions, index.clone()).height; - // Reduce all the tables that have the same height to a single root. - let root = reducer - .reduce_fast( - builder, - index.clone(), - &dimensions, - current_height.clone(), - opened_values, - ) - .into_inner_digest(); - let root_ptr = root.ptr(); - - // For each sibling in the proof, reconstruct the root. - let left: Ptr = builder.uninit(); - let right: Ptr = builder.uninit(); - builder.range(0, proof.len()).for_each(|i, builder| { - let sibling = builder.get_ptr(&proof, i); - let bit = builder.get(&index_bits, i); - - builder.if_eq(bit, C::N::ONE).then_or_else( - |builder| { - builder.assign(&left, sibling); - builder.assign(&right, root_ptr); - }, - |builder| { - builder.assign(&left, root_ptr); - builder.assign(&right, sibling); - }, - ); - - builder.poseidon2_compress_x( - &Array::Dyn(root_ptr, Usize::from(0)), - &Array::Dyn(left, Usize::from(0)), - &Array::Dyn(right, Usize::from(0)), - ); - builder.assign( - ¤t_height, - current_height.clone() * (C::N::TWO.inverse()), - ); - - builder - .if_ne(index.clone(), dimensions.len()) - .then(|builder| { - let next_height = builder.get(&dimensions, index.clone()).height; - builder - .if_eq(next_height, current_height.clone()) - .then(|builder| { - let next_height_openings_digest = reducer - .reduce_fast( - builder, - index.clone(), - &dimensions, - current_height.clone(), - opened_values, - ) - .into_inner_digest(); - builder.poseidon2_compress_x( - &root.clone(), - &root.clone(), - &next_height_openings_digest, - ); - }); - }) - }); - - // Assert that the commitments match. - for i in 0..DIGEST_SIZE { - let e1 = builder.get(commit, i); - let e2 = builder.get(&root, i); - builder.assert_felt_eq(e1, e2); - } + let proof = match proof { + Array::Dyn(ptr, len) => Array::Dyn(*ptr, len.clone()), + _ => panic!("Expected a dynamic array of felts"), + }; + let commit = match commit { + DigestVariable::Felt(arr) => arr, + _ => panic!("Expected a dynamic array of felts"), + }; + builder.verify_batch(&dimensions, opened_values, &proof, &index_bits, commit); } /// [static version] Verifies a batch opening. From d2b09423de1d208290a1969ade1e02c92149ef3f Mon Sep 17 00:00:00 2001 From: TlatoaniHJ Date: Wed, 15 Jan 2025 15:03:59 -0500 Subject: [PATCH 29/50] Remove debugging statements --- .../native/circuit/src/verify_batch/air.rs | 15 ++++++++---- .../native/circuit/src/verify_batch/chip.rs | 15 +----------- .../native/circuit/src/verify_batch/tests.rs | 24 +++++++------------ .../native/circuit/src/verify_batch/trace.rs | 20 +++++++++------- .../native/compiler/src/asm/compiler.rs | 2 -- 5 files changed, 32 insertions(+), 44 deletions(-) diff --git a/extensions/native/circuit/src/verify_batch/air.rs b/extensions/native/circuit/src/verify_batch/air.rs index 1c1eaf86e3..4d37a39986 100644 --- a/extensions/native/circuit/src/verify_batch/air.rs +++ b/extensions/native/circuit/src/verify_batch/air.rs @@ -15,8 +15,11 @@ use openvm_stark_backend::{ p3_matrix::Matrix, rap::{BaseAirWithPublicValues, PartitionedBaseAir}, }; -use crate::chip::NUM_INITIAL_READS; -use crate::verify_batch::{columns::VerifyBatchCols, CHUNK}; + +use crate::{ + chip::NUM_INITIAL_READS, + verify_batch::{columns::VerifyBatchCols, CHUNK}, +}; #[derive(Clone, Debug)] pub struct VerifyBatchAir { @@ -278,7 +281,8 @@ impl Air .when(next.incorporate_row + next.incorporate_sibling) .assert_eq(next.proof_index, AB::F::ZERO); - let timestamp_after_initial_reads = start_timestamp + AB::F::from_canonical_usize(NUM_INITIAL_READS); + let timestamp_after_initial_reads = + start_timestamp + AB::F::from_canonical_usize(NUM_INITIAL_READS); builder .when(end.clone()) @@ -478,7 +482,10 @@ impl Air .eval(builder, incorporate_sibling); self.memory_bridge .read( - MemoryAddress::new(address_space, sibling_base_pointer + (proof_index * AB::F::TWO)), + MemoryAddress::new( + address_space, + sibling_base_pointer + (proof_index * AB::F::TWO), + ), [sibling_array_start], timestamp_after_initial_reads.clone() + AB::F::ONE, &read_final_height_or_sibling_array_start, diff --git a/extensions/native/circuit/src/verify_batch/chip.rs b/extensions/native/circuit/src/verify_batch/chip.rs index 4a52000e9f..02831cf89a 100644 --- a/extensions/native/circuit/src/verify_batch/chip.rs +++ b/extensions/native/circuit/src/verify_batch/chip.rs @@ -152,12 +152,8 @@ impl InstructionExecutor .. } = instruction; - println!("dim_register = {dim_register}, opened_register = {opened_register}, sibling_register = {sibling_register}, index_register = {index_register}, commit_register = {commit_register}, address_space = {address_space}"); - let (dim_base_pointer_read, dim_base_pointer) = memory.read_cell(address_space, dim_register); - /*let (opened_base_pointer_and_length_read, [opened_base_pointer, opened_length]) = - memory.read(address_space, opened_register);*/ let (opened_base_pointer_read, opened_base_pointer) = memory.read_cell(address_space, opened_register); let (opened_length_read, opened_length) = @@ -170,8 +166,6 @@ impl InstructionExecutor memory.read_cell(address_space, commit_register); let (commit_read, commit) = memory.read(address_space, commit_pointer); - println!("dim_base_pointer = {dim_base_pointer}, opened_base_pointer = {opened_base_pointer} [opened_length = {opened_length}], sibling_base_pointer = {sibling_base_pointer}, index_base_pointer = {index_base_pointer}, commit_pointer = {commit_pointer}"); - let opened_length = opened_length.as_canonical_u32() as usize; let initial_height = memory @@ -229,7 +223,6 @@ impl InstructionExecutor address_space, opened_base_pointer + F::from_canonical_usize(2 * opened_index), ); - println!("chunks = {}, i = {i} | opened_index = {opened_index}, row_pointer = {new_row_pointer}, row_len = {row_len}", chunks.len()); row_pointer = new_row_pointer.as_canonical_u32() as usize; row_end = row_pointer + row_len.as_canonical_u32() as usize; Some(result) @@ -275,13 +268,11 @@ impl InstructionExecutor dim_base_pointer + F::from_canonical_usize(initial_opened_index), ); assert_eq!(height_check, F::from_canonical_u32(height)); - println!("initial| dim[{initial_opened_index}] = {height_check}"); let (final_height_read, height_check) = memory.read_cell( address_space, dim_base_pointer + F::from_canonical_usize(final_opened_index), ); assert_eq!(height_check, F::from_canonical_u32(height)); - println!("final| dim[{final_opened_index}] = {height_check}"); let hash: [F; CHUNK] = std::array::from_fn(|i| rolling_hash[i]); @@ -308,7 +299,6 @@ impl InstructionExecutor let incorporate_sibling = if height == 1 { None } else { - println!("height = {height}, proof_index = {proof_index}"); for _ in 0..NUM_INITIAL_READS { memory.increment_timestamp(); } @@ -317,7 +307,6 @@ impl InstructionExecutor address_space, index_base_pointer + F::from_canonical_usize(proof_index), ); - println!("\troot_is_on_right = {root_is_on_right}"); let root_is_on_right = root_is_on_right == F::ONE; let (read_sibling_array_start, sibling_array_start) = memory.read_cell( @@ -325,7 +314,6 @@ impl InstructionExecutor sibling_base_pointer + F::from_canonical_usize(2 * proof_index), ); let sibling_array_start = sibling_array_start.as_canonical_u32() as usize; - println!("\troot_is_on_right = {root_is_on_right}, sibling_array_start = {sibling_array_start}"); let mut sibling = [F::ZERO; CHUNK]; let mut reads = vec![]; @@ -337,8 +325,7 @@ impl InstructionExecutor sibling[i] = value; reads.push(read); } - println!("\tsibling = {:?}", sibling); - + let (p2_input, new_root) = if root_is_on_right { self.compress(sibling, root) } else { diff --git a/extensions/native/circuit/src/verify_batch/tests.rs b/extensions/native/circuit/src/verify_batch/tests.rs index f04f478bde..4e0d9e136d 100644 --- a/extensions/native/circuit/src/verify_batch/tests.rs +++ b/extensions/native/circuit/src/verify_batch/tests.rs @@ -175,22 +175,10 @@ fn test(cases: [Case; N]) { let commit_pointer = gen_pointer(&mut rng, 1); tester.write_usize(address_space, dim_register, [dim_base_pointer]); - tester.write_usize( - address_space, - opened_register, - [opened_base_pointer], - ); + tester.write_usize(address_space, opened_register, [opened_base_pointer]); tester.write_usize(address_space, opened_length_register, [opened.len()]); - tester.write_usize( - address_space, - sibling_register, - [sibling_base_pointer], - ); - tester.write_usize( - address_space, - index_register, - [index_base_pointer], - ); + tester.write_usize(address_space, sibling_register, [sibling_base_pointer]); + tester.write_usize(address_space, index_register, [index_base_pointer]); tester.write_usize(address_space, commit_register, [commit_pointer]); for (i, &dim_value) in dim.iter().enumerate() { @@ -209,7 +197,11 @@ fn test(cases: [Case; N]) { } for (i, &sibling) in proof.iter().enumerate() { let row_pointer = gen_pointer(&mut rng, 1); - tester.write_usize(address_space, sibling_base_pointer + (2 * i), [row_pointer, CHUNK]); + tester.write_usize( + address_space, + sibling_base_pointer + (2 * i), + [row_pointer, CHUNK], + ); tester.write(address_space, row_pointer, sibling); } for (i, &bit) in root_is_on_right.iter().enumerate() { diff --git a/extensions/native/circuit/src/verify_batch/trace.rs b/extensions/native/circuit/src/verify_batch/trace.rs index a05051c58a..7067f06df9 100644 --- a/extensions/native/circuit/src/verify_batch/trace.rs +++ b/extensions/native/circuit/src/verify_batch/trace.rs @@ -13,14 +13,17 @@ use openvm_stark_backend::{ Chip, ChipUsageGetter, }; use rayon::{iter::ParallelIterator, slice::ParallelSliceMut}; -use crate::chip::NUM_INITIAL_READS; -use crate::verify_batch::{ - chip::{ - CellRecord, IncorporateRowRecord, IncorporateSiblingRecord, InsideRowRecord, - VerifyBatchChip, VerifyBatchRecord, + +use crate::{ + chip::NUM_INITIAL_READS, + verify_batch::{ + chip::{ + CellRecord, IncorporateRowRecord, IncorporateSiblingRecord, InsideRowRecord, + VerifyBatchChip, VerifyBatchRecord, + }, + columns::VerifyBatchCols, + CHUNK, }, - columns::VerifyBatchCols, - CHUNK, }; impl ChipUsageGetter for VerifyBatchChip { @@ -74,7 +77,8 @@ impl VerifyBatchChip + TwoAdicField> AsmCo ); } DslIr::VerifyBatch(dim, opened, sibling, index, commit) => { - println!("[DslIr::VerifyBatch] dim.ptr.fp() = {}, opened.ptr().fp() = {}, sibling.ptr().fp() = {}, index.ptr().fp() = {}, commit.ptr().fp() = {}", dim.ptr().fp(), opened.ptr().fp(), sibling.ptr().fp(), index.ptr().fp(), commit.ptr().fp()); - println!("[DslIr::VerifyBatch] dim.len().fp() = {}, opened.len().fp() = {}, sibling.len().fp() = {}, index.len().fp() = {}, commit.len().fp() = {}", dim.len().get_var().fp(), opened.len().get_var().fp(), sibling.len().get_var().fp(), index.len().get_var().fp(), commit.len().get_var().fp()); self.push( AsmInstruction::VerifyBatch( dim.ptr().fp(), From 657c80d77dc9441eac651f1f61f1c052696fd6ae Mon Sep 17 00:00:00 2001 From: TlatoaniHJ Date: Wed, 15 Jan 2025 15:11:16 -0500 Subject: [PATCH 30/50] Make address space constant --- .../native/circuit/src/verify_batch/air.rs | 36 ++++++++----------- .../native/circuit/src/verify_batch/chip.rs | 9 ++--- .../circuit/src/verify_batch/columns.rs | 7 ++-- .../native/circuit/src/verify_batch/tests.rs | 1 - .../native/circuit/src/verify_batch/trace.rs | 3 -- .../native/compiler/src/conversion/mod.rs | 2 +- 6 files changed, 21 insertions(+), 37 deletions(-) diff --git a/extensions/native/circuit/src/verify_batch/air.rs b/extensions/native/circuit/src/verify_batch/air.rs index 4d37a39986..27a9eb73b1 100644 --- a/extensions/native/circuit/src/verify_batch/air.rs +++ b/extensions/native/circuit/src/verify_batch/air.rs @@ -28,6 +28,7 @@ pub struct VerifyBatchAir { pub internal_bus: VerifyBatchBus, pub(crate) subair: Arc>, pub(super) offset: usize, + pub(crate) address_space: F, } impl BaseAir for VerifyBatchAir { @@ -73,7 +74,6 @@ impl Air sibling_register, index_register, commit_register, - address_space, cells, initial_opened_index, final_opened_index, @@ -155,7 +155,6 @@ impl Air opened_base_pointer, initial_opened_index, cells[CHUNK - 1].opened_index, - address_space, left_output, ); @@ -207,7 +206,7 @@ impl Air let next_is_normal = AB::Expr::ONE - next_cell.is_first_in_row - next_cell.is_exhausted; self.memory_bridge .read( - MemoryAddress::new(address_space, cell.row_pointer), + MemoryAddress::new(self.address_space, cell.row_pointer), [left_input[i]], start_timestamp + AB::F::from_canonical_usize((2 * i) + 1), &cell.read, @@ -238,7 +237,7 @@ impl Air self.memory_bridge .read( MemoryAddress::new( - address_space, + self.address_space, opened_base_pointer + (cell.opened_index * AB::F::TWO), ), [cell.row_pointer.into(), cell.row_end - cell.row_pointer], @@ -298,7 +297,6 @@ impl Air sibling_register, index_register, commit_register, - address_space, ], ExecutionState::new(pc, very_first_timestamp), end_timestamp - very_first_timestamp, @@ -307,7 +305,7 @@ impl Air self.memory_bridge .read( - MemoryAddress::new(address_space, dim_register), + MemoryAddress::new(self.address_space, dim_register), [dim_base_pointer], very_first_timestamp, &dim_base_pointer_read, @@ -315,7 +313,7 @@ impl Air .eval(builder, end_top_level); self.memory_bridge .read( - MemoryAddress::new(address_space, opened_register), + MemoryAddress::new(self.address_space, opened_register), [opened_base_pointer], very_first_timestamp + AB::F::ONE, &opened_base_pointer_read, @@ -323,7 +321,7 @@ impl Air .eval(builder, end_top_level); self.memory_bridge .read( - MemoryAddress::new(address_space, opened_length_register), + MemoryAddress::new(self.address_space, opened_length_register), [opened_length], very_first_timestamp + AB::F::TWO, &opened_length_read, @@ -331,7 +329,7 @@ impl Air .eval(builder, end_top_level); self.memory_bridge .read( - MemoryAddress::new(address_space, sibling_register), + MemoryAddress::new(self.address_space, sibling_register), [sibling_base_pointer], very_first_timestamp + AB::F::from_canonical_usize(3), &sibling_base_pointer_read, @@ -339,7 +337,7 @@ impl Air .eval(builder, end_top_level); self.memory_bridge .read( - MemoryAddress::new(address_space, index_register), + MemoryAddress::new(self.address_space, index_register), [index_base_pointer], very_first_timestamp + AB::F::from_canonical_usize(4), &index_base_pointer_read, @@ -347,7 +345,7 @@ impl Air .eval(builder, end_top_level); self.memory_bridge .read( - MemoryAddress::new(address_space, commit_register), + MemoryAddress::new(self.address_space, commit_register), [commit_pointer], very_first_timestamp + AB::F::from_canonical_usize(5), &commit_pointer_read, @@ -356,7 +354,7 @@ impl Air self.memory_bridge .read( - MemoryAddress::new(address_space, commit_pointer), + MemoryAddress::new(self.address_space, commit_pointer), left_output, very_first_timestamp + AB::F::from_canonical_usize(6), &commit_read, @@ -373,7 +371,6 @@ impl Air when_top_level_not_end.assert_eq(next.index_base_pointer, index_base_pointer); when_top_level_not_end.assert_eq(next.start_timestamp, end_timestamp); when_top_level_not_end.assert_eq(next.opened_length, opened_length); - when_top_level_not_end.assert_eq(next.address_space, address_space); when_top_level_not_end .assert_eq(next.initial_opened_index, final_opened_index + AB::F::ONE); @@ -424,7 +421,6 @@ impl Air opened_base_pointer, initial_opened_index, final_opened_index, - address_space, row_hash, ); @@ -442,7 +438,7 @@ impl Air self.memory_bridge .read( - MemoryAddress::new(address_space, dim_base_pointer + initial_opened_index), + MemoryAddress::new(self.address_space, dim_base_pointer + initial_opened_index), [height], end_timestamp - AB::F::TWO, &read_initial_height_or_root_is_on_right, @@ -450,7 +446,7 @@ impl Air .eval(builder, incorporate_row); self.memory_bridge .read( - MemoryAddress::new(address_space, dim_base_pointer + final_opened_index), + MemoryAddress::new(self.address_space, dim_base_pointer + final_opened_index), [height], end_timestamp - AB::F::ONE, &read_final_height_or_sibling_array_start, @@ -474,7 +470,7 @@ impl Air self.memory_bridge .read( - MemoryAddress::new(address_space, index_base_pointer + proof_index), + MemoryAddress::new(self.address_space, index_base_pointer + proof_index), [root_is_on_right], timestamp_after_initial_reads.clone(), &read_initial_height_or_root_is_on_right, @@ -483,7 +479,7 @@ impl Air self.memory_bridge .read( MemoryAddress::new( - address_space, + self.address_space, sibling_base_pointer + (proof_index * AB::F::TWO), ), [sibling_array_start], @@ -505,7 +501,7 @@ impl Air self.memory_bridge .read( MemoryAddress::new( - address_space, + self.address_space, sibling_array_start + AB::F::from_canonical_usize(i), ), [(root_is_on_right * left_input[i]) @@ -534,7 +530,6 @@ impl VerifyBatchBus { opened_base_pointer: impl Into, initial_opened_index: impl Into, final_opened_index: impl Into, - address_space: impl Into, hash: [impl Into; CHUNK], ) { let mut fields = vec![ @@ -543,7 +538,6 @@ impl VerifyBatchBus { opened_base_pointer.into(), initial_opened_index.into(), final_opened_index.into(), - address_space.into(), ]; fields.extend(hash.into_iter().map(Into::into)); builder.push_interaction( diff --git a/extensions/native/circuit/src/verify_batch/chip.rs b/extensions/native/circuit/src/verify_batch/chip.rs index 02831cf89a..2bd5158e85 100644 --- a/extensions/native/circuit/src/verify_batch/chip.rs +++ b/extensions/native/circuit/src/verify_batch/chip.rs @@ -41,12 +41,6 @@ pub struct VerifyBatchRecord { pub top_level: Vec>, } -impl VerifyBatchRecord { - pub fn address_space(&self) -> usize { - self.instruction.g.as_canonical_u32() as usize - } -} - #[derive(Debug, Clone)] pub(super) struct TopLevelRecord { // must be present in first record @@ -112,6 +106,7 @@ impl VerifyBatchChip InstructionExecutor d: sibling_register, e: index_register, f: commit_register, - g: address_space, .. } = instruction; + let address_space = self.air.address_space; let (dim_base_pointer_read, dim_base_pointer) = memory.read_cell(address_space, dim_register); diff --git a/extensions/native/circuit/src/verify_batch/columns.rs b/extensions/native/circuit/src/verify_batch/columns.rs index 7919f5b9d1..a866ed735e 100644 --- a/extensions/native/circuit/src/verify_batch/columns.rs +++ b/extensions/native/circuit/src/verify_batch/columns.rs @@ -26,15 +26,14 @@ pub struct VerifyBatchCols { pub start_timestamp: T, pub end_timestamp: T, // only used for top level (so can be shared (saves 1 col) - // instruction (a, b, c, d, e) - // all other than address_space can be shared (saves 5 cols) + // instruction (a, b, c, d, e, f) + // all can be shared (saves 6 cols) pub dim_register: T, pub opened_register: T, pub opened_length_register: T, pub sibling_register: T, pub index_register: T, pub commit_register: T, - pub address_space: T, pub cells: [VerifyBatchCellCols; CHUNK], // initial/final opened index for a subsegment with same height @@ -52,7 +51,7 @@ pub struct VerifyBatchCols { pub sibling_base_pointer: T, pub index_base_pointer: T, - // these can be optimized to be shared with cells[i].read_row_pointer_and_length (saves 15 cols) + // these can be optimized to be shared with cells[i].read_row_pointer_and_length (saves 18 cols) pub dim_base_pointer_read: MemoryReadAuxCols, pub opened_base_pointer_read: MemoryReadAuxCols, pub opened_length_read: MemoryReadAuxCols, diff --git a/extensions/native/circuit/src/verify_batch/tests.rs b/extensions/native/circuit/src/verify_batch/tests.rs index 4e0d9e136d..6ead51e298 100644 --- a/extensions/native/circuit/src/verify_batch/tests.rs +++ b/extensions/native/circuit/src/verify_batch/tests.rs @@ -220,7 +220,6 @@ fn test(cases: [Case; N]) { sibling_register, index_register, commit_register, - address_space, ], ), ); diff --git a/extensions/native/circuit/src/verify_batch/trace.rs b/extensions/native/circuit/src/verify_batch/trace.rs index 7067f06df9..a8cdbc0ef4 100644 --- a/extensions/native/circuit/src/verify_batch/trace.rs +++ b/extensions/native/circuit/src/verify_batch/trace.rs @@ -81,7 +81,6 @@ impl VerifyBatchChip VerifyBatchChip VerifyBatchChip>( d: i32_f(sibling), e: i32_f(index), f: i32_f(commit), - g: AS::Native.to_field(), + g: F::ZERO, }], }; From 04b61921edbb1db9f500f1978748fdc4a104f972 Mon Sep 17 00:00:00 2001 From: TlatoaniHJ Date: Wed, 15 Jan 2025 15:32:39 -0500 Subject: [PATCH 31/50] Get varying open value size working for felt --- extensions/native/circuit/src/verify_batch/air.rs | 15 ++++++++++++++- .../native/circuit/src/verify_batch/chip.rs | 15 ++++++++++++++- .../native/circuit/src/verify_batch/columns.rs | 5 +++-- .../native/circuit/src/verify_batch/tests.rs | 1 + .../native/circuit/src/verify_batch/trace.rs | 3 +++ extensions/native/compiler/src/asm/compiler.rs | 4 ++-- extensions/native/compiler/src/asm/instruction.rs | 7 ++++--- extensions/native/compiler/src/conversion/mod.rs | 4 ++-- extensions/native/compiler/src/ir/instructions.rs | 3 ++- extensions/native/compiler/src/ir/verify_batch.rs | 2 +- 10 files changed, 46 insertions(+), 13 deletions(-) diff --git a/extensions/native/circuit/src/verify_batch/air.rs b/extensions/native/circuit/src/verify_batch/air.rs index 27a9eb73b1..71ee491ed4 100644 --- a/extensions/native/circuit/src/verify_batch/air.rs +++ b/extensions/native/circuit/src/verify_batch/air.rs @@ -74,6 +74,7 @@ impl Air sibling_register, index_register, commit_register, + opened_element_size_inv, cells, initial_opened_index, final_opened_index, @@ -153,6 +154,7 @@ impl Air very_first_timestamp, start_timestamp + AB::F::from_canonical_usize(2 * CHUNK), opened_base_pointer, + opened_element_size_inv, initial_opened_index, cells[CHUNK - 1].opened_index, left_output, @@ -167,6 +169,9 @@ impl Air builder .when(inside_row - end_inside_row) .assert_eq(next.opened_base_pointer, opened_base_pointer); + builder + .when(inside_row - end_inside_row) + .assert_eq(next.opened_element_size_inv, opened_element_size_inv); builder .when(inside_row - end_inside_row) .assert_eq(next.initial_opened_index, initial_opened_index); @@ -240,7 +245,10 @@ impl Air self.address_space, opened_base_pointer + (cell.opened_index * AB::F::TWO), ), - [cell.row_pointer.into(), cell.row_end - cell.row_pointer], + [ + cell.row_pointer.into(), + opened_element_size_inv * (cell.row_end - cell.row_pointer), + ], start_timestamp + AB::F::from_canonical_usize(2 * i), &cell.read_row_pointer_and_length, ) @@ -297,6 +305,7 @@ impl Air sibling_register, index_register, commit_register, + opened_element_size_inv, ], ExecutionState::new(pc, very_first_timestamp), end_timestamp - very_first_timestamp, @@ -371,6 +380,7 @@ impl Air when_top_level_not_end.assert_eq(next.index_base_pointer, index_base_pointer); when_top_level_not_end.assert_eq(next.start_timestamp, end_timestamp); when_top_level_not_end.assert_eq(next.opened_length, opened_length); + when_top_level_not_end.assert_eq(next.opened_element_size_inv, opened_element_size_inv); when_top_level_not_end .assert_eq(next.initial_opened_index, final_opened_index + AB::F::ONE); @@ -419,6 +429,7 @@ impl Air timestamp_after_initial_reads.clone(), end_timestamp - AB::F::TWO, opened_base_pointer, + opened_element_size_inv, initial_opened_index, final_opened_index, row_hash, @@ -528,6 +539,7 @@ impl VerifyBatchBus { start_timestamp: impl Into, end_timestamp: impl Into, opened_base_pointer: impl Into, + opened_element_size_inv: impl Into, initial_opened_index: impl Into, final_opened_index: impl Into, hash: [impl Into; CHUNK], @@ -536,6 +548,7 @@ impl VerifyBatchBus { start_timestamp.into(), end_timestamp.into(), opened_base_pointer.into(), + opened_element_size_inv.into(), initial_opened_index.into(), final_opened_index.into(), ]; diff --git a/extensions/native/circuit/src/verify_batch/chip.rs b/extensions/native/circuit/src/verify_batch/chip.rs index 2bd5158e85..90fcf848b5 100644 --- a/extensions/native/circuit/src/verify_batch/chip.rs +++ b/extensions/native/circuit/src/verify_batch/chip.rs @@ -41,6 +41,12 @@ pub struct VerifyBatchRecord { pub top_level: Vec>, } +impl VerifyBatchRecord { + pub fn opened_element_size_inv(&self) -> F { + self.instruction.g + } +} + #[derive(Debug, Clone)] pub(super) struct TopLevelRecord { // must be present in first record @@ -143,9 +149,15 @@ impl InstructionExecutor d: sibling_register, e: index_register, f: commit_register, + g: opened_element_size_inv, .. } = instruction; let address_space = self.air.address_space; + // calc inverse fast assuming opened_element_size in {1, 4} + let mut opened_element_size = F::ONE; + while opened_element_size * opened_element_size_inv != F::ONE { + opened_element_size += F::ONE; + } let (dim_base_pointer_read, dim_base_pointer) = memory.read_cell(address_space, dim_register); @@ -219,7 +231,8 @@ impl InstructionExecutor opened_base_pointer + F::from_canonical_usize(2 * opened_index), ); row_pointer = new_row_pointer.as_canonical_u32() as usize; - row_end = row_pointer + row_len.as_canonical_u32() as usize; + row_end = row_pointer + + (opened_element_size * row_len).as_canonical_u32() as usize; Some(result) } else { memory.increment_timestamp(); diff --git a/extensions/native/circuit/src/verify_batch/columns.rs b/extensions/native/circuit/src/verify_batch/columns.rs index a866ed735e..b8ad0345ba 100644 --- a/extensions/native/circuit/src/verify_batch/columns.rs +++ b/extensions/native/circuit/src/verify_batch/columns.rs @@ -26,14 +26,15 @@ pub struct VerifyBatchCols { pub start_timestamp: T, pub end_timestamp: T, // only used for top level (so can be shared (saves 1 col) - // instruction (a, b, c, d, e, f) - // all can be shared (saves 6 cols) + // instruction (a, b, c, d, e, f, g) + // all can be shared other than g (saves 6 cols) pub dim_register: T, pub opened_register: T, pub opened_length_register: T, pub sibling_register: T, pub index_register: T, pub commit_register: T, + pub opened_element_size_inv: T, pub cells: [VerifyBatchCellCols; CHUNK], // initial/final opened index for a subsegment with same height diff --git a/extensions/native/circuit/src/verify_batch/tests.rs b/extensions/native/circuit/src/verify_batch/tests.rs index 6ead51e298..7edbc58b5c 100644 --- a/extensions/native/circuit/src/verify_batch/tests.rs +++ b/extensions/native/circuit/src/verify_batch/tests.rs @@ -220,6 +220,7 @@ fn test(cases: [Case; N]) { sibling_register, index_register, commit_register, + 1, ], ), ); diff --git a/extensions/native/circuit/src/verify_batch/trace.rs b/extensions/native/circuit/src/verify_batch/trace.rs index a8cdbc0ef4..13c16fdd93 100644 --- a/extensions/native/circuit/src/verify_batch/trace.rs +++ b/extensions/native/circuit/src/verify_batch/trace.rs @@ -76,6 +76,7 @@ impl VerifyBatchChip VerifyBatchChip VerifyBatchChip + TwoAdicField> AsmCo debug_info, ); } - DslIr::VerifyBatch(dim, opened, sibling, index, commit) => { + DslIr::VerifyBatchFelt(dim, opened, sibling, index, commit) => { self.push( - AsmInstruction::VerifyBatch( + AsmInstruction::VerifyBatchFelt( dim.ptr().fp(), opened.ptr().fp(), opened.len().get_var().fp(), diff --git a/extensions/native/compiler/src/asm/instruction.rs b/extensions/native/compiler/src/asm/instruction.rs index 02f2d1f785..e6c4a22b98 100644 --- a/extensions/native/compiler/src/asm/instruction.rs +++ b/extensions/native/compiler/src/asm/instruction.rs @@ -114,8 +114,9 @@ pub enum AsmInstruction { /// (a, b, res, len, alpha, alpha_pow) FriReducedOpening(i32, i32, i32, i32, i32, i32), - // (dim, opened, opened_length, sibling, index, commit) - VerifyBatch(i32, i32, i32, i32, i32, i32), + /// (dim, opened, opened_length, sibling, index, commit) + /// opened values are field elements + VerifyBatchFelt(i32, i32, i32, i32, i32, i32), /// Print a variable. PrintV(i32), @@ -350,7 +351,7 @@ impl> AsmInstruction { a, b, res, len, alpha, alpha_pow ) } - AsmInstruction::VerifyBatch(dim, opened, opened_length, sibling, index, commit) => { + AsmInstruction::VerifyBatchFelt(dim, opened, opened_length, sibling, index, commit) => { write!( f, "verify_batch ({})fp, ({})fp, ({})fp, ({})fp, ({})fp, ({})fp", diff --git a/extensions/native/compiler/src/conversion/mod.rs b/extensions/native/compiler/src/conversion/mod.rs index 89ec75e77e..58c24df720 100644 --- a/extensions/native/compiler/src/conversion/mod.rs +++ b/extensions/native/compiler/src/conversion/mod.rs @@ -649,7 +649,7 @@ fn convert_instruction>( f: i32_f(alpha), g: i32_f(alpha_pow), }], - AsmInstruction::VerifyBatch(dim, opened, opened_length, sibling, index, commit) => vec![Instruction { + AsmInstruction::VerifyBatchFelt(dim, opened, opened_length, sibling, index, commit) => vec![Instruction { opcode: options.opcode_with_offset(VerifyBatchOpcode::VERIFY_BATCH), a: i32_f(dim), b: i32_f(opened), @@ -657,7 +657,7 @@ fn convert_instruction>( d: i32_f(sibling), e: i32_f(index), f: i32_f(commit), - g: F::ZERO, + g: F::ONE, }], }; diff --git a/extensions/native/compiler/src/ir/instructions.rs b/extensions/native/compiler/src/ir/instructions.rs index b0ee5538e8..5cddd0bf8b 100644 --- a/extensions/native/compiler/src/ir/instructions.rs +++ b/extensions/native/compiler/src/ir/instructions.rs @@ -278,7 +278,8 @@ pub enum DslIr { Ext, ), /// VerifyBatch(dim, opened, sibling, index, commit) - VerifyBatch( + /// opened values are Felts + VerifyBatchFelt( Array>, Array>>, Array>>, diff --git a/extensions/native/compiler/src/ir/verify_batch.rs b/extensions/native/compiler/src/ir/verify_batch.rs index 53180f0644..2254bbceab 100644 --- a/extensions/native/compiler/src/ir/verify_batch.rs +++ b/extensions/native/compiler/src/ir/verify_batch.rs @@ -9,7 +9,7 @@ impl Builder { index_bits: &Array>, commit: &Array>, ) { - self.push(DslIr::VerifyBatch( + self.push(DslIr::VerifyBatchFelt( dimensions.clone(), opened_values.clone(), proof.clone(), From d1a43e724101a0445362bf7dd639927c687ec2bb Mon Sep 17 00:00:00 2001 From: TlatoaniHJ Date: Wed, 15 Jan 2025 15:47:10 -0500 Subject: [PATCH 32/50] Fully integrate --- .../native/circuit/src/verify_batch/tests.rs | 55 +++++++++++++++---- .../native/compiler/src/asm/compiler.rs | 13 +++++ .../native/compiler/src/asm/instruction.rs | 13 ++++- .../native/compiler/src/conversion/mod.rs | 10 ++++ .../native/compiler/src/ir/instructions.rs | 9 +++ .../native/compiler/src/ir/verify_batch.rs | 20 ++++++- extensions/native/recursion/src/fri/mod.rs | 14 ++--- 7 files changed, 112 insertions(+), 22 deletions(-) diff --git a/extensions/native/circuit/src/verify_batch/tests.rs b/extensions/native/circuit/src/verify_batch/tests.rs index 7edbc58b5c..dca8984889 100644 --- a/extensions/native/circuit/src/verify_batch/tests.rs +++ b/extensions/native/circuit/src/verify_batch/tests.rs @@ -11,6 +11,7 @@ use openvm_stark_backend::{ utils::disable_debug_builder, verifier::VerificationError, }; +use openvm_stark_backend::p3_field::PrimeField32; use openvm_stark_sdk::{p3_baby_bear::BabyBear, utils::create_seeded_rng}; use rand::{rngs::StdRng, Rng}; @@ -80,6 +81,7 @@ struct VerifyBatchInstance { fn random_instance( rng: &mut StdRng, row_lengths: Vec>, + opened_element_size: usize, hash_function: impl Fn([F; CHUNK], [F; CHUNK]) -> ([F; CHUNK], [F; CHUNK]), ) -> VerifyBatchInstance { let mut dims = vec![]; @@ -91,7 +93,7 @@ fn random_instance( for &row_length in row_lengths { dims.push(height); let mut opened_row = vec![]; - for _ in 0..row_length { + for _ in 0..opened_element_size * row_length { opened_row.push(rng.gen()); } opened.push(opened_row); @@ -120,7 +122,10 @@ fn random_instance( const SBOX_REGISTERS: usize = 1; -type Case = Vec>; +struct Case { + row_lengths: Vec>, + opened_element_size: usize, +} fn test(cases: [Case; N]) { unsafe { @@ -143,8 +148,8 @@ fn test(cases: [Case; N]) { ); let mut rng = create_seeded_rng(); - for case in cases { - let instance = random_instance(&mut rng, case, |left, right| { + for Case { row_lengths, opened_element_size } in cases { + let instance = random_instance(&mut rng, row_lengths, opened_element_size,|left, right| { let concatenated = std::array::from_fn(|i| if i < CHUNK { left[i] } else { right[i - CHUNK] }); let permuted = chip.subchip.permute(concatenated); @@ -189,7 +194,7 @@ fn test(cases: [Case; N]) { tester.write_usize( address_space, opened_base_pointer + (2 * i), - [row_pointer, opened_row.len()], + [row_pointer, opened_row.len() / opened_element_size], ); for (j, &opened_value) in opened_row.iter().enumerate() { tester.write_cell(address_space, row_pointer + j, opened_value); @@ -209,6 +214,7 @@ fn test(cases: [Case; N]) { } tester.write(address_space, commit_pointer, commit); + let opened_element_size_inv = F::from_canonical_usize(opened_element_size).inverse().as_canonical_u32() as usize; tester.execute( &mut chip, &Instruction::from_usize( @@ -220,7 +226,7 @@ fn test(cases: [Case; N]) { sibling_register, index_register, commit_register, - 1, + opened_element_size_inv ], ), ); @@ -248,15 +254,40 @@ fn test(cases: [Case; N]) { } #[test] -fn verify_batch_test_1() { - test([vec![vec![3], vec![], vec![9, 2, 1, 13, 4], vec![16]]]); +fn verify_batch_test_felt() { + test([Case { row_lengths: vec![vec![3], vec![], vec![9, 2, 1, 13, 4], vec![16]], opened_element_size: 1 }]); +} + +#[test] +fn verify_batch_test_felt_multiple() { + test([ + Case { row_lengths: vec![vec![1, 1, 1, 2, 3], vec![9], vec![8]], opened_element_size: 1 }, + Case { row_lengths: vec![vec![], vec![], vec![], vec![1]], opened_element_size: 1 }, + Case { row_lengths: vec![vec![8], vec![7], vec![6]], opened_element_size: 1 }, + ]) +} + +#[test] +fn verify_batch_test_ext() { + test([Case { row_lengths: vec![vec![3], vec![], vec![1, 2, 1], vec![4]], opened_element_size: 4 }]); } #[test] -fn verify_batch_test_multiple() { +fn verify_batch_test_ext_multiple() { test([ - vec![vec![1, 1, 1, 2, 3], vec![9], vec![8]], - vec![vec![], vec![], vec![], vec![1]], - vec![vec![8], vec![7], vec![6]], + Case { row_lengths: vec![vec![1, 1, 1], vec![3], vec![2]], opened_element_size: 4 }, + Case { row_lengths: vec![vec![], vec![], vec![], vec![1]], opened_element_size: 4 }, + Case { row_lengths: vec![vec![4], vec![3], vec![2]], opened_element_size: 4 }, ]) } + +#[test] +fn verify_batch_test_felt_and_ext() { + test([ + Case { row_lengths: vec![vec![3], vec![], vec![9, 2, 1, 13, 4], vec![16]], opened_element_size: 1 }, + Case { row_lengths: vec![vec![1, 1, 1], vec![3], vec![2]], opened_element_size: 4 }, + Case { row_lengths: vec![vec![8], vec![7], vec![6]], opened_element_size: 1 }, + Case { row_lengths: vec![vec![], vec![], vec![], vec![1]], opened_element_size: 4 }, + Case { row_lengths: vec![vec![4], vec![3], vec![2]], opened_element_size: 4 }, + ]) +} \ No newline at end of file diff --git a/extensions/native/compiler/src/asm/compiler.rs b/extensions/native/compiler/src/asm/compiler.rs index 99c0b5a413..cfaebfe78c 100644 --- a/extensions/native/compiler/src/asm/compiler.rs +++ b/extensions/native/compiler/src/asm/compiler.rs @@ -594,6 +594,19 @@ impl + TwoAdicField> AsmCo debug_info, ); } + DslIr::VerifyBatchExt(dim, opened, sibling, index, commit) => { + self.push( + AsmInstruction::VerifyBatchExt( + dim.ptr().fp(), + opened.ptr().fp(), + opened.len().get_var().fp(), + sibling.ptr().fp(), + index.ptr().fp(), + commit.ptr().fp(), + ), + debug_info, + ); + } _ => unimplemented!(), } } diff --git a/extensions/native/compiler/src/asm/instruction.rs b/extensions/native/compiler/src/asm/instruction.rs index e6c4a22b98..e8d4ba8bd4 100644 --- a/extensions/native/compiler/src/asm/instruction.rs +++ b/extensions/native/compiler/src/asm/instruction.rs @@ -118,6 +118,10 @@ pub enum AsmInstruction { /// opened values are field elements VerifyBatchFelt(i32, i32, i32, i32, i32, i32), + /// (dim, opened, opened_length, sibling, index, commit) + /// opened values are extension field elements + VerifyBatchExt(i32, i32, i32, i32, i32, i32), + /// Print a variable. PrintV(i32), @@ -354,7 +358,14 @@ impl> AsmInstruction { AsmInstruction::VerifyBatchFelt(dim, opened, opened_length, sibling, index, commit) => { write!( f, - "verify_batch ({})fp, ({})fp, ({})fp, ({})fp, ({})fp, ({})fp", + "verify_batch_felt ({})fp, ({})fp, ({})fp, ({})fp, ({})fp, ({})fp", + dim, opened, opened_length, sibling, index, commit + ) + } + AsmInstruction::VerifyBatchExt(dim, opened, opened_length, sibling, index, commit) => { + write!( + f, + "verify_batch_ext ({})fp, ({})fp, ({})fp, ({})fp, ({})fp, ({})fp", dim, opened, opened_length, sibling, index, commit ) } diff --git a/extensions/native/compiler/src/conversion/mod.rs b/extensions/native/compiler/src/conversion/mod.rs index 58c24df720..590cbdfa8a 100644 --- a/extensions/native/compiler/src/conversion/mod.rs +++ b/extensions/native/compiler/src/conversion/mod.rs @@ -659,6 +659,16 @@ fn convert_instruction>( f: i32_f(commit), g: F::ONE, }], + AsmInstruction::VerifyBatchExt(dim, opened, opened_length, sibling, index, commit) => vec![Instruction { + opcode: options.opcode_with_offset(VerifyBatchOpcode::VERIFY_BATCH), + a: i32_f(dim), + b: i32_f(opened), + c: i32_f(opened_length), + d: i32_f(sibling), + e: i32_f(index), + f: i32_f(commit), + g: F::from_canonical_usize(4).inverse(), + }], }; let debug_infos = vec![debug_info; instructions.len()]; diff --git a/extensions/native/compiler/src/ir/instructions.rs b/extensions/native/compiler/src/ir/instructions.rs index 5cddd0bf8b..73213be473 100644 --- a/extensions/native/compiler/src/ir/instructions.rs +++ b/extensions/native/compiler/src/ir/instructions.rs @@ -286,6 +286,15 @@ pub enum DslIr { Array>, Array>, ), + /// VerifyBatch(dim, opened, sibling, index, commit) + /// opened values are Exts + VerifyBatchExt( + Array>, + Array>>, + Array>>, + Array>, + Array>, + ), // Debugging instructions. /// Executes less than (var = var < var). This operation is NOT constrained. diff --git a/extensions/native/compiler/src/ir/verify_batch.rs b/extensions/native/compiler/src/ir/verify_batch.rs index 2254bbceab..c8df8c156b 100644 --- a/extensions/native/compiler/src/ir/verify_batch.rs +++ b/extensions/native/compiler/src/ir/verify_batch.rs @@ -1,7 +1,7 @@ -use crate::ir::{Array, Builder, Config, DslIr, Felt, Usize, Var}; +use crate::ir::{Array, Builder, Config, DslIr, Ext, Felt, Usize, Var}; impl Builder { - pub fn verify_batch( + pub fn verify_batch_felt( &mut self, dimensions: &Array>, opened_values: &Array>>, @@ -17,4 +17,20 @@ impl Builder { commit.clone(), )); } + pub fn verify_batch_ext( + &mut self, + dimensions: &Array>, + opened_values: &Array>>, + proof: &Array>>, + index_bits: &Array>, + commit: &Array>, + ) { + self.push(DslIr::VerifyBatchExt( + dimensions.clone(), + opened_values.clone(), + proof.clone(), + index_bits.clone(), + commit.clone(), + )); + } } diff --git a/extensions/native/recursion/src/fri/mod.rs b/extensions/native/recursion/src/fri/mod.rs index 966da42043..fbb0277482 100644 --- a/extensions/native/recursion/src/fri/mod.rs +++ b/extensions/native/recursion/src/fri/mod.rs @@ -101,14 +101,14 @@ where let opened_values = builder.array(1); builder.set_value(&opened_values, 0, evals); builder.cycle_tracker_start("verify-batch-ext"); - /*verify_batch::( + verify_batch::( builder, &commit, dims_slice, index_pair, &NestedOpenedValues::Ext(opened_values), &step.opening_proof, - );*/ + ); builder.cycle_tracker_end("verify-batch-ext"); let two_adic_generator_one = config.get_two_adic_generator(builder, Usize::from(1)); @@ -167,10 +167,6 @@ pub fn verify_batch( Array::Dyn(ptr, len) => Array::Dyn(ptr, len.clone()), _ => panic!("Expected a dynamic array of felts"), }; - let opened_values = match opened_values { - NestedOpenedValues::Felt(arr) => arr, - _ => panic!("Expected a dynamic array of felts"), - }; let proof = match proof { Array::Dyn(ptr, len) => Array::Dyn(*ptr, len.clone()), _ => panic!("Expected a dynamic array of felts"), @@ -179,7 +175,11 @@ pub fn verify_batch( DigestVariable::Felt(arr) => arr, _ => panic!("Expected a dynamic array of felts"), }; - builder.verify_batch(&dimensions, opened_values, &proof, &index_bits, commit); + match opened_values { + NestedOpenedValues::Felt(opened_values) => builder.verify_batch_felt(&dimensions, opened_values, &proof, &index_bits, commit), + NestedOpenedValues::Ext(opened_values) => builder.verify_batch_ext(&dimensions, opened_values, &proof, &index_bits, commit), + _ => panic!("Expected a dynamic array of felts"), + }; } /// [static version] Verifies a batch opening. From 823deea2baaacfbafc566ea538ac7e068f3fc5b3 Mon Sep 17 00:00:00 2001 From: TlatoaniHJ Date: Wed, 15 Jan 2025 16:39:41 -0500 Subject: [PATCH 33/50] Fix merge main --- extensions/native/circuit/Cargo.toml | 1 + extensions/native/circuit/src/extension.rs | 7 +- .../native/circuit/src/verify_batch/chip.rs | 46 ++++++-- .../circuit/src/verify_batch/columns.rs | 22 ++-- .../native/circuit/src/verify_batch/tests.rs | 101 +++++++++++++----- .../native/circuit/src/verify_batch/trace.rs | 2 +- extensions/native/recursion/src/fri/mod.rs | 9 +- 7 files changed, 135 insertions(+), 53 deletions(-) diff --git a/extensions/native/circuit/Cargo.toml b/extensions/native/circuit/Cargo.toml index dc736e82cd..1bb5a04676 100644 --- a/extensions/native/circuit/Cargo.toml +++ b/extensions/native/circuit/Cargo.toml @@ -19,6 +19,7 @@ openvm-instructions = { workspace = true } openvm-rv32im-circuit = { workspace = true } openvm-native-compiler = { workspace = true } + strum.workspace = true itertools.workspace = true tracing.workspace = true diff --git a/extensions/native/circuit/src/extension.rs b/extensions/native/circuit/src/extension.rs index f8d5f21add..e1996f7ce1 100644 --- a/extensions/native/circuit/src/extension.rs +++ b/extensions/native/circuit/src/extension.rs @@ -17,8 +17,8 @@ use openvm_instructions::{ }; use openvm_native_compiler::{ CastfOpcode, FieldArithmeticOpcode, FieldExtensionOpcode, FriOpcode, NativeBranchEqualOpcode, - NativeJalOpcode, NativeLoadStoreOpcode, NativePhantom, BLOCK_LOAD_STORE_OPCODES, - BLOCK_LOAD_STORE_SIZE, SINGLE_LOAD_STORE_OPCODES, VerifyBatchOpcode, + NativeJalOpcode, NativeLoadStoreOpcode, NativePhantom, VerifyBatchOpcode, + BLOCK_LOAD_STORE_OPCODES, BLOCK_LOAD_STORE_SIZE, SINGLE_LOAD_STORE_OPCODES, }; use openvm_poseidon2_air::Poseidon2Config; use openvm_rv32im_circuit::{ @@ -31,7 +31,8 @@ use strum::IntoEnumIterator; use crate::{ adapters::{convert_adapter::ConvertAdapterChip, *}, - chip::VerifyBatchChip, phantom::*, + chip::VerifyBatchChip, + phantom::*, *, }; diff --git a/extensions/native/circuit/src/verify_batch/chip.rs b/extensions/native/circuit/src/verify_batch/chip.rs index 90fcf848b5..b13440e000 100644 --- a/extensions/native/circuit/src/verify_batch/chip.rs +++ b/extensions/native/circuit/src/verify_batch/chip.rs @@ -10,14 +10,19 @@ use openvm_circuit::{ use openvm_instructions::{instruction::Instruction, program::DEFAULT_PC_STEP}; use openvm_native_compiler::VerifyBatchOpcode::VERIFY_BATCH; use openvm_poseidon2_air::{Poseidon2Config, Poseidon2SubAir, Poseidon2SubChip}; -use openvm_stark_backend::p3_field::{Field, PrimeField32}; +use openvm_stark_backend::{ + p3_field::{Field, PrimeField32}, + Stateful, +}; +use serde::{Deserialize, Serialize}; use crate::verify_batch::{ air::{VerifyBatchAir, VerifyBatchBus}, CHUNK, }; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(bound = "F: Field")] pub struct VerifyBatchRecord { pub from_state: ExecutionState, pub instruction: Instruction, @@ -47,7 +52,8 @@ impl VerifyBatchRecord { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(bound = "F: Field")] pub(super) struct TopLevelRecord { // must be present in first record pub incorporate_row: Option>, @@ -55,7 +61,8 @@ pub(super) struct TopLevelRecord { pub incorporate_sibling: Option>, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(bound = "F: Field")] pub(super) struct IncorporateSiblingRecord { pub read_sibling_array_start: RecordId, pub read_root_is_on_right: RecordId, @@ -64,7 +71,8 @@ pub(super) struct IncorporateSiblingRecord { pub p2_input: [F; 2 * CHUNK], } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(bound = "F: Field")] pub(super) struct IncorporateRowRecord { pub chunks: Vec>, pub initial_opened_index: usize, @@ -74,13 +82,14 @@ pub(super) struct IncorporateRowRecord { pub p2_input: [F; 2 * CHUNK], } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(bound = "F: Field")] pub(super) struct InsideRowRecord { pub cells: Vec, pub p2_input: [F; 2 * CHUNK], } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub(super) struct CellRecord { pub read: RecordId, pub opened_index: usize, @@ -97,6 +106,29 @@ pub struct VerifyBatchChip { pub(super) subchip: Poseidon2SubChip, } +impl Stateful> + for VerifyBatchChip +{ + fn load_state(&mut self, state: Vec) { + self.records = bitcode::deserialize(&state).unwrap(); + self.height = 0; + for record in self.records.iter() { + for top_level in record.top_level.iter() { + if let Some(incorporate_row) = &top_level.incorporate_row { + self.height += 1 + incorporate_row.chunks.len(); + } + if let Some(_) = &top_level.incorporate_sibling { + self.height += 1; + } + } + } + } + + fn store_state(&self) -> Vec { + bitcode::serialize(&self.records).unwrap() + } +} + impl VerifyBatchChip { pub fn new( execution_bus: ExecutionBus, diff --git a/extensions/native/circuit/src/verify_batch/columns.rs b/extensions/native/circuit/src/verify_batch/columns.rs index b8ad0345ba..789e01e841 100644 --- a/extensions/native/circuit/src/verify_batch/columns.rs +++ b/extensions/native/circuit/src/verify_batch/columns.rs @@ -53,19 +53,19 @@ pub struct VerifyBatchCols { pub index_base_pointer: T, // these can be optimized to be shared with cells[i].read_row_pointer_and_length (saves 18 cols) - pub dim_base_pointer_read: MemoryReadAuxCols, - pub opened_base_pointer_read: MemoryReadAuxCols, - pub opened_length_read: MemoryReadAuxCols, - pub sibling_base_pointer_read: MemoryReadAuxCols, - pub index_base_pointer_read: MemoryReadAuxCols, - pub commit_pointer_read: MemoryReadAuxCols, + pub dim_base_pointer_read: MemoryReadAuxCols, + pub opened_base_pointer_read: MemoryReadAuxCols, + pub opened_length_read: MemoryReadAuxCols, + pub sibling_base_pointer_read: MemoryReadAuxCols, + pub index_base_pointer_read: MemoryReadAuxCols, + pub commit_pointer_read: MemoryReadAuxCols, // this with the other ones (saves 1 col) pub proof_index: T, // these as well (saves 6 cols) - pub read_initial_height_or_root_is_on_right: MemoryReadAuxCols, - pub read_final_height_or_sibling_array_start: MemoryReadAuxCols, + pub read_initial_height_or_root_is_on_right: MemoryReadAuxCols, + pub read_final_height_or_sibling_array_start: MemoryReadAuxCols, // i guess these can be shared with like the other ones (saves 2 cols) pub root_is_on_right: T, @@ -74,15 +74,15 @@ pub struct VerifyBatchCols { // plus this (saves 1 col) pub commit_pointer: T, // this as well (saves 3 cols) - pub commit_read: MemoryReadAuxCols, + pub commit_read: MemoryReadAuxCols, } #[repr(C)] #[derive(AlignedBorrow, Copy, Clone)] pub struct VerifyBatchCellCols { - pub read: MemoryReadAuxCols, + pub read: MemoryReadAuxCols, pub opened_index: T, - pub read_row_pointer_and_length: MemoryReadAuxCols, + pub read_row_pointer_and_length: MemoryReadAuxCols, pub row_pointer: T, pub row_end: T, pub is_first_in_row: T, diff --git a/extensions/native/circuit/src/verify_batch/tests.rs b/extensions/native/circuit/src/verify_batch/tests.rs index dca8984889..92916515d4 100644 --- a/extensions/native/circuit/src/verify_batch/tests.rs +++ b/extensions/native/circuit/src/verify_batch/tests.rs @@ -7,11 +7,10 @@ use openvm_native_compiler::{VerifyBatchOpcode, VerifyBatchOpcode::VERIFY_BATCH} use openvm_poseidon2_air::{Poseidon2Config, Poseidon2SubChip}; use openvm_stark_backend::{ p3_air::BaseAir, - p3_field::{Field, FieldAlgebra}, + p3_field::{Field, FieldAlgebra, PrimeField32}, utils::disable_debug_builder, verifier::VerificationError, }; -use openvm_stark_backend::p3_field::PrimeField32; use openvm_stark_sdk::{p3_baby_bear::BabyBear, utils::create_seeded_rng}; use rand::{rngs::StdRng, Rng}; @@ -148,16 +147,21 @@ fn test(cases: [Case; N]) { ); let mut rng = create_seeded_rng(); - for Case { row_lengths, opened_element_size } in cases { - let instance = random_instance(&mut rng, row_lengths, opened_element_size,|left, right| { - let concatenated = - std::array::from_fn(|i| if i < CHUNK { left[i] } else { right[i - CHUNK] }); - let permuted = chip.subchip.permute(concatenated); - ( - std::array::from_fn(|i| permuted[i]), - std::array::from_fn(|i| permuted[i + CHUNK]), - ) - }); + for Case { + row_lengths, + opened_element_size, + } in cases + { + let instance = + random_instance(&mut rng, row_lengths, opened_element_size, |left, right| { + let concatenated = + std::array::from_fn(|i| if i < CHUNK { left[i] } else { right[i - CHUNK] }); + let permuted = chip.subchip.permute(concatenated); + ( + std::array::from_fn(|i| permuted[i]), + std::array::from_fn(|i| permuted[i + CHUNK]), + ) + }); let VerifyBatchInstance { dim, opened, @@ -214,7 +218,9 @@ fn test(cases: [Case; N]) { } tester.write(address_space, commit_pointer, commit); - let opened_element_size_inv = F::from_canonical_usize(opened_element_size).inverse().as_canonical_u32() as usize; + let opened_element_size_inv = F::from_canonical_usize(opened_element_size) + .inverse() + .as_canonical_u32() as usize; tester.execute( &mut chip, &Instruction::from_usize( @@ -226,7 +232,7 @@ fn test(cases: [Case; N]) { sibling_register, index_register, commit_register, - opened_element_size_inv + opened_element_size_inv, ], ), ); @@ -255,39 +261,78 @@ fn test(cases: [Case; N]) { #[test] fn verify_batch_test_felt() { - test([Case { row_lengths: vec![vec![3], vec![], vec![9, 2, 1, 13, 4], vec![16]], opened_element_size: 1 }]); + test([Case { + row_lengths: vec![vec![3], vec![], vec![9, 2, 1, 13, 4], vec![16]], + opened_element_size: 1, + }]); } #[test] fn verify_batch_test_felt_multiple() { test([ - Case { row_lengths: vec![vec![1, 1, 1, 2, 3], vec![9], vec![8]], opened_element_size: 1 }, - Case { row_lengths: vec![vec![], vec![], vec![], vec![1]], opened_element_size: 1 }, - Case { row_lengths: vec![vec![8], vec![7], vec![6]], opened_element_size: 1 }, + Case { + row_lengths: vec![vec![1, 1, 1, 2, 3], vec![9], vec![8]], + opened_element_size: 1, + }, + Case { + row_lengths: vec![vec![], vec![], vec![], vec![1]], + opened_element_size: 1, + }, + Case { + row_lengths: vec![vec![8], vec![7], vec![6]], + opened_element_size: 1, + }, ]) } #[test] fn verify_batch_test_ext() { - test([Case { row_lengths: vec![vec![3], vec![], vec![1, 2, 1], vec![4]], opened_element_size: 4 }]); + test([Case { + row_lengths: vec![vec![3], vec![], vec![1, 2, 1], vec![4]], + opened_element_size: 4, + }]); } #[test] fn verify_batch_test_ext_multiple() { test([ - Case { row_lengths: vec![vec![1, 1, 1], vec![3], vec![2]], opened_element_size: 4 }, - Case { row_lengths: vec![vec![], vec![], vec![], vec![1]], opened_element_size: 4 }, - Case { row_lengths: vec![vec![4], vec![3], vec![2]], opened_element_size: 4 }, + Case { + row_lengths: vec![vec![1, 1, 1], vec![3], vec![2]], + opened_element_size: 4, + }, + Case { + row_lengths: vec![vec![], vec![], vec![], vec![1]], + opened_element_size: 4, + }, + Case { + row_lengths: vec![vec![4], vec![3], vec![2]], + opened_element_size: 4, + }, ]) } #[test] fn verify_batch_test_felt_and_ext() { test([ - Case { row_lengths: vec![vec![3], vec![], vec![9, 2, 1, 13, 4], vec![16]], opened_element_size: 1 }, - Case { row_lengths: vec![vec![1, 1, 1], vec![3], vec![2]], opened_element_size: 4 }, - Case { row_lengths: vec![vec![8], vec![7], vec![6]], opened_element_size: 1 }, - Case { row_lengths: vec![vec![], vec![], vec![], vec![1]], opened_element_size: 4 }, - Case { row_lengths: vec![vec![4], vec![3], vec![2]], opened_element_size: 4 }, + Case { + row_lengths: vec![vec![3], vec![], vec![9, 2, 1, 13, 4], vec![16]], + opened_element_size: 1, + }, + Case { + row_lengths: vec![vec![1, 1, 1], vec![3], vec![2]], + opened_element_size: 4, + }, + Case { + row_lengths: vec![vec![8], vec![7], vec![6]], + opened_element_size: 1, + }, + Case { + row_lengths: vec![vec![], vec![], vec![], vec![1]], + opened_element_size: 4, + }, + Case { + row_lengths: vec![vec![4], vec![3], vec![2]], + opened_element_size: 4, + }, ]) -} \ No newline at end of file +} diff --git a/extensions/native/circuit/src/verify_batch/trace.rs b/extensions/native/circuit/src/verify_batch/trace.rs index 13c16fdd93..46b3af1bbb 100644 --- a/extensions/native/circuit/src/verify_batch/trace.rs +++ b/extensions/native/circuit/src/verify_batch/trace.rs @@ -8,11 +8,11 @@ use openvm_stark_backend::{ p3_air::BaseAir, p3_field::{Field, PrimeField32}, p3_matrix::dense::RowMajorMatrix, + p3_maybe_rayon::prelude::*, prover::types::AirProofInput, rap::AnyRap, Chip, ChipUsageGetter, }; -use rayon::{iter::ParallelIterator, slice::ParallelSliceMut}; use crate::{ chip::NUM_INITIAL_READS, diff --git a/extensions/native/recursion/src/fri/mod.rs b/extensions/native/recursion/src/fri/mod.rs index a252ea591b..a77cfbaaf3 100644 --- a/extensions/native/recursion/src/fri/mod.rs +++ b/extensions/native/recursion/src/fri/mod.rs @@ -178,9 +178,12 @@ pub fn verify_batch( _ => panic!("Expected a dynamic array of felts"), }; match opened_values { - NestedOpenedValues::Felt(opened_values) => builder.verify_batch_felt(&dimensions, opened_values, &proof, &index_bits, commit), - NestedOpenedValues::Ext(opened_values) => builder.verify_batch_ext(&dimensions, opened_values, &proof, &index_bits, commit), - _ => panic!("Expected a dynamic array of felts"), + NestedOpenedValues::Felt(opened_values) => { + builder.verify_batch_felt(&dimensions, opened_values, &proof, &index_bits, commit) + } + NestedOpenedValues::Ext(opened_values) => { + builder.verify_batch_ext(&dimensions, opened_values, &proof, &index_bits, commit) + } }; } From 2e0db3e65a9b31b39ad486053d6c2e6bd033b2ee Mon Sep 17 00:00:00 2001 From: TlatoaniHJ Date: Wed, 15 Jan 2025 16:49:44 -0500 Subject: [PATCH 34/50] Fix magical test --- crates/vm/tests/integration_test.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/vm/tests/integration_test.rs b/crates/vm/tests/integration_test.rs index ad77b22786..13add4526f 100644 --- a/crates/vm/tests/integration_test.rs +++ b/crates/vm/tests/integration_test.rs @@ -191,6 +191,7 @@ fn test_vm_override_executor_height() { (ChipId::Executor(7), 0), (ChipId::Executor(8), 0), (ChipId::Executor(9), 0), + (ChipId::Executor(10), 0), ] .into_iter() .collect(), @@ -216,6 +217,7 @@ fn test_vm_override_executor_height() { (ChipId::Executor(7), 2048), (ChipId::Executor(8), 4096), (ChipId::Executor(9), 8192), + (ChipId::Executor(10), 16384), ] .into_iter() .collect(), @@ -240,7 +242,7 @@ fn test_vm_override_executor_height() { // heights by hands whenever something changes. assert_eq!( air_heights, - vec![2, 2, 16, 1, 8, 4, 2, 8192, 4096, 2048, 1024, 512, 256, 128, 64, 32, 262144] + vec![2, 2, 16, 1, 8, 4, 2, 16384, 8192, 4096, 2048, 1024, 512, 256, 128, 64, 32, 262144] ); } From d924444903ac20f935c27f519d3ad5094e9bc5da Mon Sep 17 00:00:00 2001 From: TlatoaniHJ Date: Wed, 15 Jan 2025 17:27:05 -0500 Subject: [PATCH 35/50] Fix behavior for rolling_hash not multiple of 8 --- .../native/circuit/src/verify_batch/air.rs | 16 +++++++++++++--- .../native/circuit/src/verify_batch/chip.rs | 14 ++++++-------- .../native/circuit/src/verify_batch/columns.rs | 1 + .../native/circuit/src/verify_batch/tests.rs | 12 +++++------- extensions/native/recursion/src/fri/mod.rs | 4 ++-- 5 files changed, 27 insertions(+), 20 deletions(-) diff --git a/extensions/native/circuit/src/verify_batch/air.rs b/extensions/native/circuit/src/verify_batch/air.rs index 71ee491ed4..790e74eb7c 100644 --- a/extensions/native/circuit/src/verify_batch/air.rs +++ b/extensions/native/circuit/src/verify_batch/air.rs @@ -196,6 +196,19 @@ impl Air // left input + // handle exhausted cells on next row + + for i in 0..CHUNK { + builder + .when(inside_row - end_inside_row) + .when(next.cells[i].is_exhausted) + .assert_eq(next_left_input[i], left_output[i]); + builder + .when(end.clone()) + .when(next.cells[i].is_exhausted) + .assert_zero(next_left_input[i]); + } + for i in 0..CHUNK { let cell = cells[i]; let next_cell = if i + 1 == CHUNK { @@ -217,9 +230,6 @@ impl Air &cell.read, ) .eval(builder, inside_row * not(cell.is_exhausted)); - builder - .when(cell.is_exhausted) - .assert_eq(left_input[i], AB::F::ZERO); let mut when_inside_row_not_last = if i == CHUNK - 1 { builder.when(inside_row - end_inside_row) diff --git a/extensions/native/circuit/src/verify_batch/chip.rs b/extensions/native/circuit/src/verify_batch/chip.rs index b13440e000..2d6588edcf 100644 --- a/extensions/native/circuit/src/verify_batch/chip.rs +++ b/extensions/native/circuit/src/verify_batch/chip.rs @@ -54,7 +54,7 @@ impl VerifyBatchRecord { #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(bound = "F: Field")] -pub(super) struct TopLevelRecord { +pub struct TopLevelRecord { // must be present in first record pub incorporate_row: Option>, // must be present in all bust last record @@ -63,7 +63,7 @@ pub(super) struct TopLevelRecord { #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(bound = "F: Field")] -pub(super) struct IncorporateSiblingRecord { +pub struct IncorporateSiblingRecord { pub read_sibling_array_start: RecordId, pub read_root_is_on_right: RecordId, pub root_is_on_right: bool, @@ -73,7 +73,7 @@ pub(super) struct IncorporateSiblingRecord { #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(bound = "F: Field")] -pub(super) struct IncorporateRowRecord { +pub struct IncorporateRowRecord { pub chunks: Vec>, pub initial_opened_index: usize, pub final_opened_index: usize, @@ -84,13 +84,13 @@ pub(super) struct IncorporateRowRecord { #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(bound = "F: Field")] -pub(super) struct InsideRowRecord { +pub struct InsideRowRecord { pub cells: Vec, pub p2_input: [F; 2 * CHUNK], } #[derive(Debug, Clone, Serialize, Deserialize)] -pub(super) struct CellRecord { +pub struct CellRecord { pub read: RecordId, pub opened_index: usize, pub read_row_pointer_and_length: Option, @@ -240,7 +240,6 @@ impl InstructionExecutor loop { let mut cells = vec![]; - let mut chunk = [F::ZERO; CHUNK]; for i in 0..CHUNK { let read_row_pointer_and_length = if is_first_in_segment || row_pointer == row_end @@ -279,14 +278,13 @@ impl InstructionExecutor row_pointer, row_end, }); - chunk[i] = value; + rolling_hash[i] = value; row_pointer += 1; } if cells.is_empty() { break; } let cells_len = cells.len(); - rolling_hash[..CHUNK].copy_from_slice(&chunk[..CHUNK]); chunks.push(InsideRowRecord { cells, p2_input: rolling_hash, diff --git a/extensions/native/circuit/src/verify_batch/columns.rs b/extensions/native/circuit/src/verify_batch/columns.rs index 789e01e841..0084c07136 100644 --- a/extensions/native/circuit/src/verify_batch/columns.rs +++ b/extensions/native/circuit/src/verify_batch/columns.rs @@ -85,6 +85,7 @@ pub struct VerifyBatchCellCols { pub read_row_pointer_and_length: MemoryReadAuxCols, pub row_pointer: T, pub row_end: T, + // flags cannot be shared pub is_first_in_row: T, pub is_exhausted: T, } diff --git a/extensions/native/circuit/src/verify_batch/tests.rs b/extensions/native/circuit/src/verify_batch/tests.rs index 92916515d4..a829482424 100644 --- a/extensions/native/circuit/src/verify_batch/tests.rs +++ b/extensions/native/circuit/src/verify_batch/tests.rs @@ -1,3 +1,5 @@ +use std::cmp::min; + use openvm_circuit::{ arch::testing::{memory::gen_pointer, VmChipTestBuilder}, system::memory::CHUNK, @@ -33,17 +35,13 @@ fn compute_commit( concat.extend(opened[opened_index].clone()); opened_index += 1; } - while concat.len() % CHUNK != 0 { - concat.push(F::ZERO); - } if !concat.is_empty() { let mut left = [F::ZERO; CHUNK]; let mut right = [F::ZERO; CHUNK]; for i in (0..concat.len()).step_by(CHUNK) { - let chunk = std::array::from_fn(|j| concat[i + j]); - let (new_left, new_right) = hash_function(chunk, right); - left = new_left; - right = new_right; + left[..(min(i + CHUNK, concat.len()) - i)] + .copy_from_slice(&concat[i..min(i + CHUNK, concat.len())]); + (left, right) = hash_function(left, right); } root = if height == dim[0] { left diff --git a/extensions/native/recursion/src/fri/mod.rs b/extensions/native/recursion/src/fri/mod.rs index a77cfbaaf3..d5d7abd604 100644 --- a/extensions/native/recursion/src/fri/mod.rs +++ b/extensions/native/recursion/src/fri/mod.rs @@ -1,8 +1,8 @@ pub use domain::*; use openvm_native_compiler::{ ir::{ - Array, ArrayLike, Builder, Config, Ext, ExtensionOperand, Felt, Ptr, RVar, SymbolicVar, - Usize, Var, DIGEST_SIZE, + Array, ArrayLike, Builder, Config, Ext, ExtensionOperand, Felt, RVar, SymbolicVar, Usize, + Var, }, prelude::MemVariable, }; From 6fb7d1524f38cc30b7363748d0b6a8c6d9a9cc10 Mon Sep 17 00:00:00 2001 From: TlatoaniHJ Date: Wed, 15 Jan 2025 19:02:25 -0500 Subject: [PATCH 36/50] Optimize specific columns --- .../native/circuit/src/verify_batch/air.rs | 150 +++++++++++------- .../circuit/src/verify_batch/columns.rs | 59 ++++--- .../native/circuit/src/verify_batch/trace.rs | 105 ++++++------ 3 files changed, 189 insertions(+), 125 deletions(-) diff --git a/extensions/native/circuit/src/verify_batch/air.rs b/extensions/native/circuit/src/verify_batch/air.rs index 790e74eb7c..b1972a860b 100644 --- a/extensions/native/circuit/src/verify_batch/air.rs +++ b/extensions/native/circuit/src/verify_batch/air.rs @@ -18,7 +18,10 @@ use openvm_stark_backend::{ use crate::{ chip::NUM_INITIAL_READS, - verify_batch::{columns::VerifyBatchCols, CHUNK}, + verify_batch::{ + columns::{InsideRowSpecificCols, TopLevelSpecificCols, VerifyBatchCols}, + CHUNK, + }, }; #[derive(Clone, Debug)] @@ -58,46 +61,20 @@ impl Air let next: &VerifyBatchCols = (*next).borrow(); let &VerifyBatchCols { + inner: _, incorporate_row, incorporate_sibling, inside_row, end_inside_row, end_top_level, start_top_level, - pc, very_first_timestamp, start_timestamp, - end_timestamp, - dim_register, - opened_register, - opened_length_register, - sibling_register, - index_register, - commit_register, opened_element_size_inv, - cells, initial_opened_index, - final_opened_index, - height, - opened_length, - dim_base_pointer, opened_base_pointer, - sibling_base_pointer, - index_base_pointer, - read_initial_height_or_root_is_on_right, - read_final_height_or_sibling_array_start, - dim_base_pointer_read, - opened_base_pointer_read, - opened_length_read, - sibling_base_pointer_read, - index_base_pointer_read, - commit_pointer_read, - root_is_on_right, - commit_pointer, - commit_read, - proof_index, - sibling_array_start, - .. + is_exhausted, + specific, } = local; let left_input = std::array::from_fn::<_, CHUNK, _>(|i| local.inner.inputs[i]); @@ -136,11 +113,18 @@ impl Air //// inside row constraints + let inside_row_specific: &InsideRowSpecificCols = + specific[..InsideRowSpecificCols::::width()].borrow(); + let cells = inside_row_specific.cells; + let next_inside_row_specific: &InsideRowSpecificCols = + next.specific[..InsideRowSpecificCols::::width()].borrow(); + let next_cells = next_inside_row_specific.cells; + // start builder .when(end.clone()) .when(next.inside_row) - .assert_eq(next.initial_opened_index, next.cells[0].opened_index); + .assert_eq(next.initial_opened_index, next_cells[0].opened_index); builder .when(end.clone()) .when(next.inside_row) @@ -201,27 +185,35 @@ impl Air for i in 0..CHUNK { builder .when(inside_row - end_inside_row) - .when(next.cells[i].is_exhausted) + .when(next.is_exhausted[i]) .assert_eq(next_left_input[i], left_output[i]); builder .when(end.clone()) - .when(next.cells[i].is_exhausted) + .when(next.is_exhausted[i]) .assert_zero(next_left_input[i]); } for i in 0..CHUNK { let cell = cells[i]; let next_cell = if i + 1 == CHUNK { - next.cells[0] + next_cells[0] } else { cells[i + 1] }; + let next_is_exhausted = if i + 1 == CHUNK { + next.is_exhausted[0] + } else { + is_exhausted[i + 1] + }; + let is_exhausted = is_exhausted[i]; - builder.assert_bool(cell.is_first_in_row); - builder.assert_bool(cell.is_exhausted); - builder.assert_bool(cell.is_first_in_row + cell.is_exhausted); + builder.when(inside_row).assert_bool(cell.is_first_in_row); + builder.assert_bool(is_exhausted); + builder + .when(inside_row) + .assert_bool(cell.is_first_in_row + is_exhausted); - let next_is_normal = AB::Expr::ONE - next_cell.is_first_in_row - next_cell.is_exhausted; + let next_is_normal = AB::Expr::ONE - next_cell.is_first_in_row - next_is_exhausted; self.memory_bridge .read( MemoryAddress::new(self.address_space, cell.row_pointer), @@ -229,13 +221,14 @@ impl Air start_timestamp + AB::F::from_canonical_usize((2 * i) + 1), &cell.read, ) - .eval(builder, inside_row * not(cell.is_exhausted)); + .eval(builder, inside_row * not(is_exhausted)); let mut when_inside_row_not_last = if i == CHUNK - 1 { builder.when(inside_row - end_inside_row) } else { builder.when(inside_row) }; + // everthing above oks // update state for normal cell when_inside_row_not_last @@ -273,18 +266,18 @@ impl Air .assert_eq(next_cell.opened_index, cell.opened_index + AB::F::ONE); when_inside_row_not_last - .when(next_cell.is_exhausted) + .when(next_is_exhausted) .assert_eq(next_cell.opened_index, cell.opened_index); when_inside_row_not_last - .when(cell.is_exhausted) - .assert_eq(next_cell.is_exhausted, AB::F::ONE); + .when(is_exhausted) + .assert_eq(next_is_exhausted, AB::F::ONE); let is_last_in_row = if i == CHUNK - 1 { end_inside_row.into() } else { - next_cell.is_first_in_row + next_cell.is_exhausted - } - cell.is_exhausted; + next_cell.is_first_in_row + next_is_exhausted + } - is_exhausted; builder .when(inside_row) .when(is_last_in_row) @@ -293,10 +286,45 @@ impl Air //// top level constraints + let top_level_specific: &TopLevelSpecificCols = + specific[..TopLevelSpecificCols::::width()].borrow(); + let &TopLevelSpecificCols { + pc, + end_timestamp, + dim_register, + opened_register, + opened_length_register, + sibling_register, + index_register, + commit_register, + final_opened_index, + height, + opened_length, + dim_base_pointer, + sibling_base_pointer, + index_base_pointer, + dim_base_pointer_read, + opened_base_pointer_read, + opened_length_read, + sibling_base_pointer_read, + index_base_pointer_read, + commit_pointer_read, + proof_index, + read_initial_height_or_root_is_on_right, + read_final_height_or_sibling_array_start, + root_is_on_right, + sibling_array_start, + reads, + commit_pointer, + commit_read, + } = top_level_specific; + let next_top_level_specific: &TopLevelSpecificCols = + next.specific[..TopLevelSpecificCols::::width()].borrow(); + builder .when(end.clone()) .when(next.incorporate_row + next.incorporate_sibling) - .assert_eq(next.proof_index, AB::F::ZERO); + .assert_eq(next_top_level_specific.proof_index, AB::F::ZERO); let timestamp_after_initial_reads = start_timestamp + AB::F::from_canonical_usize(NUM_INITIAL_READS); @@ -383,13 +411,20 @@ impl Air let mut when_top_level_not_end = builder.when(incorporate_row + incorporate_sibling - end_top_level); - when_top_level_not_end.assert_eq(next.dim_base_pointer, dim_base_pointer); + when_top_level_not_end + .assert_eq(next_top_level_specific.dim_base_pointer, dim_base_pointer); when_top_level_not_end.assert_eq(next.opened_base_pointer, opened_base_pointer); - when_top_level_not_end.assert_eq(next.sibling_base_pointer, sibling_base_pointer); - when_top_level_not_end.assert_eq(next.index_base_pointer, index_base_pointer); + when_top_level_not_end.assert_eq( + next_top_level_specific.sibling_base_pointer, + sibling_base_pointer, + ); + when_top_level_not_end.assert_eq( + next_top_level_specific.index_base_pointer, + index_base_pointer, + ); when_top_level_not_end.assert_eq(next.start_timestamp, end_timestamp); - when_top_level_not_end.assert_eq(next.opened_length, opened_length); + when_top_level_not_end.assert_eq(next_top_level_specific.opened_length, opened_length); when_top_level_not_end.assert_eq(next.opened_element_size_inv, opened_element_size_inv); when_top_level_not_end .assert_eq(next.initial_opened_index, final_opened_index + AB::F::ONE); @@ -397,19 +432,22 @@ impl Air builder .when(incorporate_sibling) .when(AB::Expr::ONE - end_top_level) - .assert_eq(next.height * AB::F::TWO, height); + .assert_eq(next_top_level_specific.height * AB::F::TWO, height); builder .when(incorporate_row) .when(AB::Expr::ONE - end_top_level) - .assert_eq(next.height, height); + .assert_eq(next_top_level_specific.height, height); builder .when(incorporate_sibling) .when(AB::Expr::ONE - end_top_level) - .assert_eq(next.proof_index, proof_index + AB::F::ONE); + .assert_eq( + next_top_level_specific.proof_index, + proof_index + AB::F::ONE, + ); builder .when(incorporate_row) .when(AB::Expr::ONE - end_top_level) - .assert_eq(next.proof_index, proof_index); + .assert_eq(next_top_level_specific.proof_index, proof_index); builder .when(end_top_level) @@ -512,11 +550,11 @@ impl Air for i in 0..CHUNK { builder .when(next.incorporate_sibling) - .when(next.root_is_on_right) + .when(next_top_level_specific.root_is_on_right) .assert_eq(next_right_input[i], left_output[i]); builder .when(next.incorporate_sibling) - .when(AB::Expr::ONE - next.root_is_on_right) + .when(AB::Expr::ONE - next_top_level_specific.root_is_on_right) .assert_eq(next_left_input[i], left_output[i]); self.memory_bridge @@ -528,7 +566,7 @@ impl Air [(root_is_on_right * left_input[i]) + ((AB::Expr::ONE - root_is_on_right) * right_input[i])], timestamp_after_initial_reads.clone() + AB::F::from_canonical_usize(2 + i), - &cells[i].read, + &reads[i], ) .eval(builder, incorporate_sibling); } diff --git a/extensions/native/circuit/src/verify_batch/columns.rs b/extensions/native/circuit/src/verify_batch/columns.rs index 0084c07136..d0d0b58c47 100644 --- a/extensions/native/circuit/src/verify_batch/columns.rs +++ b/extensions/native/circuit/src/verify_batch/columns.rs @@ -19,40 +19,52 @@ pub struct VerifyBatchCols { pub end_top_level: T, pub start_top_level: T, - // execution state (pc can be shared (saves 1 col), very_first_timestamp is used in a different way inside row) - pub pc: T, pub very_first_timestamp: T, - pub start_timestamp: T, - pub end_timestamp: T, // only used for top level (so can be shared (saves 1 col) - // instruction (a, b, c, d, e, f, g) - // all can be shared other than g (saves 6 cols) + // instruction (g) + pub opened_element_size_inv: T, + + // initial/final opened index for a subsegment with same height + pub initial_opened_index: T, + + pub opened_base_pointer: T, + + // cannot be shared, should be 0 on rows that are not inside row + pub is_exhausted: [T; CHUNK], + + pub specific: [T; max( + TopLevelSpecificCols::::width(), + InsideRowSpecificCols::::width(), + )], +} + +const fn max(a: usize, b: usize) -> usize { + [a, b][(a < b) as usize] +} +#[repr(C)] +#[derive(AlignedBorrow)] +pub struct TopLevelSpecificCols { + pub pc: T, + pub end_timestamp: T, + + // instruction (a, b, c, d, e, f) pub dim_register: T, pub opened_register: T, pub opened_length_register: T, pub sibling_register: T, pub index_register: T, pub commit_register: T, - pub opened_element_size_inv: T, - pub cells: [VerifyBatchCellCols; CHUNK], - // initial/final opened index for a subsegment with same height - // initial is used in both, final is used only in top level - pub initial_opened_index: T, - // so then this one can be shared as well (saves 1 col) pub final_opened_index: T, - // these two ig? (saves 2 cols) pub height: T, pub opened_length: T, pub dim_base_pointer: T, - pub opened_base_pointer: T, pub sibling_base_pointer: T, pub index_base_pointer: T, - // these can be optimized to be shared with cells[i].read_row_pointer_and_length (saves 18 cols) pub dim_base_pointer_read: MemoryReadAuxCols, pub opened_base_pointer_read: MemoryReadAuxCols, pub opened_length_read: MemoryReadAuxCols, @@ -60,32 +72,33 @@ pub struct VerifyBatchCols { pub index_base_pointer_read: MemoryReadAuxCols, pub commit_pointer_read: MemoryReadAuxCols, - // this with the other ones (saves 1 col) pub proof_index: T, - // these as well (saves 6 cols) pub read_initial_height_or_root_is_on_right: MemoryReadAuxCols, pub read_final_height_or_sibling_array_start: MemoryReadAuxCols, - // i guess these can be shared with like the other ones (saves 2 cols) + // incorporate sibling only pub root_is_on_right: T, pub sibling_array_start: T, + pub reads: [MemoryReadAuxCols; CHUNK], - // plus this (saves 1 col) pub commit_pointer: T, - // this as well (saves 3 cols) pub commit_read: MemoryReadAuxCols, } +#[repr(C)] +#[derive(AlignedBorrow)] +pub struct InsideRowSpecificCols { + pub cells: [VerifyBatchCellCols; CHUNK], +} + #[repr(C)] #[derive(AlignedBorrow, Copy, Clone)] -pub struct VerifyBatchCellCols { +pub struct VerifyBatchCellCols { pub read: MemoryReadAuxCols, pub opened_index: T, pub read_row_pointer_and_length: MemoryReadAuxCols, pub row_pointer: T, pub row_end: T, - // flags cannot be shared pub is_first_in_row: T, - pub is_exhausted: T, } diff --git a/extensions/native/circuit/src/verify_batch/trace.rs b/extensions/native/circuit/src/verify_batch/trace.rs index 46b3af1bbb..8984428b4a 100644 --- a/extensions/native/circuit/src/verify_batch/trace.rs +++ b/extensions/native/circuit/src/verify_batch/trace.rs @@ -1,6 +1,5 @@ use std::{borrow::BorrowMut, sync::Arc}; -use itertools::Itertools; use openvm_circuit::system::memory::{MemoryAuxColsFactory, OfflineMemory}; use openvm_circuit_primitives::utils::next_power_of_two_or_zero; use openvm_stark_backend::{ @@ -21,7 +20,7 @@ use crate::{ CellRecord, IncorporateRowRecord, IncorporateSiblingRecord, InsideRowRecord, VerifyBatchChip, VerifyBatchRecord, }, - columns::VerifyBatchCols, + columns::{InsideRowSpecificCols, TopLevelSpecificCols, VerifyBatchCols}, CHUNK, }, }; @@ -80,27 +79,30 @@ impl VerifyBatchChip = + cols.specific[..TopLevelSpecificCols::::width()].borrow_mut(); + + specific.end_timestamp = F::from_canonical_usize(read_root_is_on_right.timestamp as usize + (2 + CHUNK)); - for (read, cell) in reads.into_iter().zip_eq(cols.cells.iter_mut()) { - cell.read = aux_cols_factory.make_read_aux_cols(memory.record_by_id(read)); - } + specific.reads = + reads.map(|read| aux_cols_factory.make_read_aux_cols(memory.record_by_id(read))); cols.initial_opened_index = F::from_canonical_usize(opened_index); - cols.final_opened_index = F::from_canonical_usize(opened_index - 1); - cols.height = F::from_canonical_usize(height); - cols.opened_length = F::from_canonical_usize(parent.opened_length); - cols.dim_base_pointer = parent.dim_base_pointer; + specific.final_opened_index = F::from_canonical_usize(opened_index - 1); + specific.height = F::from_canonical_usize(height); + specific.opened_length = F::from_canonical_usize(parent.opened_length); + specific.dim_base_pointer = parent.dim_base_pointer; cols.opened_base_pointer = parent.opened_base_pointer; - cols.sibling_base_pointer = parent.sibling_base_pointer; - cols.index_base_pointer = parent.index_base_pointer; + specific.sibling_base_pointer = parent.sibling_base_pointer; + specific.index_base_pointer = parent.index_base_pointer; - cols.proof_index = F::from_canonical_usize(proof_index); - cols.read_initial_height_or_root_is_on_right = + specific.proof_index = F::from_canonical_usize(proof_index); + specific.read_initial_height_or_root_is_on_right = aux_cols_factory.make_read_aux_cols(read_root_is_on_right); - cols.read_final_height_or_sibling_array_start = + specific.read_final_height_or_sibling_array_start = aux_cols_factory.make_read_aux_cols(read_sibling_array_start); - cols.root_is_on_right = F::from_bool(root_is_on_right); - cols.sibling_array_start = read_sibling_array_start.data[0]; + specific.root_is_on_right = F::from_bool(root_is_on_right); + specific.sibling_array_start = read_sibling_array_start.data[0]; } fn correct_last_top_level_row( &self, @@ -124,27 +126,32 @@ impl VerifyBatchChip = slice.borrow_mut(); cols.end_top_level = F::ONE; - cols.pc = F::from_canonical_u32(from_state.pc); - cols.dim_register = instruction.a; - cols.opened_register = instruction.b; - cols.opened_length_register = instruction.c; - cols.sibling_register = instruction.d; - cols.index_register = instruction.e; - cols.commit_register = instruction.f; - cols.commit_pointer = commit_pointer; - cols.dim_base_pointer_read = + + let specific: &mut TopLevelSpecificCols = + cols.specific[..TopLevelSpecificCols::::width()].borrow_mut(); + + specific.pc = F::from_canonical_u32(from_state.pc); + specific.dim_register = instruction.a; + specific.opened_register = instruction.b; + specific.opened_length_register = instruction.c; + specific.sibling_register = instruction.d; + specific.index_register = instruction.e; + specific.commit_register = instruction.f; + specific.commit_pointer = commit_pointer; + specific.dim_base_pointer_read = aux_cols_factory.make_read_aux_cols(memory.record_by_id(dim_base_pointer_read)); - cols.opened_base_pointer_read = + specific.opened_base_pointer_read = aux_cols_factory.make_read_aux_cols(memory.record_by_id(opened_base_pointer_read)); - cols.opened_length_read = + specific.opened_length_read = aux_cols_factory.make_read_aux_cols(memory.record_by_id(opened_length_read)); - cols.sibling_base_pointer_read = + specific.sibling_base_pointer_read = aux_cols_factory.make_read_aux_cols(memory.record_by_id(sibling_base_pointer_read)); - cols.index_base_pointer_read = + specific.index_base_pointer_read = aux_cols_factory.make_read_aux_cols(memory.record_by_id(index_base_pointer_read)); - cols.commit_pointer_read = + specific.commit_pointer_read = aux_cols_factory.make_read_aux_cols(memory.record_by_id(commit_pointer_read)); - cols.commit_read = aux_cols_factory.make_read_aux_cols(memory.record_by_id(commit_read)); + specific.commit_read = + aux_cols_factory.make_read_aux_cols(memory.record_by_id(commit_read)); } fn incorporate_row_record_to_row( &self, @@ -188,21 +195,24 @@ impl VerifyBatchChip = + cols.specific[..TopLevelSpecificCols::::width()].borrow_mut(); + + specific.end_timestamp = F::from_canonical_u32(final_height_read.timestamp + 1); cols.initial_opened_index = F::from_canonical_usize(initial_opened_index); - cols.final_opened_index = F::from_canonical_usize(final_opened_index); - cols.height = F::from_canonical_usize(height); - cols.opened_length = F::from_canonical_usize(parent.opened_length); - cols.dim_base_pointer = parent.dim_base_pointer; + specific.final_opened_index = F::from_canonical_usize(final_opened_index); + specific.height = F::from_canonical_usize(height); + specific.opened_length = F::from_canonical_usize(parent.opened_length); + specific.dim_base_pointer = parent.dim_base_pointer; cols.opened_base_pointer = parent.opened_base_pointer; - cols.sibling_base_pointer = parent.sibling_base_pointer; - cols.index_base_pointer = parent.index_base_pointer; + specific.sibling_base_pointer = parent.sibling_base_pointer; + specific.index_base_pointer = parent.index_base_pointer; - cols.proof_index = F::from_canonical_usize(proof_index); - cols.read_initial_height_or_root_is_on_right = + specific.proof_index = F::from_canonical_usize(proof_index); + specific.read_initial_height_or_root_is_on_right = aux_cols_factory.make_read_aux_cols(initial_height_read); - cols.read_final_height_or_sibling_array_start = + specific.read_final_height_or_sibling_array_start = aux_cols_factory.make_read_aux_cols(final_height_read); } fn inside_row_record_to_row( @@ -236,8 +246,10 @@ impl VerifyBatchChip = + cols.specific[..InsideRowSpecificCols::::width()].borrow_mut(); - for (record, cell) in cells.iter().zip(cols.cells.iter_mut()) { + for (record, cell) in cells.iter().zip(specific.cells.iter_mut()) { let &CellRecord { read, opened_index, @@ -254,13 +266,14 @@ impl VerifyBatchChip= cells.len())); + cols.initial_opened_index = F::from_canonical_usize(parent.initial_opened_index); cols.opened_base_pointer = grandparent.opened_base_pointer; } From 7a0ee20663e401236663c9d08d5110b319b1f352 Mon Sep 17 00:00:00 2001 From: TlatoaniHJ Date: Thu, 16 Jan 2025 19:19:29 -0500 Subject: [PATCH 37/50] Passing old p2 tests --- extensions/native/circuit/src/extension.rs | 6 +- .../native/circuit/src/verify_batch/air.rs | 86 ++- .../native/circuit/src/verify_batch/chip.rs | 537 ++++++++++-------- .../circuit/src/verify_batch/columns.rs | 28 +- .../native/circuit/src/verify_batch/tests.rs | 138 ++++- .../native/circuit/src/verify_batch/trace.rs | 83 ++- 6 files changed, 619 insertions(+), 259 deletions(-) diff --git a/extensions/native/circuit/src/extension.rs b/extensions/native/circuit/src/extension.rs index e1996f7ce1..fb30358abb 100644 --- a/extensions/native/circuit/src/extension.rs +++ b/extensions/native/circuit/src/extension.rs @@ -209,12 +209,16 @@ impl VmExtension for Native { program_bus, memory_bridge, VerifyBatchOpcode::default_offset(), + Poseidon2Opcode::default_offset(), offline_memory.clone(), Poseidon2Config::default(), ); inventory.add_executor( verify_batch_chip, - VerifyBatchOpcode::iter().map(VmOpcode::with_default_offset), + [ + VmOpcode::with_default_offset(VerifyBatchOpcode::VERIFY_BATCH), + VmOpcode::with_default_offset(Poseidon2Opcode::PERM_POS2), + ], )?; let poseidon2_chip = NativePoseidon2Chip::new( diff --git a/extensions/native/circuit/src/verify_batch/air.rs b/extensions/native/circuit/src/verify_batch/air.rs index b1972a860b..727fa62a5d 100644 --- a/extensions/native/circuit/src/verify_batch/air.rs +++ b/extensions/native/circuit/src/verify_batch/air.rs @@ -5,6 +5,7 @@ use openvm_circuit::{ system::memory::{offline_checker::MemoryBridge, MemoryAddress}, }; use openvm_circuit_primitives::utils::not; +use openvm_instructions::Poseidon2Opcode::PERM_POS2; use openvm_native_compiler::VerifyBatchOpcode::VERIFY_BATCH; use openvm_poseidon2_air::{Poseidon2SubAir, BABY_BEAR_POSEIDON2_HALF_FULL_ROUNDS}; use openvm_stark_backend::{ @@ -19,7 +20,9 @@ use openvm_stark_backend::{ use crate::{ chip::NUM_INITIAL_READS, verify_batch::{ - columns::{InsideRowSpecificCols, TopLevelSpecificCols, VerifyBatchCols}, + columns::{ + InsideRowSpecificCols, SimplePermuteSpecificCols, TopLevelSpecificCols, VerifyBatchCols, + }, CHUNK, }, }; @@ -30,7 +33,8 @@ pub struct VerifyBatchAir { pub memory_bridge: MemoryBridge, pub internal_bus: VerifyBatchBus, pub(crate) subair: Arc>, - pub(super) offset: usize, + pub(super) verify_batch_offset: usize, + pub(super) perm_pos2_offset: usize, pub(crate) address_space: F, } @@ -65,6 +69,7 @@ impl Air incorporate_row, incorporate_sibling, inside_row, + simple, end_inside_row, end_top_level, start_top_level, @@ -91,13 +96,14 @@ impl Air builder.assert_bool(incorporate_row); builder.assert_bool(incorporate_sibling); builder.assert_bool(inside_row); - let enabled = incorporate_row + incorporate_sibling + inside_row; + builder.assert_bool(simple); + let enabled = incorporate_row + incorporate_sibling + inside_row + simple; builder.assert_bool(enabled.clone()); builder.assert_bool(end_inside_row); builder.when(end_inside_row).assert_one(inside_row); builder.assert_bool(end_top_level); - let end = end_inside_row + end_top_level + (AB::Expr::ONE - enabled.clone()); + let end = end_inside_row + end_top_level + simple + (AB::Expr::ONE - enabled.clone()); builder .when(end.clone()) .when(next.incorporate_row) @@ -335,7 +341,7 @@ impl Air .assert_eq(next.initial_opened_index, AB::F::ZERO); self.execution_bridge .execute_and_increment_pc( - AB::Expr::from_canonical_usize(VERIFY_BATCH as usize + self.offset), + AB::Expr::from_canonical_usize(VERIFY_BATCH as usize + self.verify_batch_offset), [ dim_register, opened_register, @@ -575,6 +581,76 @@ impl Air end_timestamp, timestamp_after_initial_reads + AB::F::from_canonical_usize(2 + CHUNK), ); + + //// simple permute + + let simple_permute_specific: &SimplePermuteSpecificCols = + specific[..SimplePermuteSpecificCols::::width()].borrow(); + + let &SimplePermuteSpecificCols { + pc, + output_register, + input_register, + register_address_space, + data_address_space, + output_pointer, + input_pointer, + read_output_pointer, + read_input_pointer, + read_data, + write_data, + } = simple_permute_specific; + + self.execution_bridge + .execute_and_increment_pc( + AB::Expr::from_canonical_usize(PERM_POS2 as usize + self.perm_pos2_offset), + [ + output_register.into(), + input_register.into(), + AB::Expr::ZERO, + register_address_space.into(), + data_address_space.into(), + ], + ExecutionState::new(pc, start_timestamp), + AB::Expr::from_canonical_usize(4), + ) + .eval(builder, simple); + + self.memory_bridge + .read( + MemoryAddress::new(register_address_space, output_register), + [output_pointer], + start_timestamp, + &read_output_pointer, + ) + .eval(builder, simple); + + self.memory_bridge + .read( + MemoryAddress::new(register_address_space, input_register), + [input_pointer], + start_timestamp + AB::F::ONE, + &read_input_pointer, + ) + .eval(builder, simple); + + self.memory_bridge + .read( + MemoryAddress::new(data_address_space, input_pointer), + local.inner.inputs, + start_timestamp + AB::F::TWO, + &read_data, + ) + .eval(builder, simple); + + self.memory_bridge + .write( + MemoryAddress::new(data_address_space, output_pointer), + local.inner.ending_full_rounds[BABY_BEAR_POSEIDON2_HALF_FULL_ROUNDS - 1].post, + start_timestamp + AB::F::from_canonical_usize(3), + &write_data, + ) + .eval(builder, simple); } } diff --git a/extensions/native/circuit/src/verify_batch/chip.rs b/extensions/native/circuit/src/verify_batch/chip.rs index 2d6588edcf..d2973c81bf 100644 --- a/extensions/native/circuit/src/verify_batch/chip.rs +++ b/extensions/native/circuit/src/verify_batch/chip.rs @@ -7,7 +7,9 @@ use openvm_circuit::{ program::ProgramBus, }, }; -use openvm_instructions::{instruction::Instruction, program::DEFAULT_PC_STEP}; +use openvm_instructions::{ + instruction::Instruction, program::DEFAULT_PC_STEP, Poseidon2Opcode::PERM_POS2, VmOpcode, +}; use openvm_native_compiler::VerifyBatchOpcode::VERIFY_BATCH; use openvm_poseidon2_air::{Poseidon2Config, Poseidon2SubAir, Poseidon2SubChip}; use openvm_stark_backend::{ @@ -98,9 +100,32 @@ pub struct CellRecord { pub row_end: usize, } +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(bound = "F: Field")] +pub struct SimplePermuteRecord { + pub from_state: ExecutionState, + pub instruction: Instruction, + + pub read_input_pointer: RecordId, + pub read_output_pointer: RecordId, + pub read_data: RecordId, + pub write_data: RecordId, + + pub input_pointer: F, + pub output_pointer: F, + pub p2_input: [F; 2 * CHUNK], +} + +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +#[serde(bound = "F: Field")] +pub struct VerifyBatchRecordSet { + pub verify_batch_records: Vec>, + pub simple_permute_records: Vec>, +} + pub struct VerifyBatchChip { pub(super) air: VerifyBatchAir, - pub(super) records: Vec>, + pub(super) record_set: VerifyBatchRecordSet, pub(super) height: usize, pub(super) offline_memory: Arc>>, pub(super) subchip: Poseidon2SubChip, @@ -110,9 +135,9 @@ impl Stateful> for VerifyBatchChip { fn load_state(&mut self, state: Vec) { - self.records = bitcode::deserialize(&state).unwrap(); - self.height = 0; - for record in self.records.iter() { + self.record_set = bitcode::deserialize(&state).unwrap(); + self.height = self.record_set.simple_permute_records.len(); + for record in self.record_set.verify_batch_records.iter() { for top_level in record.top_level.iter() { if let Some(incorporate_row) = &top_level.incorporate_row { self.height += 1 + incorporate_row.chunks.len(); @@ -125,7 +150,7 @@ impl Stateful> } fn store_state(&self) -> Vec { - bitcode::serialize(&self.records).unwrap() + bitcode::serialize(&self.record_set).unwrap() } } @@ -134,7 +159,8 @@ impl VerifyBatchChip>>, poseidon2_config: Poseidon2Config, ) -> Self { @@ -143,11 +169,12 @@ impl VerifyBatchChip InstructionExecutor instruction: &Instruction, from_state: ExecutionState, ) -> Result, ExecutionError> { - let &Instruction { - a: dim_register, - b: opened_register, - c: opened_length_register, - d: sibling_register, - e: index_register, - f: commit_register, - g: opened_element_size_inv, - .. - } = instruction; - let address_space = self.air.address_space; - // calc inverse fast assuming opened_element_size in {1, 4} - let mut opened_element_size = F::ONE; - while opened_element_size * opened_element_size_inv != F::ONE { - opened_element_size += F::ONE; - } - - let (dim_base_pointer_read, dim_base_pointer) = - memory.read_cell(address_space, dim_register); - let (opened_base_pointer_read, opened_base_pointer) = - memory.read_cell(address_space, opened_register); - let (opened_length_read, opened_length) = - memory.read_cell(address_space, opened_length_register); - let (sibling_base_pointer_read, sibling_base_pointer) = - memory.read_cell(address_space, sibling_register); - let (index_base_pointer_read, index_base_pointer) = - memory.read_cell(address_space, index_register); - let (commit_pointer_read, commit_pointer) = - memory.read_cell(address_space, commit_register); - let (commit_read, commit) = memory.read(address_space, commit_pointer); - - let opened_length = opened_length.as_canonical_u32() as usize; - - let initial_height = memory - .unsafe_read_cell(address_space, dim_base_pointer) - .as_canonical_u32(); - let mut height = initial_height; - let mut proof_index = 0; - let mut opened_index = 0; - let mut top_level = vec![]; - - let mut root = [F::ZERO; CHUNK]; - - while height >= 1 { - let incorporate_row = if opened_index < opened_length - && memory.unsafe_read_cell( - address_space, - dim_base_pointer + F::from_canonical_usize(opened_index), - ) == F::from_canonical_u32(height) - { - let initial_opened_index = opened_index; - for _ in 0..NUM_INITIAL_READS { - memory.increment_timestamp(); - } - let mut chunks = vec![]; - - let mut row_pointer = 0; - let mut row_end = 0; - - let mut prev_rolling_hash: Option<[F; 2 * CHUNK]> = None; - let mut rolling_hash = [F::ZERO; 2 * CHUNK]; - - let mut is_first_in_segment = true; + if instruction.opcode == VmOpcode::with_default_offset(PERM_POS2) { + let &Instruction { + a: output_register, + b: input_register, + d: register_address_space, + e: data_address_space, + .. + } = instruction; + + let (read_output_pointer, output_pointer) = + memory.read_cell(register_address_space, output_register); + let (read_input_pointer, input_pointer) = + memory.read_cell(register_address_space, input_register); + let (read_data, data) = memory.read(data_address_space, input_pointer); + let output = self.subchip.permute(data); + let (write_data, _) = memory.write(data_address_space, output_pointer, output); + + self.record_set + .simple_permute_records + .push(SimplePermuteRecord { + from_state, + instruction: instruction.clone(), + read_input_pointer, + read_output_pointer, + read_data, + write_data, + input_pointer, + output_pointer, + p2_input: data, + }); + self.height += 1; + } else if instruction.opcode == VmOpcode::with_default_offset(VERIFY_BATCH) { + let &Instruction { + a: dim_register, + b: opened_register, + c: opened_length_register, + d: sibling_register, + e: index_register, + f: commit_register, + g: opened_element_size_inv, + .. + } = instruction; + let address_space = self.air.address_space; + // calc inverse fast assuming opened_element_size in {1, 4} + let mut opened_element_size = F::ONE; + while opened_element_size * opened_element_size_inv != F::ONE { + opened_element_size += F::ONE; + } - loop { - let mut cells = vec![]; - for i in 0..CHUNK { - let read_row_pointer_and_length = if is_first_in_segment - || row_pointer == row_end - { - if is_first_in_segment { - is_first_in_segment = false; - } else { - opened_index += 1; - if opened_index == opened_length - || memory.unsafe_read_cell( - address_space, - dim_base_pointer + F::from_canonical_usize(opened_index), - ) != F::from_canonical_u32(height) - { - break; + let (dim_base_pointer_read, dim_base_pointer) = + memory.read_cell(address_space, dim_register); + let (opened_base_pointer_read, opened_base_pointer) = + memory.read_cell(address_space, opened_register); + let (opened_length_read, opened_length) = + memory.read_cell(address_space, opened_length_register); + let (sibling_base_pointer_read, sibling_base_pointer) = + memory.read_cell(address_space, sibling_register); + let (index_base_pointer_read, index_base_pointer) = + memory.read_cell(address_space, index_register); + let (commit_pointer_read, commit_pointer) = + memory.read_cell(address_space, commit_register); + let (commit_read, commit) = memory.read(address_space, commit_pointer); + + let opened_length = opened_length.as_canonical_u32() as usize; + + let initial_height = memory + .unsafe_read_cell(address_space, dim_base_pointer) + .as_canonical_u32(); + let mut height = initial_height; + let mut proof_index = 0; + let mut opened_index = 0; + let mut top_level = vec![]; + + let mut root = [F::ZERO; CHUNK]; + + while height >= 1 { + let incorporate_row = if opened_index < opened_length + && memory.unsafe_read_cell( + address_space, + dim_base_pointer + F::from_canonical_usize(opened_index), + ) == F::from_canonical_u32(height) + { + let initial_opened_index = opened_index; + for _ in 0..NUM_INITIAL_READS { + memory.increment_timestamp(); + } + let mut chunks = vec![]; + + let mut row_pointer = 0; + let mut row_end = 0; + + let mut prev_rolling_hash: Option<[F; 2 * CHUNK]> = None; + let mut rolling_hash = [F::ZERO; 2 * CHUNK]; + + let mut is_first_in_segment = true; + + loop { + let mut cells = vec![]; + for i in 0..CHUNK { + let read_row_pointer_and_length = if is_first_in_segment + || row_pointer == row_end + { + if is_first_in_segment { + is_first_in_segment = false; + } else { + opened_index += 1; + if opened_index == opened_length + || memory.unsafe_read_cell( + address_space, + dim_base_pointer + + F::from_canonical_usize(opened_index), + ) != F::from_canonical_u32(height) + { + break; + } } - } - let (result, [new_row_pointer, row_len]) = memory.read( - address_space, - opened_base_pointer + F::from_canonical_usize(2 * opened_index), - ); - row_pointer = new_row_pointer.as_canonical_u32() as usize; - row_end = row_pointer - + (opened_element_size * row_len).as_canonical_u32() as usize; - Some(result) - } else { - memory.increment_timestamp(); - None - }; - let (read, value) = - memory.read_cell(address_space, F::from_canonical_usize(row_pointer)); - cells.push(CellRecord { - read, - opened_index, - read_row_pointer_and_length, - row_pointer, - row_end, + let (result, [new_row_pointer, row_len]) = memory.read( + address_space, + opened_base_pointer + F::from_canonical_usize(2 * opened_index), + ); + row_pointer = new_row_pointer.as_canonical_u32() as usize; + row_end = row_pointer + + (opened_element_size * row_len).as_canonical_u32() as usize; + Some(result) + } else { + memory.increment_timestamp(); + None + }; + let (read, value) = memory + .read_cell(address_space, F::from_canonical_usize(row_pointer)); + cells.push(CellRecord { + read, + opened_index, + read_row_pointer_and_length, + row_pointer, + row_end, + }); + rolling_hash[i] = value; + row_pointer += 1; + } + if cells.is_empty() { + break; + } + let cells_len = cells.len(); + chunks.push(InsideRowRecord { + cells, + p2_input: rolling_hash, }); - rolling_hash[i] = value; - row_pointer += 1; - } - if cells.is_empty() { - break; - } - let cells_len = cells.len(); - chunks.push(InsideRowRecord { - cells, - p2_input: rolling_hash, - }); - self.height += 1; - prev_rolling_hash = Some(rolling_hash); - self.subchip.permute_mut(&mut rolling_hash); - if cells_len < CHUNK { - for _ in 0..CHUNK - cells_len { - memory.increment_timestamp(); - memory.increment_timestamp(); + self.height += 1; + prev_rolling_hash = Some(rolling_hash); + self.subchip.permute_mut(&mut rolling_hash); + if cells_len < CHUNK { + for _ in 0..CHUNK - cells_len { + memory.increment_timestamp(); + memory.increment_timestamp(); + } + break; } - break; } - } - let final_opened_index = opened_index - 1; - let (initial_height_read, height_check) = memory.read_cell( - address_space, - dim_base_pointer + F::from_canonical_usize(initial_opened_index), - ); - assert_eq!(height_check, F::from_canonical_u32(height)); - let (final_height_read, height_check) = memory.read_cell( - address_space, - dim_base_pointer + F::from_canonical_usize(final_opened_index), - ); - assert_eq!(height_check, F::from_canonical_u32(height)); - - let hash: [F; CHUNK] = std::array::from_fn(|i| rolling_hash[i]); - - let (p2_input, new_root) = if height == initial_height { - (prev_rolling_hash.unwrap(), hash) + let final_opened_index = opened_index - 1; + let (initial_height_read, height_check) = memory.read_cell( + address_space, + dim_base_pointer + F::from_canonical_usize(initial_opened_index), + ); + assert_eq!(height_check, F::from_canonical_u32(height)); + let (final_height_read, height_check) = memory.read_cell( + address_space, + dim_base_pointer + F::from_canonical_usize(final_opened_index), + ); + assert_eq!(height_check, F::from_canonical_u32(height)); + + let hash: [F; CHUNK] = std::array::from_fn(|i| rolling_hash[i]); + + let (p2_input, new_root) = if height == initial_height { + (prev_rolling_hash.unwrap(), hash) + } else { + self.compress(root, hash) + }; + root = new_root; + + self.height += 1; + Some(IncorporateRowRecord { + chunks, + initial_opened_index, + final_opened_index, + initial_height_read, + final_height_read, + p2_input, + }) } else { - self.compress(root, hash) + None }; - root = new_root; - - self.height += 1; - Some(IncorporateRowRecord { - chunks, - initial_opened_index, - final_opened_index, - initial_height_read, - final_height_read, - p2_input, - }) - } else { - None - }; - - let incorporate_sibling = if height == 1 { - None - } else { - for _ in 0..NUM_INITIAL_READS { - memory.increment_timestamp(); - } - let (read_root_is_on_right, root_is_on_right) = memory.read_cell( - address_space, - index_base_pointer + F::from_canonical_usize(proof_index), - ); - let root_is_on_right = root_is_on_right == F::ONE; - - let (read_sibling_array_start, sibling_array_start) = memory.read_cell( - address_space, - sibling_base_pointer + F::from_canonical_usize(2 * proof_index), - ); - let sibling_array_start = sibling_array_start.as_canonical_u32() as usize; - - let mut sibling = [F::ZERO; CHUNK]; - let mut reads = vec![]; - for i in 0..CHUNK { - let (read, value) = memory.read_cell( + let incorporate_sibling = if height == 1 { + None + } else { + for _ in 0..NUM_INITIAL_READS { + memory.increment_timestamp(); + } + + let (read_root_is_on_right, root_is_on_right) = memory.read_cell( address_space, - F::from_canonical_usize(sibling_array_start + i), + index_base_pointer + F::from_canonical_usize(proof_index), ); - sibling[i] = value; - reads.push(read); - } + let root_is_on_right = root_is_on_right == F::ONE; - let (p2_input, new_root) = if root_is_on_right { - self.compress(sibling, root) - } else { - self.compress(root, sibling) + let (read_sibling_array_start, sibling_array_start) = memory.read_cell( + address_space, + sibling_base_pointer + F::from_canonical_usize(2 * proof_index), + ); + let sibling_array_start = sibling_array_start.as_canonical_u32() as usize; + + let mut sibling = [F::ZERO; CHUNK]; + let mut reads = vec![]; + for i in 0..CHUNK { + let (read, value) = memory.read_cell( + address_space, + F::from_canonical_usize(sibling_array_start + i), + ); + sibling[i] = value; + reads.push(read); + } + + let (p2_input, new_root) = if root_is_on_right { + self.compress(sibling, root) + } else { + self.compress(root, sibling) + }; + root = new_root; + + self.height += 1; + Some(IncorporateSiblingRecord { + read_sibling_array_start, + read_root_is_on_right, + root_is_on_right, + reads: std::array::from_fn(|i| reads[i]), + p2_input, + }) }; - root = new_root; - - self.height += 1; - Some(IncorporateSiblingRecord { - read_sibling_array_start, - read_root_is_on_right, - root_is_on_right, - reads: std::array::from_fn(|i| reads[i]), - p2_input, - }) - }; - - top_level.push(TopLevelRecord { - incorporate_row, - incorporate_sibling, - }); - - height /= 2; - proof_index += 1; - } - assert_eq!(commit, root); - self.records.push(VerifyBatchRecord { - from_state, - instruction: instruction.clone(), - dim_base_pointer, - opened_base_pointer, - opened_length, - sibling_base_pointer, - index_base_pointer, - commit_pointer, - dim_base_pointer_read, - opened_base_pointer_read, - opened_length_read, - sibling_base_pointer_read, - index_base_pointer_read, - commit_pointer_read, - commit_read, - initial_height: initial_height as usize, - top_level, - }); + top_level.push(TopLevelRecord { + incorporate_row, + incorporate_sibling, + }); + height /= 2; + proof_index += 1; + } + + assert_eq!(commit, root); + self.record_set + .verify_batch_records + .push(VerifyBatchRecord { + from_state, + instruction: instruction.clone(), + dim_base_pointer, + opened_base_pointer, + opened_length, + sibling_base_pointer, + index_base_pointer, + commit_pointer, + dim_base_pointer_read, + opened_base_pointer_read, + opened_length_read, + sibling_base_pointer_read, + index_base_pointer_read, + commit_pointer_read, + commit_read, + initial_height: initial_height as usize, + top_level, + }); + } else { + unreachable!() + } Ok(ExecutionState { pc: from_state.pc + DEFAULT_PC_STEP, timestamp: memory.timestamp(), @@ -418,7 +482,12 @@ impl InstructionExecutor } fn get_opcode_name(&self, opcode: usize) -> String { - assert_eq!(opcode, (VERIFY_BATCH as usize) + self.air.offset); - String::from("VERIFY_BATCH") + if opcode == (VERIFY_BATCH as usize) + self.air.verify_batch_offset { + return String::from("VERIFY_BATCH"); + } else if opcode == (PERM_POS2 as usize) + self.air.perm_pos2_offset { + return String::from("PERM_POS2"); + } else { + unreachable!() + } } } diff --git a/extensions/native/circuit/src/verify_batch/columns.rs b/extensions/native/circuit/src/verify_batch/columns.rs index d0d0b58c47..69724da9d1 100644 --- a/extensions/native/circuit/src/verify_batch/columns.rs +++ b/extensions/native/circuit/src/verify_batch/columns.rs @@ -1,4 +1,4 @@ -use openvm_circuit::system::memory::offline_checker::MemoryReadAuxCols; +use openvm_circuit::system::memory::offline_checker::{MemoryReadAuxCols, MemoryWriteAuxCols}; use openvm_circuit_primitives_derive::AlignedBorrow; use openvm_poseidon2_air::Poseidon2SubCols; @@ -14,6 +14,7 @@ pub struct VerifyBatchCols { pub incorporate_row: T, pub incorporate_sibling: T, pub inside_row: T, + pub simple: T, pub end_inside_row: T, pub end_top_level: T, @@ -33,15 +34,19 @@ pub struct VerifyBatchCols { // cannot be shared, should be 0 on rows that are not inside row pub is_exhausted: [T; CHUNK], - pub specific: [T; max( + pub specific: [T; max3( TopLevelSpecificCols::::width(), InsideRowSpecificCols::::width(), + SimplePermuteSpecificCols::::width(), )], } const fn max(a: usize, b: usize) -> usize { [a, b][(a < b) as usize] } +const fn max3(a: usize, b: usize, c: usize) -> usize { + max(a, max(b, c)) +} #[repr(C)] #[derive(AlignedBorrow)] pub struct TopLevelSpecificCols { @@ -102,3 +107,22 @@ pub struct VerifyBatchCellCols { pub row_end: T, pub is_first_in_row: T, } + +#[repr(C)] +#[derive(AlignedBorrow, Copy, Clone)] +pub struct SimplePermuteSpecificCols { + pub pc: T, + // instruction (a, b, d, e) + pub output_register: T, + pub input_register: T, + pub register_address_space: T, + pub data_address_space: T, + + pub output_pointer: T, + pub input_pointer: T, + + pub read_output_pointer: MemoryReadAuxCols, + pub read_input_pointer: MemoryReadAuxCols, + pub read_data: MemoryReadAuxCols, + pub write_data: MemoryWriteAuxCols, +} diff --git a/extensions/native/circuit/src/verify_batch/tests.rs b/extensions/native/circuit/src/verify_batch/tests.rs index a829482424..d367a28f7f 100644 --- a/extensions/native/circuit/src/verify_batch/tests.rs +++ b/extensions/native/circuit/src/verify_batch/tests.rs @@ -1,22 +1,29 @@ use std::cmp::min; -use openvm_circuit::{ - arch::testing::{memory::gen_pointer, VmChipTestBuilder}, - system::memory::CHUNK, +use openvm_circuit::arch::testing::{memory::gen_pointer, VmChipTestBuilder, VmChipTester}; +use openvm_instructions::{ + instruction::Instruction, Poseidon2Opcode, Poseidon2Opcode::PERM_POS2, UsizeOpcode, VmOpcode, }; -use openvm_instructions::{instruction::Instruction, UsizeOpcode, VmOpcode}; use openvm_native_compiler::{VerifyBatchOpcode, VerifyBatchOpcode::VERIFY_BATCH}; use openvm_poseidon2_air::{Poseidon2Config, Poseidon2SubChip}; use openvm_stark_backend::{ p3_air::BaseAir, - p3_field::{Field, FieldAlgebra, PrimeField32}, + p3_field::{Field, FieldAlgebra, PrimeField32, PrimeField64}, utils::disable_debug_builder, verifier::VerificationError, }; -use openvm_stark_sdk::{p3_baby_bear::BabyBear, utils::create_seeded_rng}; +use openvm_stark_sdk::{ + config::{ + baby_bear_blake3::{BabyBearBlake3Config, BabyBearBlake3Engine}, + fri_params::standard_fri_params_with_100_bits_conjectured_security, + }, + engine::StarkFriEngine, + p3_baby_bear::BabyBear, + utils::create_seeded_rng, +}; use rand::{rngs::StdRng, Rng}; -use crate::verify_batch::chip::VerifyBatchChip; +use crate::verify_batch::{chip::VerifyBatchChip, CHUNK}; fn compute_commit( dim: &[usize], @@ -132,14 +139,13 @@ fn test(cases: [Case; N]) { // single op let address_space = 5; - let offset = VerifyBatchOpcode::default_offset(); - let mut tester = VmChipTestBuilder::default(); let mut chip = VerifyBatchChip::::new( tester.execution_bus(), tester.program_bus(), tester.memory_bridge(), - offset, + VerifyBatchOpcode::default_offset(), + Poseidon2Opcode::default_offset(), tester.offline_memory_mutex_arc(), Poseidon2Config::default(), ); @@ -222,7 +228,7 @@ fn test(cases: [Case; N]) { tester.execute( &mut chip, &Instruction::from_usize( - VmOpcode::from_usize(VERIFY_BATCH as usize + offset), + VmOpcode::from_usize(VERIFY_BATCH as usize + VerifyBatchOpcode::default_offset()), [ dim_register, opened_register, @@ -334,3 +340,113 @@ fn verify_batch_test_felt_and_ext() { }, ]) } + +/// Create random instructions for the poseidon2 chip. +fn random_instructions(num_ops: usize) -> Vec> { + let mut rng = create_seeded_rng(); + (0..num_ops) + .map(|_| { + let [a, b] = + std::array::from_fn(|_| BabyBear::from_canonical_usize(gen_pointer(&mut rng, 1))); + Instruction { + opcode: VmOpcode::from_usize( + Poseidon2Opcode::PERM_POS2 as usize + Poseidon2Opcode::default_offset(), + ), + a, + b, + c: BabyBear::ZERO, + d: BabyBear::ONE, + e: BabyBear::TWO, + f: BabyBear::ZERO, + g: BabyBear::ZERO, + } + }) + .collect() +} + +fn tester_with_random_poseidon2_ops(num_ops: usize) -> VmChipTester { + let elem_range = || 1..=100; + + let mut tester = VmChipTestBuilder::default(); + let mut chip = VerifyBatchChip::::new( + tester.execution_bus(), + tester.program_bus(), + tester.memory_bridge(), + VerifyBatchOpcode::default_offset(), + Poseidon2Opcode::default_offset(), + tester.offline_memory_mutex_arc(), + Poseidon2Config::default(), + ); + + let mut rng = create_seeded_rng(); + + for instruction in random_instructions(num_ops) { + let opcode = PERM_POS2; + let [a, b, c, d, e] = [ + instruction.a, + instruction.b, + instruction.c, + instruction.d, + instruction.e, + ] + .map(|elem| elem.as_canonical_u64() as usize); + + let dst = gen_pointer(&mut rng, CHUNK); + let lhs = gen_pointer(&mut rng, CHUNK); + let rhs = gen_pointer(&mut rng, CHUNK); + + let data: [_; 2 * CHUNK] = + std::array::from_fn(|_| BabyBear::from_canonical_usize(rng.gen_range(elem_range()))); + + let hash = chip.subchip.permute(data); + + tester.write_cell(d, a, BabyBear::from_canonical_usize(dst)); + tester.write_cell(d, b, BabyBear::from_canonical_usize(lhs)); + if opcode == Poseidon2Opcode::COMP_POS2 { + tester.write_cell(d, c, BabyBear::from_canonical_usize(rhs)); + } + + match opcode { + Poseidon2Opcode::COMP_POS2 => { + let data_left: [_; CHUNK] = std::array::from_fn(|i| data[i]); + let data_right: [_; CHUNK] = std::array::from_fn(|i| data[CHUNK + i]); + tester.write(e, lhs, data_left); + tester.write(e, rhs, data_right); + } + Poseidon2Opcode::PERM_POS2 => { + tester.write(e, lhs, data); + } + } + + tester.execute(&mut chip, &instruction); + + match opcode { + Poseidon2Opcode::COMP_POS2 => { + let expected: [_; CHUNK] = std::array::from_fn(|i| hash[i]); + let actual = tester.read::<{ CHUNK }>(e, dst); + assert_eq!(expected, actual); + } + Poseidon2Opcode::PERM_POS2 => { + let actual = tester.read::<{ 2 * CHUNK }>(e, dst); + assert_eq!(hash, actual); + } + } + } + tester.build().load(chip).finalize() +} + +fn get_engine() -> BabyBearBlake3Engine { + BabyBearBlake3Engine::new(standard_fri_params_with_100_bits_conjectured_security(3)) +} + +#[test] +fn verify_batch_chip_simple_permute_1() { + let tester = tester_with_random_poseidon2_ops(1); + tester.test(get_engine).expect("Verification failed"); +} + +#[test] +fn verify_batch_chip_simple_permute_50() { + let tester = tester_with_random_poseidon2_ops(50); + tester.test(get_engine).expect("Verification failed"); +} diff --git a/extensions/native/circuit/src/verify_batch/trace.rs b/extensions/native/circuit/src/verify_batch/trace.rs index 8984428b4a..92852fa903 100644 --- a/extensions/native/circuit/src/verify_batch/trace.rs +++ b/extensions/native/circuit/src/verify_batch/trace.rs @@ -14,13 +14,15 @@ use openvm_stark_backend::{ }; use crate::{ - chip::NUM_INITIAL_READS, + chip::{SimplePermuteRecord, NUM_INITIAL_READS}, verify_batch::{ chip::{ CellRecord, IncorporateRowRecord, IncorporateSiblingRecord, InsideRowRecord, VerifyBatchChip, VerifyBatchRecord, }, - columns::{InsideRowSpecificCols, TopLevelSpecificCols, VerifyBatchCols}, + columns::{ + InsideRowSpecificCols, SimplePermuteSpecificCols, TopLevelSpecificCols, VerifyBatchCols, + }, CHUNK, }, }; @@ -72,6 +74,7 @@ impl VerifyBatchChip VerifyBatchChip VerifyBatchChip VerifyBatchChip, aux_cols_factory: &MemoryAuxColsFactory, @@ -347,6 +352,63 @@ impl VerifyBatchChip, + aux_cols_factory: &MemoryAuxColsFactory, + slice: &mut [F], + memory: &OfflineMemory, + ) { + let &SimplePermuteRecord { + from_state, + read_input_pointer, + read_output_pointer, + read_data, + write_data, + input_pointer, + output_pointer, + p2_input, + .. + } = record; + + let output_register = record.instruction.a; + let input_register = record.instruction.b; + let register_address_space = record.instruction.d; + let data_address_space = record.instruction.e; + + let read_input_pointer = memory.record_by_id(read_input_pointer); + let read_output_pointer = memory.record_by_id(read_output_pointer); + let read_data = memory.record_by_id(read_data); + let write_data = memory.record_by_id(write_data); + + self.generate_subair_cols(p2_input, slice); + let cols: &mut VerifyBatchCols = slice.borrow_mut(); + cols.incorporate_row = F::ZERO; + cols.incorporate_sibling = F::ZERO; + cols.inside_row = F::ZERO; + cols.simple = F::ONE; + cols.end_inside_row = F::ZERO; + cols.end_top_level = F::ZERO; + cols.is_exhausted = [F::ZERO; CHUNK]; + + cols.start_timestamp = F::from_canonical_u32(from_state.timestamp); + let specific: &mut SimplePermuteSpecificCols = + cols.specific[..SimplePermuteSpecificCols::::width()].borrow_mut(); + + *specific = SimplePermuteSpecificCols { + pc: F::from_canonical_u32(from_state.pc), + output_register, + input_register, + register_address_space, + data_address_space, + output_pointer, + input_pointer, + read_output_pointer: aux_cols_factory.make_read_aux_cols(read_output_pointer), + read_input_pointer: aux_cols_factory.make_read_aux_cols(read_input_pointer), + read_data: aux_cols_factory.make_read_aux_cols(read_data), + write_data: aux_cols_factory.make_write_aux_cols(write_data), + }; + } fn generate_trace(self) -> RowMajorMatrix { let width = self.trace_width(); @@ -358,14 +420,23 @@ impl VerifyBatchChip Date: Thu, 16 Jan 2025 19:40:44 -0500 Subject: [PATCH 38/50] Add simple perm functionality --- extensions/native/circuit/src/extension.rs | 15 ------- extensions/native/circuit/src/lib.rs | 4 +- .../native/circuit/src/verify_batch/air.rs | 42 +++++++++++++++---- .../native/circuit/src/verify_batch/chip.rs | 40 ++++++++++++++---- .../circuit/src/verify_batch/columns.rs | 6 ++- .../native/circuit/src/verify_batch/mod.rs | 4 +- .../native/circuit/src/verify_batch/trace.rs | 18 +++++--- 7 files changed, 86 insertions(+), 43 deletions(-) diff --git a/extensions/native/circuit/src/extension.rs b/extensions/native/circuit/src/extension.rs index fb30358abb..182430ecb4 100644 --- a/extensions/native/circuit/src/extension.rs +++ b/extensions/native/circuit/src/extension.rs @@ -86,7 +86,6 @@ pub enum NativeExecutor { Jal(NativeJalChip), FieldArithmetic(FieldArithmeticChip), FieldExtension(FieldExtensionChip), - Poseidon2(NativePoseidon2Chip), FriReducedOpening(FriReducedOpeningChip), VerifyBatch(VerifyBatchChip), } @@ -221,20 +220,6 @@ impl VmExtension for Native { ], )?; - let poseidon2_chip = NativePoseidon2Chip::new( - execution_bus, - program_bus, - memory_bridge, - Poseidon2Config::default(), - Poseidon2Opcode::default_offset(), - builder.system_config().max_constraint_degree, - offline_memory.clone(), - ); - inventory.add_executor( - poseidon2_chip, - Poseidon2Opcode::iter().map(VmOpcode::with_default_offset), - )?; - builder.add_phantom_sub_executor( NativeHintInputSubEx, PhantomDiscriminant(NativePhantom::HintInput as u16), diff --git a/extensions/native/circuit/src/lib.rs b/extensions/native/circuit/src/lib.rs index 60c902b831..8916d17c4f 100644 --- a/extensions/native/circuit/src/lib.rs +++ b/extensions/native/circuit/src/lib.rs @@ -7,7 +7,7 @@ mod field_extension; mod fri; mod jal; mod loadstore; -mod poseidon2; +// mod poseidon2; mod verify_batch; pub use branch_eq::*; @@ -17,7 +17,7 @@ pub use field_extension::*; pub use fri::*; pub use jal::*; pub use loadstore::*; -pub use poseidon2::*; +// pub use poseidon2::*; pub use verify_batch::*; mod extension; diff --git a/extensions/native/circuit/src/verify_batch/air.rs b/extensions/native/circuit/src/verify_batch/air.rs index 727fa62a5d..f14c5cc7b5 100644 --- a/extensions/native/circuit/src/verify_batch/air.rs +++ b/extensions/native/circuit/src/verify_batch/air.rs @@ -597,8 +597,10 @@ impl Air input_pointer, read_output_pointer, read_input_pointer, - read_data, - write_data, + read_data_1, + read_data_2, + write_data_1, + write_data_2, } = simple_permute_specific; self.execution_bridge @@ -612,7 +614,7 @@ impl Air data_address_space.into(), ], ExecutionState::new(pc, start_timestamp), - AB::Expr::from_canonical_usize(4), + AB::Expr::from_canonical_usize(6), ) .eval(builder, simple); @@ -637,18 +639,42 @@ impl Air self.memory_bridge .read( MemoryAddress::new(data_address_space, input_pointer), - local.inner.inputs, + left_input, start_timestamp + AB::F::TWO, - &read_data, + &read_data_1, + ) + .eval(builder, simple); + + self.memory_bridge + .read( + MemoryAddress::new( + data_address_space, + input_pointer + AB::F::from_canonical_usize(CHUNK), + ), + right_input, + start_timestamp + AB::F::from_canonical_usize(3), + &read_data_2, ) .eval(builder, simple); self.memory_bridge .write( MemoryAddress::new(data_address_space, output_pointer), - local.inner.ending_full_rounds[BABY_BEAR_POSEIDON2_HALF_FULL_ROUNDS - 1].post, - start_timestamp + AB::F::from_canonical_usize(3), - &write_data, + left_output, + start_timestamp + AB::F::from_canonical_usize(4), + &write_data_1, + ) + .eval(builder, simple); + + self.memory_bridge + .write( + MemoryAddress::new( + data_address_space, + output_pointer + AB::F::from_canonical_usize(CHUNK), + ), + right_output, + start_timestamp + AB::F::from_canonical_usize(5), + &write_data_2, ) .eval(builder, simple); } diff --git a/extensions/native/circuit/src/verify_batch/chip.rs b/extensions/native/circuit/src/verify_batch/chip.rs index d2973c81bf..317dfcc648 100644 --- a/extensions/native/circuit/src/verify_batch/chip.rs +++ b/extensions/native/circuit/src/verify_batch/chip.rs @@ -108,8 +108,10 @@ pub struct SimplePermuteRecord { pub read_input_pointer: RecordId, pub read_output_pointer: RecordId, - pub read_data: RecordId, - pub write_data: RecordId, + pub read_data_1: RecordId, + pub read_data_2: RecordId, + pub write_data_1: RecordId, + pub write_data_2: RecordId, pub input_pointer: F, pub output_pointer: F, @@ -214,9 +216,29 @@ impl InstructionExecutor memory.read_cell(register_address_space, output_register); let (read_input_pointer, input_pointer) = memory.read_cell(register_address_space, input_register); - let (read_data, data) = memory.read(data_address_space, input_pointer); - let output = self.subchip.permute(data); - let (write_data, _) = memory.write(data_address_space, output_pointer, output); + let (read_data_1, data_1) = memory.read::(data_address_space, input_pointer); + let (read_data_2, data_2) = memory.read::( + data_address_space, + input_pointer + F::from_canonical_usize(CHUNK), + ); + let p2_input = std::array::from_fn(|i| { + if i < CHUNK { + data_1[i] + } else { + data_2[i - CHUNK] + } + }); + let output = self.subchip.permute(p2_input); + let (write_data_1, _) = memory.write::( + data_address_space, + output_pointer, + std::array::from_fn(|i| output[i]), + ); + let (write_data_2, _) = memory.write::( + data_address_space, + output_pointer + F::from_canonical_usize(CHUNK), + std::array::from_fn(|i| output[CHUNK + i]), + ); self.record_set .simple_permute_records @@ -225,11 +247,13 @@ impl InstructionExecutor instruction: instruction.clone(), read_input_pointer, read_output_pointer, - read_data, - write_data, + read_data_1, + read_data_2, + write_data_1, + write_data_2, input_pointer, output_pointer, - p2_input: data, + p2_input, }); self.height += 1; } else if instruction.opcode == VmOpcode::with_default_offset(VERIFY_BATCH) { diff --git a/extensions/native/circuit/src/verify_batch/columns.rs b/extensions/native/circuit/src/verify_batch/columns.rs index 69724da9d1..879257803b 100644 --- a/extensions/native/circuit/src/verify_batch/columns.rs +++ b/extensions/native/circuit/src/verify_batch/columns.rs @@ -123,6 +123,8 @@ pub struct SimplePermuteSpecificCols { pub read_output_pointer: MemoryReadAuxCols, pub read_input_pointer: MemoryReadAuxCols, - pub read_data: MemoryReadAuxCols, - pub write_data: MemoryWriteAuxCols, + pub read_data_1: MemoryReadAuxCols, + pub read_data_2: MemoryReadAuxCols, + pub write_data_1: MemoryWriteAuxCols, + pub write_data_2: MemoryWriteAuxCols, } diff --git a/extensions/native/circuit/src/verify_batch/mod.rs b/extensions/native/circuit/src/verify_batch/mod.rs index 682226edfc..2c1509dc98 100644 --- a/extensions/native/circuit/src/verify_batch/mod.rs +++ b/extensions/native/circuit/src/verify_batch/mod.rs @@ -1,4 +1,4 @@ -use crate::NATIVE_POSEIDON2_CHUNK_SIZE; +//use crate::NATIVE_POSEIDON2_CHUNK_SIZE; mod air; pub mod chip; @@ -7,4 +7,4 @@ mod columns; mod tests; mod trace; -const CHUNK: usize = NATIVE_POSEIDON2_CHUNK_SIZE; +const CHUNK: usize = 8; //NATIVE_POSEIDON2_CHUNK_SIZE; diff --git a/extensions/native/circuit/src/verify_batch/trace.rs b/extensions/native/circuit/src/verify_batch/trace.rs index 92852fa903..7e6640e9a8 100644 --- a/extensions/native/circuit/src/verify_batch/trace.rs +++ b/extensions/native/circuit/src/verify_batch/trace.rs @@ -363,8 +363,10 @@ impl VerifyBatchChip VerifyBatchChip = slice.borrow_mut(); @@ -405,8 +409,10 @@ impl VerifyBatchChip Date: Fri, 17 Jan 2025 11:28:17 -0500 Subject: [PATCH 39/50] Support COMP_POS2 --- extensions/native/circuit/src/extension.rs | 1 + .../native/circuit/src/verify_batch/air.rs | 73 ++++++++++------ .../native/circuit/src/verify_batch/chip.rs | 83 +++++++++++++------ .../circuit/src/verify_batch/columns.rs | 16 ++-- .../native/circuit/src/verify_batch/tests.rs | 37 ++++++--- .../native/circuit/src/verify_batch/trace.rs | 80 +++++++++++------- 6 files changed, 188 insertions(+), 102 deletions(-) diff --git a/extensions/native/circuit/src/extension.rs b/extensions/native/circuit/src/extension.rs index 182430ecb4..2feb3f7d85 100644 --- a/extensions/native/circuit/src/extension.rs +++ b/extensions/native/circuit/src/extension.rs @@ -217,6 +217,7 @@ impl VmExtension for Native { [ VmOpcode::with_default_offset(VerifyBatchOpcode::VERIFY_BATCH), VmOpcode::with_default_offset(Poseidon2Opcode::PERM_POS2), + VmOpcode::with_default_offset(Poseidon2Opcode::COMP_POS2), ], )?; diff --git a/extensions/native/circuit/src/verify_batch/air.rs b/extensions/native/circuit/src/verify_batch/air.rs index f14c5cc7b5..9b0a6d8eb1 100644 --- a/extensions/native/circuit/src/verify_batch/air.rs +++ b/extensions/native/circuit/src/verify_batch/air.rs @@ -5,7 +5,7 @@ use openvm_circuit::{ system::memory::{offline_checker::MemoryBridge, MemoryAddress}, }; use openvm_circuit_primitives::utils::not; -use openvm_instructions::Poseidon2Opcode::PERM_POS2; +use openvm_instructions::Poseidon2Opcode::{COMP_POS2, PERM_POS2}; use openvm_native_compiler::VerifyBatchOpcode::VERIFY_BATCH; use openvm_poseidon2_air::{Poseidon2SubAir, BABY_BEAR_POSEIDON2_HALF_FULL_ROUNDS}; use openvm_stark_backend::{ @@ -18,10 +18,11 @@ use openvm_stark_backend::{ }; use crate::{ - chip::NUM_INITIAL_READS, + chip::{NUM_INITIAL_READS, NUM_SIMPLE_ACCESSES}, verify_batch::{ columns::{ - InsideRowSpecificCols, SimplePermuteSpecificCols, TopLevelSpecificCols, VerifyBatchCols, + InsideRowSpecificCols, SimplePoseidonSpecificCols, TopLevelSpecificCols, + VerifyBatchCols, }, CHUNK, }, @@ -34,7 +35,7 @@ pub struct VerifyBatchAir { pub internal_bus: VerifyBatchBus, pub(crate) subair: Arc>, pub(super) verify_batch_offset: usize, - pub(super) perm_pos2_offset: usize, + pub(super) simple_offset: usize, pub(crate) address_space: F, } @@ -584,37 +585,45 @@ impl Air //// simple permute - let simple_permute_specific: &SimplePermuteSpecificCols = - specific[..SimplePermuteSpecificCols::::width()].borrow(); + let simple_permute_specific: &SimplePoseidonSpecificCols = + specific[..SimplePoseidonSpecificCols::::width()].borrow(); - let &SimplePermuteSpecificCols { + let &SimplePoseidonSpecificCols { pc, + is_compress, output_register, - input_register, + input_register_1, + input_register_2, register_address_space, data_address_space, output_pointer, - input_pointer, + input_pointer_1, + input_pointer_2, read_output_pointer, - read_input_pointer, + read_input_pointer_1, + read_input_pointer_2, read_data_1, read_data_2, write_data_1, write_data_2, } = simple_permute_specific; + let is_permute = AB::Expr::ONE - is_compress; + self.execution_bridge .execute_and_increment_pc( - AB::Expr::from_canonical_usize(PERM_POS2 as usize + self.perm_pos2_offset), + (is_permute.clone() * AB::F::from_canonical_usize(PERM_POS2 as usize)) + + (is_compress * AB::F::from_canonical_usize(COMP_POS2 as usize)) + + AB::F::from_canonical_usize(self.simple_offset), [ output_register.into(), - input_register.into(), - AB::Expr::ZERO, + input_register_1.into(), + input_register_2.into(), register_address_space.into(), data_address_space.into(), ], ExecutionState::new(pc, start_timestamp), - AB::Expr::from_canonical_usize(6), + AB::Expr::from_canonical_u32(NUM_SIMPLE_ACCESSES), ) .eval(builder, simple); @@ -629,30 +638,40 @@ impl Air self.memory_bridge .read( - MemoryAddress::new(register_address_space, input_register), - [input_pointer], + MemoryAddress::new(register_address_space, input_register_1), + [input_pointer_1], start_timestamp + AB::F::ONE, - &read_input_pointer, + &read_input_pointer_1, ) .eval(builder, simple); self.memory_bridge .read( - MemoryAddress::new(data_address_space, input_pointer), - left_input, + MemoryAddress::new(register_address_space, input_register_2), + [input_pointer_2], start_timestamp + AB::F::TWO, + &read_input_pointer_2, + ) + .eval(builder, simple * is_compress); + builder.when(simple).when(is_permute.clone()).assert_eq( + input_pointer_2, + input_pointer_1 + AB::F::from_canonical_usize(CHUNK), + ); + + self.memory_bridge + .read( + MemoryAddress::new(data_address_space, input_pointer_1), + left_input, + start_timestamp + AB::F::from_canonical_usize(3), &read_data_1, ) .eval(builder, simple); self.memory_bridge .read( - MemoryAddress::new( - data_address_space, - input_pointer + AB::F::from_canonical_usize(CHUNK), - ), + MemoryAddress::new(data_address_space, input_pointer_2), right_input, - start_timestamp + AB::F::from_canonical_usize(3), + start_timestamp + AB::F::from_canonical_usize(4), &read_data_2, ) .eval(builder, simple); @@ -661,7 +680,7 @@ impl Air .write( MemoryAddress::new(data_address_space, output_pointer), left_output, - start_timestamp + AB::F::from_canonical_usize(4), + start_timestamp + AB::F::from_canonical_usize(5), &write_data_1, ) .eval(builder, simple); @@ -673,10 +692,10 @@ impl Air output_pointer + AB::F::from_canonical_usize(CHUNK), ), right_output, - start_timestamp + AB::F::from_canonical_usize(5), + start_timestamp + AB::F::from_canonical_usize(6), &write_data_2, ) - .eval(builder, simple); + .eval(builder, simple * is_permute); } } diff --git a/extensions/native/circuit/src/verify_batch/chip.rs b/extensions/native/circuit/src/verify_batch/chip.rs index 317dfcc648..e5dda23776 100644 --- a/extensions/native/circuit/src/verify_batch/chip.rs +++ b/extensions/native/circuit/src/verify_batch/chip.rs @@ -8,7 +8,10 @@ use openvm_circuit::{ }, }; use openvm_instructions::{ - instruction::Instruction, program::DEFAULT_PC_STEP, Poseidon2Opcode::PERM_POS2, VmOpcode, + instruction::Instruction, + program::DEFAULT_PC_STEP, + Poseidon2Opcode::{COMP_POS2, PERM_POS2}, + VmOpcode, }; use openvm_native_compiler::VerifyBatchOpcode::VERIFY_BATCH; use openvm_poseidon2_air::{Poseidon2Config, Poseidon2SubAir, Poseidon2SubChip}; @@ -102,18 +105,20 @@ pub struct CellRecord { #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(bound = "F: Field")] -pub struct SimplePermuteRecord { +pub struct SimplePoseidonRecord { pub from_state: ExecutionState, pub instruction: Instruction, - pub read_input_pointer: RecordId, + pub read_input_pointer_1: RecordId, + pub read_input_pointer_2: Option, pub read_output_pointer: RecordId, pub read_data_1: RecordId, pub read_data_2: RecordId, pub write_data_1: RecordId, - pub write_data_2: RecordId, + pub write_data_2: Option, - pub input_pointer: F, + pub input_pointer_1: F, + pub input_pointer_2: F, pub output_pointer: F, pub p2_input: [F; 2 * CHUNK], } @@ -122,7 +127,7 @@ pub struct SimplePermuteRecord { #[serde(bound = "F: Field")] pub struct VerifyBatchRecordSet { pub verify_batch_records: Vec>, - pub simple_permute_records: Vec>, + pub simple_permute_records: Vec>, } pub struct VerifyBatchChip { @@ -172,7 +177,7 @@ impl VerifyBatchChip VerifyBatchChip InstructionExecutor for VerifyBatchChip @@ -203,10 +209,13 @@ impl InstructionExecutor instruction: &Instruction, from_state: ExecutionState, ) -> Result, ExecutionError> { - if instruction.opcode == VmOpcode::with_default_offset(PERM_POS2) { + if instruction.opcode == VmOpcode::with_default_offset(PERM_POS2) + || instruction.opcode == VmOpcode::with_default_offset(COMP_POS2) + { let &Instruction { a: output_register, - b: input_register, + b: input_register_1, + c: input_register_2, d: register_address_space, e: data_address_space, .. @@ -214,13 +223,19 @@ impl InstructionExecutor let (read_output_pointer, output_pointer) = memory.read_cell(register_address_space, output_register); - let (read_input_pointer, input_pointer) = - memory.read_cell(register_address_space, input_register); - let (read_data_1, data_1) = memory.read::(data_address_space, input_pointer); - let (read_data_2, data_2) = memory.read::( - data_address_space, - input_pointer + F::from_canonical_usize(CHUNK), - ); + let (read_input_pointer_1, input_pointer_1) = + memory.read_cell(register_address_space, input_register_1); + let (read_input_pointer_2, input_pointer_2) = + if instruction.opcode == VmOpcode::with_default_offset(PERM_POS2) { + memory.increment_timestamp(); + (None, input_pointer_1 + F::from_canonical_usize(CHUNK)) + } else { + let (read_input_pointer_2, input_pointer_2) = + memory.read_cell(register_address_space, input_register_2); + (Some(read_input_pointer_2), input_pointer_2) + }; + let (read_data_1, data_1) = memory.read::(data_address_space, input_pointer_1); + let (read_data_2, data_2) = memory.read::(data_address_space, input_pointer_2); let p2_input = std::array::from_fn(|i| { if i < CHUNK { data_1[i] @@ -234,24 +249,40 @@ impl InstructionExecutor output_pointer, std::array::from_fn(|i| output[i]), ); - let (write_data_2, _) = memory.write::( - data_address_space, - output_pointer + F::from_canonical_usize(CHUNK), - std::array::from_fn(|i| output[CHUNK + i]), + let write_data_2 = if instruction.opcode == VmOpcode::with_default_offset(PERM_POS2) { + Some( + memory + .write::( + data_address_space, + output_pointer + F::from_canonical_usize(CHUNK), + std::array::from_fn(|i| output[CHUNK + i]), + ) + .0, + ) + } else { + memory.increment_timestamp(); + None + }; + + assert_eq!( + memory.timestamp(), + from_state.timestamp + NUM_SIMPLE_ACCESSES ); self.record_set .simple_permute_records - .push(SimplePermuteRecord { + .push(SimplePoseidonRecord { from_state, instruction: instruction.clone(), - read_input_pointer, + read_input_pointer_1, + read_input_pointer_2, read_output_pointer, read_data_1, read_data_2, write_data_1, write_data_2, - input_pointer, + input_pointer_1, + input_pointer_2, output_pointer, p2_input, }); @@ -508,10 +539,12 @@ impl InstructionExecutor fn get_opcode_name(&self, opcode: usize) -> String { if opcode == (VERIFY_BATCH as usize) + self.air.verify_batch_offset { return String::from("VERIFY_BATCH"); - } else if opcode == (PERM_POS2 as usize) + self.air.perm_pos2_offset { + } else if opcode == (PERM_POS2 as usize) + self.air.simple_offset { return String::from("PERM_POS2"); + } else if opcode == (COMP_POS2 as usize) + self.air.simple_offset { + return String::from("COMP_POS2"); } else { - unreachable!() + unreachable!("unsupported opcode: {}", opcode) } } } diff --git a/extensions/native/circuit/src/verify_batch/columns.rs b/extensions/native/circuit/src/verify_batch/columns.rs index 879257803b..dac82f93eb 100644 --- a/extensions/native/circuit/src/verify_batch/columns.rs +++ b/extensions/native/circuit/src/verify_batch/columns.rs @@ -37,7 +37,7 @@ pub struct VerifyBatchCols { pub specific: [T; max3( TopLevelSpecificCols::::width(), InsideRowSpecificCols::::width(), - SimplePermuteSpecificCols::::width(), + SimplePoseidonSpecificCols::::width(), )], } @@ -110,19 +110,23 @@ pub struct VerifyBatchCellCols { #[repr(C)] #[derive(AlignedBorrow, Copy, Clone)] -pub struct SimplePermuteSpecificCols { +pub struct SimplePoseidonSpecificCols { pub pc: T, - // instruction (a, b, d, e) + pub is_compress: T, + // instruction (a, b, c, d, e) pub output_register: T, - pub input_register: T, + pub input_register_1: T, + pub input_register_2: T, pub register_address_space: T, pub data_address_space: T, pub output_pointer: T, - pub input_pointer: T, + pub input_pointer_1: T, + pub input_pointer_2: T, pub read_output_pointer: MemoryReadAuxCols, - pub read_input_pointer: MemoryReadAuxCols, + pub read_input_pointer_1: MemoryReadAuxCols, + pub read_input_pointer_2: MemoryReadAuxCols, pub read_data_1: MemoryReadAuxCols, pub read_data_2: MemoryReadAuxCols, pub write_data_1: MemoryWriteAuxCols, diff --git a/extensions/native/circuit/src/verify_batch/tests.rs b/extensions/native/circuit/src/verify_batch/tests.rs index d367a28f7f..a76b38ce58 100644 --- a/extensions/native/circuit/src/verify_batch/tests.rs +++ b/extensions/native/circuit/src/verify_batch/tests.rs @@ -2,7 +2,7 @@ use std::cmp::min; use openvm_circuit::arch::testing::{memory::gen_pointer, VmChipTestBuilder, VmChipTester}; use openvm_instructions::{ - instruction::Instruction, Poseidon2Opcode, Poseidon2Opcode::PERM_POS2, UsizeOpcode, VmOpcode, + instruction::Instruction, Poseidon2Opcode, Poseidon2Opcode::*, UsizeOpcode, VmOpcode, }; use openvm_native_compiler::{VerifyBatchOpcode, VerifyBatchOpcode::VERIFY_BATCH}; use openvm_poseidon2_air::{Poseidon2Config, Poseidon2SubChip}; @@ -346,15 +346,20 @@ fn random_instructions(num_ops: usize) -> Vec> { let mut rng = create_seeded_rng(); (0..num_ops) .map(|_| { - let [a, b] = + let [a, b, c] = std::array::from_fn(|_| BabyBear::from_canonical_usize(gen_pointer(&mut rng, 1))); Instruction { opcode: VmOpcode::from_usize( - Poseidon2Opcode::PERM_POS2 as usize + Poseidon2Opcode::default_offset(), + if rng.gen_bool(0.5) { + PERM_POS2 + } else { + COMP_POS2 + } as usize + + Poseidon2Opcode::default_offset(), ), a, b, - c: BabyBear::ZERO, + c, d: BabyBear::ONE, e: BabyBear::TWO, f: BabyBear::ZERO, @@ -381,7 +386,9 @@ fn tester_with_random_poseidon2_ops(num_ops: usize) -> VmChipTester VmChipTester { + COMP_POS2 => { let data_left: [_; CHUNK] = std::array::from_fn(|i| data[i]); let data_right: [_; CHUNK] = std::array::from_fn(|i| data[CHUNK + i]); tester.write(e, lhs, data_left); tester.write(e, rhs, data_right); } - Poseidon2Opcode::PERM_POS2 => { + PERM_POS2 => { tester.write(e, lhs, data); } } @@ -421,12 +428,12 @@ fn tester_with_random_poseidon2_ops(num_ops: usize) -> VmChipTester { + COMP_POS2 => { let expected: [_; CHUNK] = std::array::from_fn(|i| hash[i]); let actual = tester.read::<{ CHUNK }>(e, dst); assert_eq!(expected, actual); } - Poseidon2Opcode::PERM_POS2 => { + PERM_POS2 => { let actual = tester.read::<{ 2 * CHUNK }>(e, dst); assert_eq!(hash, actual); } @@ -440,13 +447,19 @@ fn get_engine() -> BabyBearBlake3Engine { } #[test] -fn verify_batch_chip_simple_permute_1() { +fn verify_batch_chip_simple_1() { let tester = tester_with_random_poseidon2_ops(1); tester.test(get_engine).expect("Verification failed"); } #[test] -fn verify_batch_chip_simple_permute_50() { +fn verify_batch_chip_simple_3() { + let tester = tester_with_random_poseidon2_ops(3); + tester.test(get_engine).expect("Verification failed"); +} + +#[test] +fn verify_batch_chip_simple_50() { let tester = tester_with_random_poseidon2_ops(50); tester.test(get_engine).expect("Verification failed"); } diff --git a/extensions/native/circuit/src/verify_batch/trace.rs b/extensions/native/circuit/src/verify_batch/trace.rs index 7e6640e9a8..dc6e345467 100644 --- a/extensions/native/circuit/src/verify_batch/trace.rs +++ b/extensions/native/circuit/src/verify_batch/trace.rs @@ -2,6 +2,7 @@ use std::{borrow::BorrowMut, sync::Arc}; use openvm_circuit::system::memory::{MemoryAuxColsFactory, OfflineMemory}; use openvm_circuit_primitives::utils::next_power_of_two_or_zero; +use openvm_instructions::{instruction::Instruction, Poseidon2Opcode::COMP_POS2, VmOpcode}; use openvm_stark_backend::{ config::{StarkGenericConfig, Val}, p3_air::BaseAir, @@ -14,14 +15,15 @@ use openvm_stark_backend::{ }; use crate::{ - chip::{SimplePermuteRecord, NUM_INITIAL_READS}, + chip::{SimplePoseidonRecord, NUM_INITIAL_READS}, verify_batch::{ chip::{ CellRecord, IncorporateRowRecord, IncorporateSiblingRecord, InsideRowRecord, VerifyBatchChip, VerifyBatchRecord, }, columns::{ - InsideRowSpecificCols, SimplePermuteSpecificCols, TopLevelSpecificCols, VerifyBatchCols, + InsideRowSpecificCols, SimplePoseidonSpecificCols, TopLevelSpecificCols, + VerifyBatchCols, }, CHUNK, }, @@ -354,36 +356,41 @@ impl VerifyBatchChip, + record: &SimplePoseidonRecord, aux_cols_factory: &MemoryAuxColsFactory, slice: &mut [F], memory: &OfflineMemory, ) { - let &SimplePermuteRecord { + let &SimplePoseidonRecord { from_state, - read_input_pointer, + instruction: + Instruction { + opcode, + a: output_register, + b: input_register_1, + c: input_register_2, + d: register_address_space, + e: data_address_space, + .. + }, + read_input_pointer_1, + read_input_pointer_2, read_output_pointer, read_data_1, read_data_2, write_data_1, write_data_2, - input_pointer, + input_pointer_1, + input_pointer_2, output_pointer, p2_input, - .. } = record; - let output_register = record.instruction.a; - let input_register = record.instruction.b; - let register_address_space = record.instruction.d; - let data_address_space = record.instruction.e; - - let read_input_pointer = memory.record_by_id(read_input_pointer); + let read_input_pointer_1 = memory.record_by_id(read_input_pointer_1); let read_output_pointer = memory.record_by_id(read_output_pointer); let read_data_1 = memory.record_by_id(read_data_1); let read_data_2 = memory.record_by_id(read_data_2); let write_data_1 = memory.record_by_id(write_data_1); - let write_data_2 = memory.record_by_id(write_data_2); self.generate_subair_cols(p2_input, slice); let cols: &mut VerifyBatchCols = slice.borrow_mut(); @@ -396,24 +403,33 @@ impl VerifyBatchChip = - cols.specific[..SimplePermuteSpecificCols::::width()].borrow_mut(); - - *specific = SimplePermuteSpecificCols { - pc: F::from_canonical_u32(from_state.pc), - output_register, - input_register, - register_address_space, - data_address_space, - output_pointer, - input_pointer, - read_output_pointer: aux_cols_factory.make_read_aux_cols(read_output_pointer), - read_input_pointer: aux_cols_factory.make_read_aux_cols(read_input_pointer), - read_data_1: aux_cols_factory.make_read_aux_cols(read_data_1), - read_data_2: aux_cols_factory.make_read_aux_cols(read_data_2), - write_data_1: aux_cols_factory.make_write_aux_cols(write_data_1), - write_data_2: aux_cols_factory.make_write_aux_cols(write_data_2), - }; + let specific: &mut SimplePoseidonSpecificCols = + cols.specific[..SimplePoseidonSpecificCols::::width()].borrow_mut(); + + specific.pc = F::from_canonical_u32(from_state.pc); + specific.is_compress = F::from_bool(opcode == VmOpcode::with_default_offset(COMP_POS2)); + specific.output_register = output_register; + specific.input_register_1 = input_register_1; + specific.input_register_2 = input_register_2; + specific.register_address_space = register_address_space; + specific.data_address_space = data_address_space; + specific.output_pointer = output_pointer; + specific.input_pointer_1 = input_pointer_1; + specific.input_pointer_2 = input_pointer_2; + specific.read_output_pointer = aux_cols_factory.make_read_aux_cols(read_output_pointer); + specific.read_input_pointer_1 = aux_cols_factory.make_read_aux_cols(read_input_pointer_1); + specific.read_data_1 = aux_cols_factory.make_read_aux_cols(read_data_1); + specific.read_data_2 = aux_cols_factory.make_read_aux_cols(read_data_2); + specific.write_data_1 = aux_cols_factory.make_write_aux_cols(write_data_1); + + if opcode == VmOpcode::with_default_offset(COMP_POS2) { + let read_input_pointer_2 = memory.record_by_id(read_input_pointer_2.unwrap()); + specific.read_input_pointer_2 = + aux_cols_factory.make_read_aux_cols(read_input_pointer_2); + } else { + let write_data_2 = memory.record_by_id(write_data_2.unwrap()); + specific.write_data_2 = aux_cols_factory.make_write_aux_cols(write_data_2); + } } fn generate_trace(self) -> RowMajorMatrix { From 2c7059849c88d0492dc6acad8cbfe1c769a9ce8e Mon Sep 17 00:00:00 2001 From: TlatoaniHJ Date: Fri, 17 Jan 2025 11:36:36 -0500 Subject: [PATCH 40/50] Remove old Poseidon2 chip --- extensions/native/circuit/src/extension.rs | 5 +- extensions/native/circuit/src/lib.rs | 2 - .../native/circuit/src/poseidon2/air.rs | 179 -------------- .../native/circuit/src/poseidon2/chip.rs | 234 ------------------ .../native/circuit/src/poseidon2/columns.rs | 60 ----- .../native/circuit/src/poseidon2/mod.rs | 135 ---------- .../native/circuit/src/poseidon2/tests.rs | 136 ---------- .../native/circuit/src/poseidon2/trace.rs | 80 ------ 8 files changed, 2 insertions(+), 829 deletions(-) delete mode 100644 extensions/native/circuit/src/poseidon2/air.rs delete mode 100644 extensions/native/circuit/src/poseidon2/chip.rs delete mode 100644 extensions/native/circuit/src/poseidon2/columns.rs delete mode 100644 extensions/native/circuit/src/poseidon2/mod.rs delete mode 100644 extensions/native/circuit/src/poseidon2/tests.rs delete mode 100644 extensions/native/circuit/src/poseidon2/trace.rs diff --git a/extensions/native/circuit/src/extension.rs b/extensions/native/circuit/src/extension.rs index cd3e871906..ada54a5b16 100644 --- a/extensions/native/circuit/src/extension.rs +++ b/extensions/native/circuit/src/extension.rs @@ -17,9 +17,8 @@ use openvm_instructions::{ }; use openvm_native_compiler::{ CastfOpcode, FieldArithmeticOpcode, FieldExtensionOpcode, FriOpcode, NativeBranchEqualOpcode, - NativeJalOpcode, NativeLoadStoreOpcode, NativePhantom, VerifyBatchOpcode, - BLOCK_LOAD_STORE_SIZE, - NativeLoadStore4Opcode, + NativeJalOpcode, NativeLoadStore4Opcode, NativeLoadStoreOpcode, NativePhantom, + VerifyBatchOpcode, BLOCK_LOAD_STORE_SIZE, }; use openvm_poseidon2_air::Poseidon2Config; use openvm_rv32im_circuit::{ diff --git a/extensions/native/circuit/src/lib.rs b/extensions/native/circuit/src/lib.rs index 8916d17c4f..e9a110fe92 100644 --- a/extensions/native/circuit/src/lib.rs +++ b/extensions/native/circuit/src/lib.rs @@ -7,7 +7,6 @@ mod field_extension; mod fri; mod jal; mod loadstore; -// mod poseidon2; mod verify_batch; pub use branch_eq::*; @@ -17,7 +16,6 @@ pub use field_extension::*; pub use fri::*; pub use jal::*; pub use loadstore::*; -// pub use poseidon2::*; pub use verify_batch::*; mod extension; diff --git a/extensions/native/circuit/src/poseidon2/air.rs b/extensions/native/circuit/src/poseidon2/air.rs deleted file mode 100644 index 54b4304a1f..0000000000 --- a/extensions/native/circuit/src/poseidon2/air.rs +++ /dev/null @@ -1,179 +0,0 @@ -use std::{array::from_fn, borrow::Borrow, sync::Arc}; - -use derive_new::new; -use itertools::izip; -use openvm_circuit::{ - arch::ExecutionBridge, - system::memory::{offline_checker::MemoryBridge, MemoryAddress}, -}; -use openvm_poseidon2_air::{Poseidon2SubAir, BABY_BEAR_POSEIDON2_HALF_FULL_ROUNDS}; -use openvm_stark_backend::{ - air_builders::sub::SubAirBuilder, - interaction::InteractionBuilder, - p3_air::{Air, AirBuilder, BaseAir}, - p3_field::{Field, FieldAlgebra}, - p3_matrix::Matrix, - rap::{BaseAirWithPublicValues, PartitionedBaseAir}, -}; - -use super::{NativePoseidon2Cols, NATIVE_POSEIDON2_CHUNK_SIZE}; - -#[derive(Debug, new)] -pub struct NativePoseidon2Air { - pub(super) execution_bridge: ExecutionBridge, - pub(super) memory_bridge: MemoryBridge, - pub(super) subair: Arc>, - pub(super) offset: usize, -} - -impl BaseAir for NativePoseidon2Air { - fn width(&self) -> usize { - NativePoseidon2Cols::::width() - } -} -impl BaseAirWithPublicValues - for NativePoseidon2Air -{ -} -impl PartitionedBaseAir - for NativePoseidon2Air -{ -} - -impl Air - for NativePoseidon2Air -{ - fn eval(&self, builder: &mut AB) { - let mut sub_builder = - SubAirBuilder::, AB::F>::new( - builder, - 0..self.subair.width(), - ); - self.subair.eval(&mut sub_builder); - self.eval_memory_and_execution(builder); - } -} - -impl NativePoseidon2Air { - fn eval_memory_and_execution(&self, builder: &mut AB) { - let main = builder.main(); - let local = main.row_slice(0); - let cols: &NativePoseidon2Cols = (*local).borrow(); - - let timestamp = cols.memory.from_state.timestamp; - let mut timestamp_delta: usize = 0; - let mut timestamp_pp = || { - timestamp_delta += 1; - timestamp + AB::F::from_canonical_usize(timestamp_delta - 1) - }; - - // Because there are only two opcodes this adapter needs to handle, we make it so that - // is_valid == 1 if PERMUTE, is_valid == 2 if COMPRESS, and is_valid != 0 if valid. - let permute = -cols.memory.opcode_flag.into() * (cols.memory.opcode_flag - AB::F::TWO); - let compress = cols.memory.opcode_flag.into() - * (cols.memory.opcode_flag - AB::F::ONE) - * (AB::F::TWO.inverse()); - let is_valid = permute.clone() + compress.clone(); - builder.assert_zero( - cols.memory.opcode_flag - * (cols.memory.opcode_flag - AB::F::ONE) - * (cols.memory.opcode_flag - AB::F::TWO), - ); - builder - .when_ne(permute.clone(), AB::F::ONE) - .assert_eq(cols.memory.c, cols.memory.rs_ptr[1]); - - for (ptr, val, aux, count) in izip!( - [ - cols.memory.rd_ptr, - cols.memory.rs_ptr[0], - cols.memory.rs_ptr[1] - ], - [ - cols.memory.rd_val, - cols.memory.rs_val[0], - cols.memory.rs_val[1] - ], - &[ - cols.memory.rd_read_aux, - cols.memory.rs_read_aux[0], - cols.memory.rs_read_aux[1], - ], - [is_valid.clone(), is_valid.clone(), compress], - ) { - self.memory_bridge - .read( - MemoryAddress::new(cols.memory.ptr_as, ptr), - [val], - timestamp_pp(), - aux, - ) - .eval(builder, count); - } - - let read_chunk_1: [_; NATIVE_POSEIDON2_CHUNK_SIZE] = from_fn(|i| cols.inner.inputs[i]); - let read_chunk_2: [_; NATIVE_POSEIDON2_CHUNK_SIZE] = - from_fn(|i| cols.inner.inputs[i + NATIVE_POSEIDON2_CHUNK_SIZE]); - - for (ptr, data, aux, count) in izip!( - [cols.memory.rs_val[0], cols.memory.rs_val[1]], - [read_chunk_1, read_chunk_2], - &[cols.memory.chunk_read_aux[0], cols.memory.chunk_read_aux[1]], - [is_valid.clone(), is_valid.clone()], - ) { - self.memory_bridge - .read( - MemoryAddress::new(cols.memory.chunk_as, ptr), - data, - timestamp_pp(), - aux, - ) - .eval(builder, count); - } - - let write_chunk_1: [_; NATIVE_POSEIDON2_CHUNK_SIZE] = from_fn(|i| { - cols.inner.ending_full_rounds[BABY_BEAR_POSEIDON2_HALF_FULL_ROUNDS - 1].post[i] - }); - let write_chunk_2: [_; NATIVE_POSEIDON2_CHUNK_SIZE] = from_fn(|i| { - cols.inner.ending_full_rounds[BABY_BEAR_POSEIDON2_HALF_FULL_ROUNDS - 1].post - [i + NATIVE_POSEIDON2_CHUNK_SIZE] - }); - - for (ptr, data, aux, count) in izip!( - [ - cols.memory.rd_val.into(), - cols.memory.rd_val + AB::F::from_canonical_usize(NATIVE_POSEIDON2_CHUNK_SIZE) - ], - [write_chunk_1, write_chunk_2], - &[ - cols.memory.chunk_write_aux[0], - cols.memory.chunk_write_aux[1] - ], - [is_valid.clone(), permute], - ) { - self.memory_bridge - .write( - MemoryAddress::new(cols.memory.chunk_as, ptr), - data, - timestamp_pp(), - aux, - ) - .eval(builder, count); - } - - self.execution_bridge - .execute_and_increment_pc( - cols.memory.opcode_flag - AB::F::ONE + AB::F::from_canonical_usize(self.offset), - [ - cols.memory.rd_ptr, - cols.memory.rs_ptr[0], - cols.memory.c, - cols.memory.ptr_as, - cols.memory.chunk_as, - ], - cols.memory.from_state, - AB::F::from_canonical_usize(timestamp_delta), - ) - .eval(builder, is_valid); - } -} diff --git a/extensions/native/circuit/src/poseidon2/chip.rs b/extensions/native/circuit/src/poseidon2/chip.rs deleted file mode 100644 index e53975a0ec..0000000000 --- a/extensions/native/circuit/src/poseidon2/chip.rs +++ /dev/null @@ -1,234 +0,0 @@ -use std::{ - array::from_fn, - sync::{Arc, Mutex}, -}; - -use openvm_circuit::{ - arch::{ExecutionBridge, ExecutionBus, ExecutionError, ExecutionState, InstructionExecutor}, - system::{ - memory::{ - offline_checker::{MemoryBridge, MemoryReadAuxCols, MemoryWriteAuxCols}, - MemoryController, OfflineMemory, RecordId, - }, - program::ProgramBus, - }, -}; -use openvm_instructions::{ - instruction::Instruction, program::DEFAULT_PC_STEP, Poseidon2Opcode, UsizeOpcode, -}; -use openvm_poseidon2_air::{Poseidon2Config, Poseidon2SubChip}; -use openvm_stark_backend::{ - p3_field::{Field, PrimeField32}, - Stateful, -}; -use serde::{Deserialize, Serialize}; - -use super::{ - NativePoseidon2Air, NativePoseidon2MemoryCols, NATIVE_POSEIDON2_CHUNK_SIZE, - NATIVE_POSEIDON2_WIDTH, -}; - -pub struct NativePoseidon2BaseChip { - pub air: Arc>, - pub subchip: Poseidon2SubChip, - pub records: Vec>>, - pub offline_memory: Arc>>, -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct NativePoseidon2ChipRecord { - pub from_state: ExecutionState, - pub opcode: Poseidon2Opcode, - pub input: [F; NATIVE_POSEIDON2_WIDTH], - pub c: F, - pub rd: RecordId, - pub rs1: RecordId, - pub rs2: Option, - - pub read1: RecordId, - pub read2: RecordId, - - pub write1: RecordId, - pub write2: Option, -} - -impl NativePoseidon2BaseChip { - pub fn new( - execution_bus: ExecutionBus, - program_bus: ProgramBus, - memory_bridge: MemoryBridge, - poseidon2_config: Poseidon2Config, - offset: usize, - offline_memory: Arc>>, - ) -> Self { - let subchip = Poseidon2SubChip::new(poseidon2_config.constants); - Self { - air: Arc::new(NativePoseidon2Air::new( - ExecutionBridge::new(execution_bus, program_bus), - memory_bridge, - subchip.air.clone(), - offset, - )), - subchip, - records: vec![], - offline_memory, - } - } -} - -impl InstructionExecutor - for NativePoseidon2BaseChip -{ - fn execute( - &mut self, - memory: &mut MemoryController, - instruction: &Instruction, - from_state: ExecutionState, - ) -> Result, ExecutionError> { - let &Instruction { - opcode, - a, - b, - c, - d, - e, - .. - } = instruction; - let local_opcode = Poseidon2Opcode::from_usize(opcode.local_opcode_idx(self.air.offset)); - - let rd = memory.read_cell(d, a); - let rs1 = memory.read_cell(d, b); - let rs2 = match local_opcode { - Poseidon2Opcode::PERM_POS2 => { - memory.increment_timestamp(); - None - } - Poseidon2Opcode::COMP_POS2 => Some(memory.read_cell(d, c)), - }; - - let read1 = memory.read::(e, rs1.1); - let read2 = memory.read::( - e, - match rs2 { - Some(rs2) => rs2.1, - None => rs1.1 + F::from_canonical_usize(NATIVE_POSEIDON2_CHUNK_SIZE), - }, - ); - - let mut input_state: [F; NATIVE_POSEIDON2_WIDTH] = [F::ZERO; NATIVE_POSEIDON2_WIDTH]; - input_state[..NATIVE_POSEIDON2_CHUNK_SIZE].copy_from_slice(&read1.1); - input_state[NATIVE_POSEIDON2_CHUNK_SIZE..].copy_from_slice(&read2.1); - - let output_state = self.subchip.permute(input_state); - - let output1: [F; NATIVE_POSEIDON2_CHUNK_SIZE] = from_fn(|i| output_state[i]); - let output2: [F; NATIVE_POSEIDON2_CHUNK_SIZE] = - from_fn(|i| output_state[NATIVE_POSEIDON2_CHUNK_SIZE + i]); - - let (write1, _) = memory.write::(e, rd.1, output1); - let write2 = match local_opcode { - Poseidon2Opcode::PERM_POS2 => Some( - memory - .write::( - e, - rd.1 + F::from_canonical_usize(NATIVE_POSEIDON2_CHUNK_SIZE), - output2, - ) - .0, - ), - Poseidon2Opcode::COMP_POS2 => { - memory.increment_timestamp(); - None - } - }; - - self.records.push(Some(NativePoseidon2ChipRecord { - from_state, - opcode: local_opcode, - input: input_state, - c, - rd: rd.0, - rs1: rs1.0, - rs2: rs2.map(|rs2| rs2.0), - read1: read1.0, - read2: read2.0, - write1, - write2, - })); - - Ok(ExecutionState { - pc: from_state.pc + DEFAULT_PC_STEP, - timestamp: memory.timestamp(), - }) - } - - fn get_opcode_name(&self, opcode: usize) -> String { - format!( - "{:?}", - Poseidon2Opcode::from_usize(opcode - self.air.offset) - ) - } -} - -impl NativePoseidon2ChipRecord { - pub fn to_memory_cols(&self, memory: &OfflineMemory) -> NativePoseidon2MemoryCols { - let aux_cols_factory = memory.aux_cols_factory(); - let rs1 = memory.record_by_id(self.rs1); - let (rs2_ptr, rs2_val, rs2_read_aux) = match self.rs2 { - Some(rs2) => { - let rs2 = memory.record_by_id(rs2); - ( - rs2.pointer, - rs2.data[0], - aux_cols_factory.make_read_aux_cols(rs2), - ) - } - None => ( - F::ZERO, - rs1.data[0] + F::from_canonical_usize(NATIVE_POSEIDON2_CHUNK_SIZE), - MemoryReadAuxCols::disabled(), - ), - }; - let rd = memory.record_by_id(self.rd); - let read1 = memory.record_by_id(self.read1); - let read2 = memory.record_by_id(self.read2); - NativePoseidon2MemoryCols { - from_state: self.from_state.map(F::from_canonical_u32), - opcode_flag: match self.opcode { - Poseidon2Opcode::PERM_POS2 => F::ONE, - Poseidon2Opcode::COMP_POS2 => F::TWO, - }, - ptr_as: rd.address_space, - chunk_as: read1.address_space, - c: self.c, - rs_ptr: [rs1.pointer, rs2_ptr], - rd_ptr: rd.pointer, - rs_val: [rs1.data[0], rs2_val], - rd_val: rd.data[0], - rs_read_aux: [aux_cols_factory.make_read_aux_cols(rs1), rs2_read_aux], - rd_read_aux: aux_cols_factory.make_read_aux_cols(rd), - chunk_read_aux: [ - aux_cols_factory.make_read_aux_cols(read1), - aux_cols_factory.make_read_aux_cols(read2), - ], - chunk_write_aux: [ - aux_cols_factory.make_write_aux_cols(memory.record_by_id(self.write1)), - self.write2.map_or(MemoryWriteAuxCols::disabled(), |w| { - aux_cols_factory.make_write_aux_cols(memory.record_by_id(w)) - }), - ], - } - } -} - -impl Stateful> - for NativePoseidon2BaseChip -{ - fn load_state(&mut self, state: Vec) { - self.records = bitcode::deserialize(&state).unwrap() - } - - fn store_state(&self) -> Vec { - bitcode::serialize(&self.records).unwrap() - } -} diff --git a/extensions/native/circuit/src/poseidon2/columns.rs b/extensions/native/circuit/src/poseidon2/columns.rs deleted file mode 100644 index 12b591a761..0000000000 --- a/extensions/native/circuit/src/poseidon2/columns.rs +++ /dev/null @@ -1,60 +0,0 @@ -use openvm_circuit::{ - arch::ExecutionState, - system::memory::offline_checker::{MemoryReadAuxCols, MemoryWriteAuxCols}, -}; -use openvm_circuit_primitives::AlignedBorrow; -use openvm_poseidon2_air::Poseidon2SubCols; -use openvm_stark_backend::p3_field::FieldAlgebra; - -use super::NATIVE_POSEIDON2_CHUNK_SIZE; - -#[repr(C)] -#[derive(AlignedBorrow)] -pub struct NativePoseidon2Cols { - pub inner: Poseidon2SubCols, - pub memory: NativePoseidon2MemoryCols, -} - -#[repr(C)] -#[derive(Clone, Debug, AlignedBorrow)] -pub struct NativePoseidon2MemoryCols { - pub from_state: ExecutionState, - // 1 if PERMUTE, 2 if COMPRESS, 0 otherwise - pub opcode_flag: T, - - pub ptr_as: T, - pub chunk_as: T, - - // rs_ptr[1] if COMPRESS, original value of instruction field c if PERMUTE - pub c: T, - - pub rs_ptr: [T; 2], - pub rd_ptr: T, - pub rs_val: [T; 2], - pub rd_val: T, - pub rs_read_aux: [MemoryReadAuxCols; 2], - pub rd_read_aux: MemoryReadAuxCols, - - pub chunk_read_aux: [MemoryReadAuxCols; 2], - pub chunk_write_aux: [MemoryWriteAuxCols; 2], -} - -impl NativePoseidon2MemoryCols { - pub fn blank() -> Self { - Self { - from_state: ExecutionState::default(), - opcode_flag: F::ZERO, - ptr_as: F::ZERO, - chunk_as: F::ZERO, - c: F::ZERO, - rs_ptr: [F::ZERO; 2], - rd_ptr: F::ZERO, - rs_val: [F::ZERO; 2], - rd_val: F::ZERO, - rs_read_aux: [MemoryReadAuxCols::disabled(); 2], - rd_read_aux: MemoryReadAuxCols::disabled(), - chunk_read_aux: [MemoryReadAuxCols::disabled(); 2], - chunk_write_aux: [MemoryWriteAuxCols::disabled(); 2], - } - } -} diff --git a/extensions/native/circuit/src/poseidon2/mod.rs b/extensions/native/circuit/src/poseidon2/mod.rs deleted file mode 100644 index 8f33d6aa9e..0000000000 --- a/extensions/native/circuit/src/poseidon2/mod.rs +++ /dev/null @@ -1,135 +0,0 @@ -use std::sync::{Arc, Mutex}; - -pub use columns::*; -use openvm_circuit::{ - arch::{ExecutionBus, ExecutionError, ExecutionState, InstructionExecutor}, - system::{ - memory::{offline_checker::MemoryBridge, MemoryController, OfflineMemory}, - program::ProgramBus, - }, -}; -use openvm_circuit_primitives_derive::BytesStateful; -use openvm_instructions::instruction::Instruction; -use openvm_poseidon2_air::Poseidon2Config; -use openvm_stark_backend::{ - config::{StarkGenericConfig, Val}, - p3_field::{Field, PrimeField32}, - prover::types::AirProofInput, - rap::AnyRap, - Chip, ChipUsageGetter, -}; - -mod air; -pub use air::*; -mod chip; -pub use chip::*; -mod columns; - -mod trace; - -#[cfg(test)] -mod tests; - -pub const NATIVE_POSEIDON2_WIDTH: usize = 16; -pub const NATIVE_POSEIDON2_CHUNK_SIZE: usize = 8; - -#[derive(BytesStateful)] -pub enum NativePoseidon2Chip { - Register0(NativePoseidon2BaseChip), - Register1(NativePoseidon2BaseChip), -} - -impl NativePoseidon2Chip { - pub fn new( - execution_bus: ExecutionBus, - program_bus: ProgramBus, - memory_bridge: MemoryBridge, - poseidon2_config: Poseidon2Config, - offset: usize, - max_constraint_degree: usize, - offline_memory: Arc>>, - ) -> Self { - if max_constraint_degree >= 7 { - Self::Register0(NativePoseidon2BaseChip::new( - execution_bus, - program_bus, - memory_bridge, - poseidon2_config, - offset, - offline_memory, - )) - } else { - Self::Register1(NativePoseidon2BaseChip::new( - execution_bus, - program_bus, - memory_bridge, - poseidon2_config, - offset, - offline_memory, - )) - } - } -} - -impl InstructionExecutor for NativePoseidon2Chip { - fn execute( - &mut self, - memory: &mut MemoryController, - instruction: &Instruction, - from_state: ExecutionState, - ) -> Result, ExecutionError> { - match self { - NativePoseidon2Chip::Register0(chip) => chip.execute(memory, instruction, from_state), - NativePoseidon2Chip::Register1(chip) => chip.execute(memory, instruction, from_state), - } - } - - fn get_opcode_name(&self, opcode: usize) -> String { - match self { - NativePoseidon2Chip::Register0(chip) => chip.get_opcode_name(opcode), - NativePoseidon2Chip::Register1(chip) => chip.get_opcode_name(opcode), - } - } -} - -impl Chip for NativePoseidon2Chip> -where - Val: PrimeField32, -{ - fn air(&self) -> Arc> { - match self { - NativePoseidon2Chip::Register0(chip) => chip.air(), - NativePoseidon2Chip::Register1(chip) => chip.air(), - } - } - - fn generate_air_proof_input(self) -> AirProofInput { - match self { - NativePoseidon2Chip::Register0(chip) => chip.generate_air_proof_input(), - NativePoseidon2Chip::Register1(chip) => chip.generate_air_proof_input(), - } - } -} - -impl ChipUsageGetter for NativePoseidon2Chip { - fn air_name(&self) -> String { - match self { - NativePoseidon2Chip::Register0(chip) => chip.air_name(), - NativePoseidon2Chip::Register1(chip) => chip.air_name(), - } - } - - fn current_trace_height(&self) -> usize { - match self { - NativePoseidon2Chip::Register0(chip) => chip.current_trace_height(), - NativePoseidon2Chip::Register1(chip) => chip.current_trace_height(), - } - } - - fn trace_width(&self) -> usize { - match self { - NativePoseidon2Chip::Register0(chip) => chip.trace_width(), - NativePoseidon2Chip::Register1(chip) => chip.trace_width(), - } - } -} diff --git a/extensions/native/circuit/src/poseidon2/tests.rs b/extensions/native/circuit/src/poseidon2/tests.rs deleted file mode 100644 index 7614b281e5..0000000000 --- a/extensions/native/circuit/src/poseidon2/tests.rs +++ /dev/null @@ -1,136 +0,0 @@ -use openvm_circuit::arch::testing::{memory::gen_pointer, VmChipTestBuilder, VmChipTester}; -use openvm_instructions::{instruction::Instruction, Poseidon2Opcode, UsizeOpcode, VmOpcode}; -use openvm_poseidon2_air::Poseidon2Config; -use openvm_stark_backend::p3_field::{FieldAlgebra, PrimeField64}; -use openvm_stark_sdk::{ - config::{ - baby_bear_blake3::{BabyBearBlake3Config, BabyBearBlake3Engine}, - fri_params::standard_fri_params_with_100_bits_conjectured_security, - }, - engine::StarkFriEngine, - p3_baby_bear::BabyBear, - utils::create_seeded_rng, -}; -use rand::Rng; - -use super::{NativePoseidon2Chip, NATIVE_POSEIDON2_CHUNK_SIZE, NATIVE_POSEIDON2_WIDTH}; - -/// Create random instructions for the poseidon2 chip. -fn random_instructions(num_ops: usize) -> Vec> { - let mut rng = create_seeded_rng(); - (0..num_ops) - .map(|_| { - let [a, b, c] = - std::array::from_fn(|_| BabyBear::from_canonical_usize(gen_pointer(&mut rng, 1))); - Instruction { - opcode: if rng.gen_bool(0.5) { - VmOpcode::from_usize(Poseidon2Opcode::PERM_POS2 as usize) - } else { - VmOpcode::from_usize(Poseidon2Opcode::COMP_POS2 as usize) - }, - a, - b, - c, - d: BabyBear::ONE, - e: BabyBear::TWO, - f: BabyBear::ZERO, - g: BabyBear::ZERO, - } - }) - .collect() -} - -fn tester_with_random_poseidon2_ops( - num_ops: usize, - max_constraint_degree: usize, -) -> VmChipTester { - let elem_range = || 1..=100; - - let mut tester = VmChipTestBuilder::default(); - let mut chip = NativePoseidon2Chip::new( - tester.execution_bus(), - tester.program_bus(), - tester.memory_bridge(), - Poseidon2Config::default(), - 0, - max_constraint_degree, - tester.offline_memory_mutex_arc(), - ); - - let mut rng = create_seeded_rng(); - - for instruction in random_instructions(num_ops) { - let opcode = Poseidon2Opcode::from_usize(instruction.opcode.as_usize()); - let [a, b, c, d, e] = [ - instruction.a, - instruction.b, - instruction.c, - instruction.d, - instruction.e, - ] - .map(|elem| elem.as_canonical_u64() as usize); - - let dst = gen_pointer(&mut rng, NATIVE_POSEIDON2_CHUNK_SIZE); - let lhs = gen_pointer(&mut rng, NATIVE_POSEIDON2_CHUNK_SIZE); - let rhs = gen_pointer(&mut rng, NATIVE_POSEIDON2_CHUNK_SIZE); - - let data: [_; NATIVE_POSEIDON2_WIDTH] = - std::array::from_fn(|_| BabyBear::from_canonical_usize(rng.gen_range(elem_range()))); - - let hash = match &chip { - NativePoseidon2Chip::Register0(chip) => chip.subchip.permute(data), - NativePoseidon2Chip::Register1(chip) => chip.subchip.permute(data), - }; - - tester.write_cell(d, a, BabyBear::from_canonical_usize(dst)); - tester.write_cell(d, b, BabyBear::from_canonical_usize(lhs)); - if opcode == Poseidon2Opcode::COMP_POS2 { - tester.write_cell(d, c, BabyBear::from_canonical_usize(rhs)); - } - - match opcode { - Poseidon2Opcode::COMP_POS2 => { - let data_left: [_; NATIVE_POSEIDON2_CHUNK_SIZE] = std::array::from_fn(|i| data[i]); - let data_right: [_; NATIVE_POSEIDON2_CHUNK_SIZE] = - std::array::from_fn(|i| data[NATIVE_POSEIDON2_CHUNK_SIZE + i]); - tester.write(e, lhs, data_left); - tester.write(e, rhs, data_right); - } - Poseidon2Opcode::PERM_POS2 => { - tester.write(e, lhs, data); - } - } - - tester.execute(&mut chip, &instruction); - - match opcode { - Poseidon2Opcode::COMP_POS2 => { - let expected: [_; NATIVE_POSEIDON2_CHUNK_SIZE] = std::array::from_fn(|i| hash[i]); - let actual = tester.read::(e, dst); - assert_eq!(expected, actual); - } - Poseidon2Opcode::PERM_POS2 => { - let actual = tester.read::(e, dst); - assert_eq!(hash, actual); - } - } - } - tester.build().load(chip).finalize() -} - -fn get_engine() -> BabyBearBlake3Engine { - BabyBearBlake3Engine::new(standard_fri_params_with_100_bits_conjectured_security(3)) -} - -/// Checking that 50 random instructions pass. -#[test] -fn poseidon2_chip_random_max_constraint_degree_7() { - let tester = tester_with_random_poseidon2_ops(50, 7); - tester.test(get_engine).expect("Verification failed"); -} - -#[test] -fn poseidon2_chip_random_max_constraint_degree_3() { - let tester = tester_with_random_poseidon2_ops(50, 3); - tester.test(get_engine).expect("Verification failed"); -} diff --git a/extensions/native/circuit/src/poseidon2/trace.rs b/extensions/native/circuit/src/poseidon2/trace.rs deleted file mode 100644 index 05d096819a..0000000000 --- a/extensions/native/circuit/src/poseidon2/trace.rs +++ /dev/null @@ -1,80 +0,0 @@ -use std::{borrow::BorrowMut, iter::repeat, sync::Arc}; - -use openvm_stark_backend::{ - config::{StarkGenericConfig, Val}, - p3_air::BaseAir, - p3_field::{FieldAlgebra, PrimeField32}, - p3_matrix::dense::RowMajorMatrix, - p3_maybe_rayon::prelude::*, - prover::types::AirProofInput, - rap::{get_air_name, AnyRap}, - Chip, ChipUsageGetter, -}; - -use super::{ - NativePoseidon2BaseChip, NativePoseidon2Cols, NativePoseidon2MemoryCols, NATIVE_POSEIDON2_WIDTH, -}; - -impl Chip - for NativePoseidon2BaseChip, SBOX_REGISTERS> -where - Val: PrimeField32, -{ - fn air(&self) -> Arc> { - self.air.clone() - } - - fn generate_air_proof_input(self) -> AirProofInput { - let air = self.air(); - let height = self.current_trace_height().next_power_of_two(); - let width = self.trace_width(); - let mut records = self.records; - records.extend(repeat(None).take(height - records.len())); - - let inputs = records - .par_iter() - .map(|record| match record { - Some(record) => record.input, - None => [Val::::ZERO; NATIVE_POSEIDON2_WIDTH], - }) - .collect(); - let inner_trace = self.subchip.generate_trace(inputs); - let inner_width = self.air.subair.width(); - - let memory = self.offline_memory.lock().unwrap(); - let memory_cols = records.par_iter().map(|record| match record { - Some(record) => record.to_memory_cols(&memory), - None => NativePoseidon2MemoryCols::blank(), - }); - - let mut values = Val::::zero_vec(height * width); - values - .par_chunks_mut(width) - .zip(inner_trace.values.par_chunks(inner_width)) - .zip(memory_cols) - .for_each(|((row, inner_row), memory_cols)| { - // WARNING: Poseidon2SubCols must be the first field in NativePoseidon2Cols - row[..inner_width].copy_from_slice(inner_row); - let cols: &mut NativePoseidon2Cols, SBOX_REGISTERS> = row.borrow_mut(); - cols.memory = memory_cols; - }); - - AirProofInput::simple_no_pis(air, RowMajorMatrix::new(values, width)) - } -} - -impl ChipUsageGetter - for NativePoseidon2BaseChip -{ - fn air_name(&self) -> String { - get_air_name(&self.air) - } - - fn current_trace_height(&self) -> usize { - self.records.len() - } - - fn trace_width(&self) -> usize { - self.air.width() - } -} From ae3a551edc3f68d530b8e4909bd8bcec50f0ff7f Mon Sep 17 00:00:00 2001 From: TlatoaniHJ Date: Fri, 17 Jan 2025 11:41:03 -0500 Subject: [PATCH 41/50] Fix magical test --- crates/vm/tests/integration_test.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/crates/vm/tests/integration_test.rs b/crates/vm/tests/integration_test.rs index 13add4526f..ad77b22786 100644 --- a/crates/vm/tests/integration_test.rs +++ b/crates/vm/tests/integration_test.rs @@ -191,7 +191,6 @@ fn test_vm_override_executor_height() { (ChipId::Executor(7), 0), (ChipId::Executor(8), 0), (ChipId::Executor(9), 0), - (ChipId::Executor(10), 0), ] .into_iter() .collect(), @@ -217,7 +216,6 @@ fn test_vm_override_executor_height() { (ChipId::Executor(7), 2048), (ChipId::Executor(8), 4096), (ChipId::Executor(9), 8192), - (ChipId::Executor(10), 16384), ] .into_iter() .collect(), @@ -242,7 +240,7 @@ fn test_vm_override_executor_height() { // heights by hands whenever something changes. assert_eq!( air_heights, - vec![2, 2, 16, 1, 8, 4, 2, 16384, 8192, 4096, 2048, 1024, 512, 256, 128, 64, 32, 262144] + vec![2, 2, 16, 1, 8, 4, 2, 8192, 4096, 2048, 1024, 512, 256, 128, 64, 32, 262144] ); } From a724e49708a6036c9123999f1f56e7660fd1f435 Mon Sep 17 00:00:00 2001 From: TlatoaniHJ Date: Fri, 17 Jan 2025 11:48:53 -0500 Subject: [PATCH 42/50] Fix lint --- .../native/circuit/src/verify_batch/air.rs | 5 ++-- .../native/circuit/src/verify_batch/chip.rs | 16 ++++++------- .../native/circuit/src/verify_batch/trace.rs | 23 ++++++++++--------- 3 files changed, 23 insertions(+), 21 deletions(-) diff --git a/extensions/native/circuit/src/verify_batch/air.rs b/extensions/native/circuit/src/verify_batch/air.rs index 9b0a6d8eb1..556ad52003 100644 --- a/extensions/native/circuit/src/verify_batch/air.rs +++ b/extensions/native/circuit/src/verify_batch/air.rs @@ -172,11 +172,11 @@ impl Air // right input - for i in 0..CHUNK { + for &next_right_input in next_right_input.iter() { builder .when(end.clone()) .when(next.inside_row) - .assert_zero(next_right_input[i]); + .assert_zero(next_right_input); } for i in 0..CHUNK { @@ -700,6 +700,7 @@ impl Air } impl VerifyBatchBus { + #[allow(clippy::too_many_arguments)] pub fn interact( &self, builder: &mut AB, diff --git a/extensions/native/circuit/src/verify_batch/chip.rs b/extensions/native/circuit/src/verify_batch/chip.rs index e5dda23776..abe9e2831f 100644 --- a/extensions/native/circuit/src/verify_batch/chip.rs +++ b/extensions/native/circuit/src/verify_batch/chip.rs @@ -149,7 +149,7 @@ impl Stateful> if let Some(incorporate_row) = &top_level.incorporate_row { self.height += 1 + incorporate_row.chunks.len(); } - if let Some(_) = &top_level.incorporate_sibling { + if top_level.incorporate_sibling.is_some() { self.height += 1; } } @@ -354,7 +354,7 @@ impl InstructionExecutor loop { let mut cells = vec![]; - for i in 0..CHUNK { + for chunk_elem in rolling_hash.iter_mut().take(CHUNK) { let read_row_pointer_and_length = if is_first_in_segment || row_pointer == row_end { @@ -393,7 +393,7 @@ impl InstructionExecutor row_pointer, row_end, }); - rolling_hash[i] = value; + *chunk_elem = value; row_pointer += 1; } if cells.is_empty() { @@ -470,12 +470,12 @@ impl InstructionExecutor let mut sibling = [F::ZERO; CHUNK]; let mut reads = vec![]; - for i in 0..CHUNK { + for (i, sibling_elem) in sibling.iter_mut().enumerate().take(CHUNK) { let (read, value) = memory.read_cell( address_space, F::from_canonical_usize(sibling_array_start + i), ); - sibling[i] = value; + *sibling_elem = value; reads.push(read); } @@ -538,11 +538,11 @@ impl InstructionExecutor fn get_opcode_name(&self, opcode: usize) -> String { if opcode == (VERIFY_BATCH as usize) + self.air.verify_batch_offset { - return String::from("VERIFY_BATCH"); + String::from("VERIFY_BATCH") } else if opcode == (PERM_POS2 as usize) + self.air.simple_offset { - return String::from("PERM_POS2"); + String::from("PERM_POS2") } else if opcode == (COMP_POS2 as usize) + self.air.simple_offset { - return String::from("COMP_POS2"); + String::from("COMP_POS2") } else { unreachable!("unsupported opcode: {}", opcode) } diff --git a/extensions/native/circuit/src/verify_batch/trace.rs b/extensions/native/circuit/src/verify_batch/trace.rs index dc6e345467..ae21bf19e2 100644 --- a/extensions/native/circuit/src/verify_batch/trace.rs +++ b/extensions/native/circuit/src/verify_batch/trace.rs @@ -47,8 +47,9 @@ impl VerifyBatchChip, @@ -158,6 +159,7 @@ impl VerifyBatchChip, @@ -221,6 +223,7 @@ impl VerifyBatchChip, @@ -295,17 +298,16 @@ impl VerifyBatchChip::width(); let mut used_cells = 0; - let mut proof_index = 0; let mut height = record.initial_height; let mut opened_index = 0; - for top_level in record.top_level.iter() { + for (proof_index, top_level) in record.top_level.iter().enumerate() { if let Some(incorporate_row) = &top_level.incorporate_row { self.incorporate_row_record_to_row( - &incorporate_row, + incorporate_row, aux_cols_factory, &mut slice[used_cells..used_cells + width], memory, - &record, + record, proof_index, height, ); @@ -314,11 +316,11 @@ impl VerifyBatchChip VerifyBatchChip VerifyBatchChip Date: Fri, 17 Jan 2025 12:24:46 -0500 Subject: [PATCH 43/50] Document VERIFY_BATCH opcode --- docs/specs/ISA.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/specs/ISA.md b/docs/specs/ISA.md index 18d7cd00fc..881bc22bf0 100644 --- a/docs/specs/ISA.md +++ b/docs/specs/ISA.md @@ -490,6 +490,15 @@ and only support `CHUNK = 8` and `WIDTH = 16` in BabyBear Poseidon2 above. For t is read in two batches of size `CHUNK`, and, similarly, the output is written in either one or two batches of size `CHUNK`, depending on the output size of the corresponding opcode. +### Verification + +We have the following special opcode tailored to optimize verification. Due to already having a large number of operands, +the address space is fixed to be `AS::Native = 5`. + +| Name | Operands | Description | +|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| **VERIFY_BATCH** `[CHUNK, PID]`

Here `CHUNK` and `PID` are **constants** that determine different opcodes. `PID` is an internal identifier for particular Poseidon2 constants dependent on the field (see below). | `a,b,c,d,e,f,g` | Computes `mmcs::verify_batch`. In the native address space, `[a], [b], [d], [e], [f]` should be the array start pointers for the dimensions array, the opened values array (which contains more arrays), the proof (which contains arrays of length `CHUNK`) and the commitment (which is an array of length `CHUNK`). `[c]` should be the length of the opened values array (and so should be equal to the length of the dimensions array as well). `g` should be the reciprocal of the size (in field elements) of the values contained in the opened values array: if the opened values array contains field elements, `g` should be 1; if the opened values array contains extension field elements, `g` should be 1/4. | + ## Phantom Sub-Instructions As mentioned in [System](#system), the **PHANTOM** instruction has different behavior based on the operand `c`. From 54825668221a0f42ebe740b80f0bda1b0afbbc79 Mon Sep 17 00:00:00 2001 From: TlatoaniHJ Date: Fri, 17 Jan 2025 12:54:58 -0500 Subject: [PATCH 44/50] Rename VerifyBatchChip to NativePoseidon2Chip --- extensions/native/circuit/src/extension.rs | 6 ++--- extensions/native/circuit/src/lib.rs | 4 +-- .../src/{verify_batch => poseidon2}/air.rs | 22 ++++++++-------- .../src/{verify_batch => poseidon2}/chip.rs | 20 +++++++------- .../{verify_batch => poseidon2}/columns.rs | 4 +-- .../native/circuit/src/poseidon2/mod.rs | 8 ++++++ .../src/{verify_batch => poseidon2}/tests.rs | 6 ++--- .../src/{verify_batch => poseidon2}/trace.rs | 26 +++++++++---------- .../native/circuit/src/verify_batch/mod.rs | 10 ------- extensions/native/recursion/src/fri/mod.rs | 2 +- .../native/recursion/src/fri/two_adic_pcs.rs | 4 +-- 11 files changed, 55 insertions(+), 57 deletions(-) rename extensions/native/circuit/src/{verify_batch => poseidon2}/air.rs (97%) rename extensions/native/circuit/src/{verify_batch => poseidon2}/chip.rs (97%) rename extensions/native/circuit/src/{verify_batch => poseidon2}/columns.rs (97%) create mode 100644 extensions/native/circuit/src/poseidon2/mod.rs rename extensions/native/circuit/src/{verify_batch => poseidon2}/tests.rs (98%) rename extensions/native/circuit/src/{verify_batch => poseidon2}/trace.rs (95%) delete mode 100644 extensions/native/circuit/src/verify_batch/mod.rs diff --git a/extensions/native/circuit/src/extension.rs b/extensions/native/circuit/src/extension.rs index ada54a5b16..f37ccd2948 100644 --- a/extensions/native/circuit/src/extension.rs +++ b/extensions/native/circuit/src/extension.rs @@ -31,7 +31,7 @@ use strum::IntoEnumIterator; use crate::{ adapters::{convert_adapter::ConvertAdapterChip, *}, - chip::VerifyBatchChip, + chip::NativePoseidon2Chip, phantom::*, *, }; @@ -87,7 +87,7 @@ pub enum NativeExecutor { FieldArithmetic(FieldArithmeticChip), FieldExtension(FieldExtensionChip), FriReducedOpening(FriReducedOpeningChip), - VerifyBatch(VerifyBatchChip), + VerifyBatch(NativePoseidon2Chip), } #[derive(From, ChipUsageGetter, Chip, AnyEnum, BytesStateful)] @@ -199,7 +199,7 @@ impl VmExtension for Native { FriOpcode::iter().map(VmOpcode::with_default_offset), )?; - let verify_batch_chip = VerifyBatchChip::new( + let verify_batch_chip = NativePoseidon2Chip::new( execution_bus, program_bus, memory_bridge, diff --git a/extensions/native/circuit/src/lib.rs b/extensions/native/circuit/src/lib.rs index e9a110fe92..46c6bc890f 100644 --- a/extensions/native/circuit/src/lib.rs +++ b/extensions/native/circuit/src/lib.rs @@ -7,7 +7,7 @@ mod field_extension; mod fri; mod jal; mod loadstore; -mod verify_batch; +mod poseidon2; pub use branch_eq::*; pub use castf::*; @@ -16,7 +16,7 @@ pub use field_extension::*; pub use fri::*; pub use jal::*; pub use loadstore::*; -pub use verify_batch::*; +pub use poseidon2::*; mod extension; pub use extension::*; diff --git a/extensions/native/circuit/src/verify_batch/air.rs b/extensions/native/circuit/src/poseidon2/air.rs similarity index 97% rename from extensions/native/circuit/src/verify_batch/air.rs rename to extensions/native/circuit/src/poseidon2/air.rs index 556ad52003..b1fc0f7657 100644 --- a/extensions/native/circuit/src/verify_batch/air.rs +++ b/extensions/native/circuit/src/poseidon2/air.rs @@ -19,17 +19,17 @@ use openvm_stark_backend::{ use crate::{ chip::{NUM_INITIAL_READS, NUM_SIMPLE_ACCESSES}, - verify_batch::{ + poseidon2::{ columns::{ InsideRowSpecificCols, SimplePoseidonSpecificCols, TopLevelSpecificCols, - VerifyBatchCols, + NativePoseidon2Cols, }, CHUNK, }, }; #[derive(Clone, Debug)] -pub struct VerifyBatchAir { +pub struct NativePoseidon2Air { pub execution_bridge: ExecutionBridge, pub memory_bridge: MemoryBridge, pub internal_bus: VerifyBatchBus, @@ -39,33 +39,33 @@ pub struct VerifyBatchAir { pub(crate) address_space: F, } -impl BaseAir for VerifyBatchAir { +impl BaseAir for NativePoseidon2Air { fn width(&self) -> usize { - VerifyBatchCols::::width() + NativePoseidon2Cols::::width() } } impl BaseAirWithPublicValues - for VerifyBatchAir + for NativePoseidon2Air { } impl PartitionedBaseAir - for VerifyBatchAir + for NativePoseidon2Air { } impl Air - for VerifyBatchAir + for NativePoseidon2Air { fn eval(&self, builder: &mut AB) { let main = builder.main(); let local = main.row_slice(0); - let local: &VerifyBatchCols = (*local).borrow(); + let local: &NativePoseidon2Cols = (*local).borrow(); let next = main.row_slice(1); - let next: &VerifyBatchCols = (*next).borrow(); + let next: &NativePoseidon2Cols = (*next).borrow(); - let &VerifyBatchCols { + let &NativePoseidon2Cols { inner: _, incorporate_row, incorporate_sibling, diff --git a/extensions/native/circuit/src/verify_batch/chip.rs b/extensions/native/circuit/src/poseidon2/chip.rs similarity index 97% rename from extensions/native/circuit/src/verify_batch/chip.rs rename to extensions/native/circuit/src/poseidon2/chip.rs index abe9e2831f..d4b7631df8 100644 --- a/extensions/native/circuit/src/verify_batch/chip.rs +++ b/extensions/native/circuit/src/poseidon2/chip.rs @@ -21,8 +21,8 @@ use openvm_stark_backend::{ }; use serde::{Deserialize, Serialize}; -use crate::verify_batch::{ - air::{VerifyBatchAir, VerifyBatchBus}, +use crate::poseidon2::{ + air::{NativePoseidon2Air, VerifyBatchBus}, CHUNK, }; @@ -125,21 +125,21 @@ pub struct SimplePoseidonRecord { #[derive(Debug, Clone, Serialize, Deserialize, Default)] #[serde(bound = "F: Field")] -pub struct VerifyBatchRecordSet { +pub struct NativePoseidon2RecordSet { pub verify_batch_records: Vec>, pub simple_permute_records: Vec>, } -pub struct VerifyBatchChip { - pub(super) air: VerifyBatchAir, - pub(super) record_set: VerifyBatchRecordSet, +pub struct NativePoseidon2Chip { + pub(super) air: NativePoseidon2Air, + pub(super) record_set: NativePoseidon2RecordSet, pub(super) height: usize, pub(super) offline_memory: Arc>>, pub(super) subchip: Poseidon2SubChip, } impl Stateful> - for VerifyBatchChip + for NativePoseidon2Chip { fn load_state(&mut self, state: Vec) { self.record_set = bitcode::deserialize(&state).unwrap(); @@ -161,7 +161,7 @@ impl Stateful> } } -impl VerifyBatchChip { +impl NativePoseidon2Chip { pub fn new( execution_bus: ExecutionBus, program_bus: ProgramBus, @@ -171,7 +171,7 @@ impl VerifyBatchChip>>, poseidon2_config: Poseidon2Config, ) -> Self { - let air = VerifyBatchAir { + let air = NativePoseidon2Air { execution_bridge: ExecutionBridge::new(execution_bus, program_bus), memory_bridge, internal_bus: VerifyBatchBus(7), @@ -201,7 +201,7 @@ pub(super) const NUM_INITIAL_READS: usize = 7; pub(super) const NUM_SIMPLE_ACCESSES: u32 = 7; impl InstructionExecutor - for VerifyBatchChip + for NativePoseidon2Chip { fn execute( &mut self, diff --git a/extensions/native/circuit/src/verify_batch/columns.rs b/extensions/native/circuit/src/poseidon2/columns.rs similarity index 97% rename from extensions/native/circuit/src/verify_batch/columns.rs rename to extensions/native/circuit/src/poseidon2/columns.rs index dac82f93eb..55ffd20a8a 100644 --- a/extensions/native/circuit/src/verify_batch/columns.rs +++ b/extensions/native/circuit/src/poseidon2/columns.rs @@ -2,11 +2,11 @@ use openvm_circuit::system::memory::offline_checker::{MemoryReadAuxCols, MemoryW use openvm_circuit_primitives_derive::AlignedBorrow; use openvm_poseidon2_air::Poseidon2SubCols; -use crate::verify_batch::CHUNK; +use crate::poseidon2::CHUNK; #[repr(C)] #[derive(AlignedBorrow)] -pub struct VerifyBatchCols { +pub struct NativePoseidon2Cols { // poseidon2 pub inner: Poseidon2SubCols, diff --git a/extensions/native/circuit/src/poseidon2/mod.rs b/extensions/native/circuit/src/poseidon2/mod.rs new file mode 100644 index 0000000000..b891197828 --- /dev/null +++ b/extensions/native/circuit/src/poseidon2/mod.rs @@ -0,0 +1,8 @@ +mod air; +pub mod chip; +mod columns; +#[cfg(test)] +mod tests; +mod trace; + +const CHUNK: usize = 8; diff --git a/extensions/native/circuit/src/verify_batch/tests.rs b/extensions/native/circuit/src/poseidon2/tests.rs similarity index 98% rename from extensions/native/circuit/src/verify_batch/tests.rs rename to extensions/native/circuit/src/poseidon2/tests.rs index a76b38ce58..c282032251 100644 --- a/extensions/native/circuit/src/verify_batch/tests.rs +++ b/extensions/native/circuit/src/poseidon2/tests.rs @@ -23,7 +23,7 @@ use openvm_stark_sdk::{ }; use rand::{rngs::StdRng, Rng}; -use crate::verify_batch::{chip::VerifyBatchChip, CHUNK}; +use crate::poseidon2::{chip::NativePoseidon2Chip, CHUNK}; fn compute_commit( dim: &[usize], @@ -140,7 +140,7 @@ fn test(cases: [Case; N]) { let address_space = 5; let mut tester = VmChipTestBuilder::default(); - let mut chip = VerifyBatchChip::::new( + let mut chip = NativePoseidon2Chip::::new( tester.execution_bus(), tester.program_bus(), tester.memory_bridge(), @@ -373,7 +373,7 @@ fn tester_with_random_poseidon2_ops(num_ops: usize) -> VmChipTester::new( + let mut chip = NativePoseidon2Chip::::new( tester.execution_bus(), tester.program_bus(), tester.memory_bridge(), diff --git a/extensions/native/circuit/src/verify_batch/trace.rs b/extensions/native/circuit/src/poseidon2/trace.rs similarity index 95% rename from extensions/native/circuit/src/verify_batch/trace.rs rename to extensions/native/circuit/src/poseidon2/trace.rs index ae21bf19e2..3d23c38b06 100644 --- a/extensions/native/circuit/src/verify_batch/trace.rs +++ b/extensions/native/circuit/src/poseidon2/trace.rs @@ -16,20 +16,20 @@ use openvm_stark_backend::{ use crate::{ chip::{SimplePoseidonRecord, NUM_INITIAL_READS}, - verify_batch::{ + poseidon2::{ chip::{ CellRecord, IncorporateRowRecord, IncorporateSiblingRecord, InsideRowRecord, - VerifyBatchChip, VerifyBatchRecord, + NativePoseidon2Chip, VerifyBatchRecord, }, columns::{ InsideRowSpecificCols, SimplePoseidonSpecificCols, TopLevelSpecificCols, - VerifyBatchCols, + NativePoseidon2Cols, }, CHUNK, }, }; -impl ChipUsageGetter for VerifyBatchChip { +impl ChipUsageGetter for NativePoseidon2Chip { fn air_name(&self) -> String { "VerifyBatchAir".to_string() } @@ -39,11 +39,11 @@ impl ChipUsageGetter for VerifyBatchChip< } fn trace_width(&self) -> usize { - VerifyBatchCols::::width() + NativePoseidon2Cols::::width() } } -impl VerifyBatchChip { +impl NativePoseidon2Chip { fn generate_subair_cols(&self, input: [F; 2 * CHUNK], cols: &mut [F]) { let inner_trace = self.subchip.generate_trace(vec![input]); let inner_width = self.air.subair.width(); @@ -73,7 +73,7 @@ impl VerifyBatchChip = slice.borrow_mut(); + let cols: &mut NativePoseidon2Cols = slice.borrow_mut(); cols.incorporate_row = F::ZERO; cols.incorporate_sibling = F::ONE; cols.inside_row = F::ZERO; @@ -130,7 +130,7 @@ impl VerifyBatchChip = slice.borrow_mut(); + let cols: &mut NativePoseidon2Cols = slice.borrow_mut(); cols.end_top_level = F::ONE; let specific: &mut TopLevelSpecificCols = @@ -183,7 +183,7 @@ impl VerifyBatchChip = slice.borrow_mut(); + let cols: &mut NativePoseidon2Cols = slice.borrow_mut(); cols.incorporate_row = F::ONE; cols.incorporate_sibling = F::ZERO; cols.inside_row = F::ZERO; @@ -237,7 +237,7 @@ impl VerifyBatchChip = slice.borrow_mut(); + let cols: &mut NativePoseidon2Cols = slice.borrow_mut(); cols.incorporate_row = F::ZERO; cols.incorporate_sibling = F::ZERO; cols.inside_row = F::ONE; @@ -295,7 +295,7 @@ impl VerifyBatchChip, ) -> usize { - let width = VerifyBatchCols::::width(); + let width = NativePoseidon2Cols::::width(); let mut used_cells = 0; let mut height = record.initial_height; @@ -394,7 +394,7 @@ impl VerifyBatchChip = slice.borrow_mut(); + let cols: &mut NativePoseidon2Cols = slice.borrow_mut(); cols.incorporate_row = F::ZERO; cols.incorporate_sibling = F::ZERO; cols.inside_row = F::ZERO; @@ -473,7 +473,7 @@ impl VerifyBatchChip Chip - for VerifyBatchChip, SBOX_REGISTERS> + for NativePoseidon2Chip, SBOX_REGISTERS> where Val: PrimeField32, { diff --git a/extensions/native/circuit/src/verify_batch/mod.rs b/extensions/native/circuit/src/verify_batch/mod.rs deleted file mode 100644 index 2c1509dc98..0000000000 --- a/extensions/native/circuit/src/verify_batch/mod.rs +++ /dev/null @@ -1,10 +0,0 @@ -//use crate::NATIVE_POSEIDON2_CHUNK_SIZE; - -mod air; -pub mod chip; -mod columns; -#[cfg(test)] -mod tests; -mod trace; - -const CHUNK: usize = 8; //NATIVE_POSEIDON2_CHUNK_SIZE; diff --git a/extensions/native/recursion/src/fri/mod.rs b/extensions/native/recursion/src/fri/mod.rs index d5d7abd604..b9838bb9c3 100644 --- a/extensions/native/recursion/src/fri/mod.rs +++ b/extensions/native/recursion/src/fri/mod.rs @@ -151,7 +151,7 @@ pub fn verify_batch( opened_values: &NestedOpenedValues, proof: &Array>, ) { - //println!("verify_batch"); + //println!("poseidon2"); //panic!(); if builder.flags.static_only { verify_batch_static( diff --git a/extensions/native/recursion/src/fri/two_adic_pcs.rs b/extensions/native/recursion/src/fri/two_adic_pcs.rs index 18b9f4ca99..d413894808 100644 --- a/extensions/native/recursion/src/fri/two_adic_pcs.rs +++ b/extensions/native/recursion/src/fri/two_adic_pcs.rs @@ -24,7 +24,7 @@ use crate::{ /// /// So traces are sorted in `opening_proof`. /// -/// 2. FieldMerkleTreeMMCS::verify_batch keeps the raw values in the original order. So traces are not sorted in `opened_values`. +/// 2. FieldMerkleTreeMMCS::poseidon2 keeps the raw values in the original order. So traces are not sorted in `opened_values`. /// /// Reference: /// @@ -134,7 +134,7 @@ pub fn verify_two_adic_pcs( }; let batch_dims: Array> = builder.array(mats.len()); - // `verify_batch` requires `permed_opened_values` to be in the committed order. + // `poseidon2` requires `permed_opened_values` to be in the committed order. let permed_opened_values = builder.array(batch_opening.opened_values.len()); builder.range(0, mats.len()).for_each(|k, builder| { let mat_index = to_perm_index(builder, k); From 76def78a2511a90c427903588167f90352ada381 Mon Sep 17 00:00:00 2001 From: TlatoaniHJ Date: Fri, 17 Jan 2025 13:02:15 -0500 Subject: [PATCH 45/50] Rustfmt --- extensions/native/circuit/src/poseidon2/air.rs | 4 ++-- extensions/native/circuit/src/poseidon2/trace.rs | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/extensions/native/circuit/src/poseidon2/air.rs b/extensions/native/circuit/src/poseidon2/air.rs index b1fc0f7657..e8b04009df 100644 --- a/extensions/native/circuit/src/poseidon2/air.rs +++ b/extensions/native/circuit/src/poseidon2/air.rs @@ -21,8 +21,8 @@ use crate::{ chip::{NUM_INITIAL_READS, NUM_SIMPLE_ACCESSES}, poseidon2::{ columns::{ - InsideRowSpecificCols, SimplePoseidonSpecificCols, TopLevelSpecificCols, - NativePoseidon2Cols, + InsideRowSpecificCols, NativePoseidon2Cols, SimplePoseidonSpecificCols, + TopLevelSpecificCols, }, CHUNK, }, diff --git a/extensions/native/circuit/src/poseidon2/trace.rs b/extensions/native/circuit/src/poseidon2/trace.rs index 3d23c38b06..c1b33b63c2 100644 --- a/extensions/native/circuit/src/poseidon2/trace.rs +++ b/extensions/native/circuit/src/poseidon2/trace.rs @@ -22,14 +22,16 @@ use crate::{ NativePoseidon2Chip, VerifyBatchRecord, }, columns::{ - InsideRowSpecificCols, SimplePoseidonSpecificCols, TopLevelSpecificCols, - NativePoseidon2Cols, + InsideRowSpecificCols, NativePoseidon2Cols, SimplePoseidonSpecificCols, + TopLevelSpecificCols, }, CHUNK, }, }; -impl ChipUsageGetter for NativePoseidon2Chip { +impl ChipUsageGetter + for NativePoseidon2Chip +{ fn air_name(&self) -> String { "VerifyBatchAir".to_string() } From 522734eda249a50172faf96fbad0c4541c1fab23 Mon Sep 17 00:00:00 2001 From: Jonathan Wang <31040440+jonathanpwang@users.noreply.github.com> Date: Mon, 20 Jan 2025 23:28:29 -0500 Subject: [PATCH 46/50] chore: move poseidon2 opcode and test into native-compiler/circuit --- crates/toolchain/instructions/src/lib.rs | 24 +-- crates/vm/tests/integration_test.rs | 132 ---------------- extensions/native/circuit/src/extension.rs | 10 +- .../native/circuit/src/poseidon2/air.rs | 6 +- .../native/circuit/src/poseidon2/chip.rs | 8 +- .../native/circuit/src/poseidon2/tests.rs | 149 +++++++++++++++++- .../native/circuit/src/poseidon2/trace.rs | 4 +- .../native/compiler/src/conversion/mod.rs | 5 +- extensions/native/compiler/src/lib.rs | 23 +++ 9 files changed, 184 insertions(+), 177 deletions(-) diff --git a/crates/toolchain/instructions/src/lib.rs b/crates/toolchain/instructions/src/lib.rs index 0a46753e63..fe2240d254 100644 --- a/crates/toolchain/instructions/src/lib.rs +++ b/crates/toolchain/instructions/src/lib.rs @@ -82,34 +82,12 @@ pub enum SystemOpcode { #[derive( Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, EnumCount, EnumIter, FromRepr, UsizeOpcode, )] -#[opcode_offset = 0x120] +#[opcode_offset = 0x020] #[repr(usize)] pub enum PublishOpcode { PUBLISH, } -#[derive( - Copy, - Clone, - Debug, - PartialEq, - Eq, - PartialOrd, - Ord, - EnumCount, - EnumIter, - FromRepr, - UsizeOpcode, - Serialize, - Deserialize, -)] -#[opcode_offset = 0x150] -#[repr(usize)] -pub enum Poseidon2Opcode { - PERM_POS2, - COMP_POS2, -} - // ================================================================================================= // For internal dev use only // ================================================================================================= diff --git a/crates/vm/tests/integration_test.rs b/crates/vm/tests/integration_test.rs index ad77b22786..57717de300 100644 --- a/crates/vm/tests/integration_test.rs +++ b/crates/vm/tests/integration_test.rs @@ -25,7 +25,6 @@ use openvm_instructions::{ instruction::Instruction, program::{Program, DEFAULT_PC_STEP}, PhantomDiscriminant, - Poseidon2Opcode::*, PublishOpcode::PUBLISH, SysPhantom, SystemOpcode::*, @@ -54,7 +53,6 @@ use openvm_stark_sdk::{ }, engine::StarkFriEngine, p3_baby_bear::BabyBear, - utils::create_seeded_rng, }; use rand::Rng; use serde::{Deserialize, Serialize}; @@ -70,40 +68,6 @@ where rng.gen_range(0..MAX_MEMORY - len) / len * len } -// log_blowup = 3 for poseidon2 chip -fn air_test_with_compress_poseidon2( - poseidon2_max_constraint_degree: usize, - program: Program, - continuation_enabled: bool, -) { - let fri_params = if matches!(std::env::var("OPENVM_FAST_TEST"), Ok(x) if &x == "1") { - FriParameters { - log_blowup: 3, - log_final_poly_len: 0, - num_queries: 2, - proof_of_work_bits: 0, - } - } else { - standard_fri_params_with_100_bits_conjectured_security(3) - }; - let engine = BabyBearPoseidon2Engine::new(fri_params); - - let config = if continuation_enabled { - NativeConfig::aggregation(0, poseidon2_max_constraint_degree).with_continuations() - } else { - NativeConfig::aggregation(0, poseidon2_max_constraint_degree) - }; - let vm = VirtualMachine::new(engine, config); - - let pk = vm.keygen(); - let result = vm.execute_and_generate(program, vec![]).unwrap(); - let proofs = vm.prove(&pk, result); - for proof in proofs { - vm.verify_single(&pk.get_vk(), &proof) - .expect("Verification failed"); - } -} - #[test] fn test_vm_1() { let n = 6; @@ -850,102 +814,6 @@ fn test_vm_hint() { air_test_with_min_segments(config, program, input_stream, 1); } -#[test] -fn test_vm_compress_poseidon2_as2() { - let mut rng = create_seeded_rng(); - - let mut instructions = vec![]; - - let lhs_ptr = gen_pointer(&mut rng, CHUNK) as isize; - for i in 0..CHUNK as isize { - // [lhs_ptr + i]_2 <- rnd() - instructions.push(Instruction::large_from_isize( - VmOpcode::with_default_offset(ADD), - lhs_ptr + i, - rng.gen_range(1..1 << 20), - 0, - 2, - 0, - 0, - 0, - )); - } - let rhs_ptr = gen_pointer(&mut rng, CHUNK) as isize; - for i in 0..CHUNK as isize { - // [rhs_ptr + i]_2 <- rnd() - instructions.push(Instruction::large_from_isize( - VmOpcode::with_default_offset(ADD), - rhs_ptr + i, - rng.gen_range(1..1 << 20), - 0, - 2, - 0, - 0, - 0, - )); - } - let dst_ptr = gen_pointer(&mut rng, CHUNK) as isize; - - // [11]_1 <- lhs_ptr - instructions.push(Instruction::large_from_isize( - VmOpcode::with_default_offset(ADD), - 11, - lhs_ptr, - 0, - 1, - 0, - 0, - 0, - )); - - // [22]_1 <- rhs_ptr - instructions.push(Instruction::large_from_isize( - VmOpcode::with_default_offset(ADD), - 22, - rhs_ptr, - 0, - 1, - 0, - 0, - 0, - )); - // [33]_1 <- dst_ptr - instructions.push(Instruction::large_from_isize( - VmOpcode::with_default_offset(ADD), - 33, - 0, - dst_ptr, - 1, - 0, - 0, - 0, - )); - - instructions.push(Instruction::from_isize( - VmOpcode::with_default_offset(COMP_POS2), - 33, - 11, - 22, - 1, - 2, - )); - instructions.push(Instruction::from_isize( - VmOpcode::with_default_offset(TERMINATE), - 0, - 0, - 0, - 0, - 0, - )); - - let program = Program::from_instructions(&instructions); - - air_test_with_compress_poseidon2(7, program.clone(), false); - air_test_with_compress_poseidon2(3, program.clone(), false); - air_test_with_compress_poseidon2(7, program.clone(), true); - air_test_with_compress_poseidon2(3, program.clone(), true); -} - /// Add instruction to write input to memory, call KECCAK256 opcode, then check against expected output fn instructions_for_keccak256_test(input: &[u8]) -> Vec> { let mut instructions = vec![]; diff --git a/extensions/native/circuit/src/extension.rs b/extensions/native/circuit/src/extension.rs index f37ccd2948..cc1bf8cfd9 100644 --- a/extensions/native/circuit/src/extension.rs +++ b/extensions/native/circuit/src/extension.rs @@ -12,12 +12,10 @@ use openvm_circuit::{ }; use openvm_circuit_derive::{AnyEnum, InstructionExecutor, VmConfig}; use openvm_circuit_primitives_derive::{BytesStateful, Chip, ChipUsageGetter}; -use openvm_instructions::{ - program::DEFAULT_PC_STEP, PhantomDiscriminant, Poseidon2Opcode, UsizeOpcode, VmOpcode, -}; +use openvm_instructions::{program::DEFAULT_PC_STEP, PhantomDiscriminant, UsizeOpcode, VmOpcode}; use openvm_native_compiler::{ CastfOpcode, FieldArithmeticOpcode, FieldExtensionOpcode, FriOpcode, NativeBranchEqualOpcode, - NativeJalOpcode, NativeLoadStore4Opcode, NativeLoadStoreOpcode, NativePhantom, + NativeJalOpcode, NativeLoadStore4Opcode, NativeLoadStoreOpcode, NativePhantom, Poseidon2Opcode, VerifyBatchOpcode, BLOCK_LOAD_STORE_SIZE, }; use openvm_poseidon2_air::Poseidon2Config; @@ -199,7 +197,7 @@ impl VmExtension for Native { FriOpcode::iter().map(VmOpcode::with_default_offset), )?; - let verify_batch_chip = NativePoseidon2Chip::new( + let poseidon2_chip = NativePoseidon2Chip::new( execution_bus, program_bus, memory_bridge, @@ -209,7 +207,7 @@ impl VmExtension for Native { Poseidon2Config::default(), ); inventory.add_executor( - verify_batch_chip, + poseidon2_chip, [ VmOpcode::with_default_offset(VerifyBatchOpcode::VERIFY_BATCH), VmOpcode::with_default_offset(Poseidon2Opcode::PERM_POS2), diff --git a/extensions/native/circuit/src/poseidon2/air.rs b/extensions/native/circuit/src/poseidon2/air.rs index e8b04009df..f84ec4dca9 100644 --- a/extensions/native/circuit/src/poseidon2/air.rs +++ b/extensions/native/circuit/src/poseidon2/air.rs @@ -5,8 +5,10 @@ use openvm_circuit::{ system::memory::{offline_checker::MemoryBridge, MemoryAddress}, }; use openvm_circuit_primitives::utils::not; -use openvm_instructions::Poseidon2Opcode::{COMP_POS2, PERM_POS2}; -use openvm_native_compiler::VerifyBatchOpcode::VERIFY_BATCH; +use openvm_native_compiler::{ + Poseidon2Opcode::{COMP_POS2, PERM_POS2}, + VerifyBatchOpcode::VERIFY_BATCH, +}; use openvm_poseidon2_air::{Poseidon2SubAir, BABY_BEAR_POSEIDON2_HALF_FULL_ROUNDS}; use openvm_stark_backend::{ air_builders::sub::SubAirBuilder, diff --git a/extensions/native/circuit/src/poseidon2/chip.rs b/extensions/native/circuit/src/poseidon2/chip.rs index d4b7631df8..bb611d5702 100644 --- a/extensions/native/circuit/src/poseidon2/chip.rs +++ b/extensions/native/circuit/src/poseidon2/chip.rs @@ -7,13 +7,11 @@ use openvm_circuit::{ program::ProgramBus, }, }; -use openvm_instructions::{ - instruction::Instruction, - program::DEFAULT_PC_STEP, +use openvm_instructions::{instruction::Instruction, program::DEFAULT_PC_STEP, VmOpcode}; +use openvm_native_compiler::{ Poseidon2Opcode::{COMP_POS2, PERM_POS2}, - VmOpcode, + VerifyBatchOpcode::VERIFY_BATCH, }; -use openvm_native_compiler::VerifyBatchOpcode::VERIFY_BATCH; use openvm_poseidon2_air::{Poseidon2Config, Poseidon2SubAir, Poseidon2SubChip}; use openvm_stark_backend::{ p3_field::{Field, PrimeField32}, diff --git a/extensions/native/circuit/src/poseidon2/tests.rs b/extensions/native/circuit/src/poseidon2/tests.rs index c282032251..5ca799bf88 100644 --- a/extensions/native/circuit/src/poseidon2/tests.rs +++ b/extensions/native/circuit/src/poseidon2/tests.rs @@ -1,10 +1,16 @@ use std::cmp::min; -use openvm_circuit::arch::testing::{memory::gen_pointer, VmChipTestBuilder, VmChipTester}; +use openvm_circuit::arch::{ + testing::{memory::gen_pointer, VmChipTestBuilder, VmChipTester}, + VirtualMachine, +}; use openvm_instructions::{ - instruction::Instruction, Poseidon2Opcode, Poseidon2Opcode::*, UsizeOpcode, VmOpcode, + instruction::Instruction, program::Program, SystemOpcode, UsizeOpcode, VmOpcode, +}; +use openvm_native_compiler::{ + FieldArithmeticOpcode, Poseidon2Opcode, Poseidon2Opcode::*, VerifyBatchOpcode, + VerifyBatchOpcode::VERIFY_BATCH, }; -use openvm_native_compiler::{VerifyBatchOpcode, VerifyBatchOpcode::VERIFY_BATCH}; use openvm_poseidon2_air::{Poseidon2Config, Poseidon2SubChip}; use openvm_stark_backend::{ p3_air::BaseAir, @@ -15,7 +21,9 @@ use openvm_stark_backend::{ use openvm_stark_sdk::{ config::{ baby_bear_blake3::{BabyBearBlake3Config, BabyBearBlake3Engine}, + baby_bear_poseidon2::BabyBearPoseidon2Engine, fri_params::standard_fri_params_with_100_bits_conjectured_security, + FriParameters, }, engine::StarkFriEngine, p3_baby_bear::BabyBear, @@ -23,7 +31,10 @@ use openvm_stark_sdk::{ }; use rand::{rngs::StdRng, Rng}; -use crate::poseidon2::{chip::NativePoseidon2Chip, CHUNK}; +use crate::{ + poseidon2::{chip::NativePoseidon2Chip, CHUNK}, + NativeConfig, +}; fn compute_commit( dim: &[usize], @@ -463,3 +474,133 @@ fn verify_batch_chip_simple_50() { let tester = tester_with_random_poseidon2_ops(50); tester.test(get_engine).expect("Verification failed"); } + +// log_blowup = 3 for poseidon2 chip +fn air_test_with_compress_poseidon2( + poseidon2_max_constraint_degree: usize, + program: Program, + continuation_enabled: bool, +) { + let fri_params = if matches!(std::env::var("OPENVM_FAST_TEST"), Ok(x) if &x == "1") { + FriParameters { + log_blowup: 3, + log_final_poly_len: 0, + num_queries: 2, + proof_of_work_bits: 0, + } + } else { + standard_fri_params_with_100_bits_conjectured_security(3) + }; + let engine = BabyBearPoseidon2Engine::new(fri_params); + + let config = if continuation_enabled { + NativeConfig::aggregation(0, poseidon2_max_constraint_degree).with_continuations() + } else { + NativeConfig::aggregation(0, poseidon2_max_constraint_degree) + }; + let vm = VirtualMachine::new(engine, config); + + let pk = vm.keygen(); + let result = vm.execute_and_generate(program, vec![]).unwrap(); + let proofs = vm.prove(&pk, result); + for proof in proofs { + vm.verify_single(&pk.get_vk(), &proof) + .expect("Verification failed"); + } +} + +#[test] +fn test_vm_compress_poseidon2_as2() { + let mut rng = create_seeded_rng(); + + let mut instructions = vec![]; + + let lhs_ptr = gen_pointer(&mut rng, CHUNK) as isize; + for i in 0..CHUNK as isize { + // [lhs_ptr + i]_2 <- rnd() + instructions.push(Instruction::large_from_isize( + VmOpcode::with_default_offset(FieldArithmeticOpcode::ADD), + lhs_ptr + i, + rng.gen_range(1..1 << 20), + 0, + 2, + 0, + 0, + 0, + )); + } + let rhs_ptr = gen_pointer(&mut rng, CHUNK) as isize; + for i in 0..CHUNK as isize { + // [rhs_ptr + i]_2 <- rnd() + instructions.push(Instruction::large_from_isize( + VmOpcode::with_default_offset(FieldArithmeticOpcode::ADD), + rhs_ptr + i, + rng.gen_range(1..1 << 20), + 0, + 2, + 0, + 0, + 0, + )); + } + let dst_ptr = gen_pointer(&mut rng, CHUNK) as isize; + + // [11]_1 <- lhs_ptr + instructions.push(Instruction::large_from_isize( + VmOpcode::with_default_offset(FieldArithmeticOpcode::ADD), + 11, + lhs_ptr, + 0, + 1, + 0, + 0, + 0, + )); + + // [22]_1 <- rhs_ptr + instructions.push(Instruction::large_from_isize( + VmOpcode::with_default_offset(FieldArithmeticOpcode::ADD), + 22, + rhs_ptr, + 0, + 1, + 0, + 0, + 0, + )); + // [33]_1 <- dst_ptr + instructions.push(Instruction::large_from_isize( + VmOpcode::with_default_offset(FieldArithmeticOpcode::ADD), + 33, + 0, + dst_ptr, + 1, + 0, + 0, + 0, + )); + + instructions.push(Instruction::from_isize( + VmOpcode::with_default_offset(COMP_POS2), + 33, + 11, + 22, + 1, + 2, + )); + instructions.push(Instruction::from_isize( + VmOpcode::with_default_offset(SystemOpcode::TERMINATE), + 0, + 0, + 0, + 0, + 0, + )); + + let program = Program::from_instructions(&instructions); + + air_test_with_compress_poseidon2(7, program.clone(), false); + air_test_with_compress_poseidon2(3, program.clone(), false); + air_test_with_compress_poseidon2(7, program.clone(), true); + air_test_with_compress_poseidon2(3, program.clone(), true); +} diff --git a/extensions/native/circuit/src/poseidon2/trace.rs b/extensions/native/circuit/src/poseidon2/trace.rs index c1b33b63c2..24451c9c9d 100644 --- a/extensions/native/circuit/src/poseidon2/trace.rs +++ b/extensions/native/circuit/src/poseidon2/trace.rs @@ -2,7 +2,8 @@ use std::{borrow::BorrowMut, sync::Arc}; use openvm_circuit::system::memory::{MemoryAuxColsFactory, OfflineMemory}; use openvm_circuit_primitives::utils::next_power_of_two_or_zero; -use openvm_instructions::{instruction::Instruction, Poseidon2Opcode::COMP_POS2, VmOpcode}; +use openvm_instructions::{instruction::Instruction, VmOpcode}; +use openvm_native_compiler::Poseidon2Opcode::COMP_POS2; use openvm_stark_backend::{ config::{StarkGenericConfig, Val}, p3_air::BaseAir, @@ -28,7 +29,6 @@ use crate::{ CHUNK, }, }; - impl ChipUsageGetter for NativePoseidon2Chip { diff --git a/extensions/native/compiler/src/conversion/mod.rs b/extensions/native/compiler/src/conversion/mod.rs index 880086a633..633c10f01b 100644 --- a/extensions/native/compiler/src/conversion/mod.rs +++ b/extensions/native/compiler/src/conversion/mod.rs @@ -2,8 +2,7 @@ use openvm_circuit::arch::instructions::program::Program; use openvm_instructions::{ instruction::{DebugInfo, Instruction}, program::{DEFAULT_MAX_NUM_PUBLIC_VALUES, DEFAULT_PC_STEP}, - PhantomDiscriminant, Poseidon2Opcode, PublishOpcode, SysPhantom, SystemOpcode, UsizeOpcode, - VmOpcode, + PhantomDiscriminant, PublishOpcode, SysPhantom, SystemOpcode, UsizeOpcode, VmOpcode, }; use openvm_rv32im_transpiler::BranchEqualOpcode; use openvm_stark_backend::p3_field::{ExtensionField, PrimeField32, PrimeField64}; @@ -12,7 +11,7 @@ use serde::{Deserialize, Serialize}; use crate::{ asm::{AsmInstruction, AssemblyCode}, FieldArithmeticOpcode, FieldExtensionOpcode, FriOpcode, NativeBranchEqualOpcode, - NativeJalOpcode, NativeLoadStore4Opcode, NativeLoadStoreOpcode, NativePhantom, + NativeJalOpcode, NativeLoadStore4Opcode, NativeLoadStoreOpcode, NativePhantom, Poseidon2Opcode, VerifyBatchOpcode, }; diff --git a/extensions/native/compiler/src/lib.rs b/extensions/native/compiler/src/lib.rs index a64b12e0bb..d8abf522e3 100644 --- a/extensions/native/compiler/src/lib.rs +++ b/extensions/native/compiler/src/lib.rs @@ -149,6 +149,29 @@ pub enum NativePhantom { HintBits, } +#[derive( + Copy, + Clone, + Debug, + PartialEq, + Eq, + PartialOrd, + Ord, + EnumCount, + EnumIter, + FromRepr, + UsizeOpcode, + Serialize, + Deserialize, +)] +#[opcode_offset = 0x150] +#[repr(usize)] +#[allow(non_camel_case_types)] +pub enum Poseidon2Opcode { + PERM_POS2, + COMP_POS2, +} + /// Opcodes for FRI opening proofs. #[derive( Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, EnumCount, EnumIter, FromRepr, UsizeOpcode, From 9b0b7f41d3ea04c73f571793d65e4e7984133328 Mon Sep 17 00:00:00 2001 From: Jonathan Wang <31040440+jonathanpwang@users.noreply.github.com> Date: Mon, 20 Jan 2025 23:55:31 -0500 Subject: [PATCH 47/50] chore: add some comments --- extensions/native/circuit/src/poseidon2/air.rs | 14 +++++++------- extensions/native/compiler/src/ir/verify_batch.rs | 8 ++++++++ 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/extensions/native/circuit/src/poseidon2/air.rs b/extensions/native/circuit/src/poseidon2/air.rs index f84ec4dca9..1a2bb0286c 100644 --- a/extensions/native/circuit/src/poseidon2/air.rs +++ b/extensions/native/circuit/src/poseidon2/air.rs @@ -1,4 +1,4 @@ -use std::{borrow::Borrow, sync::Arc}; +use std::{array::from_fn, borrow::Borrow, sync::Arc}; use openvm_circuit::{ arch::{ExecutionBridge, ExecutionState}, @@ -85,16 +85,16 @@ impl Air specific, } = local; - let left_input = std::array::from_fn::<_, CHUNK, _>(|i| local.inner.inputs[i]); - let right_input = std::array::from_fn::<_, CHUNK, _>(|i| local.inner.inputs[i + CHUNK]); - let left_output = std::array::from_fn::<_, CHUNK, _>(|i| { + let left_input = from_fn::<_, CHUNK, _>(|i| local.inner.inputs[i]); + let right_input = from_fn::<_, CHUNK, _>(|i| local.inner.inputs[i + CHUNK]); + let left_output = from_fn::<_, CHUNK, _>(|i| { local.inner.ending_full_rounds[BABY_BEAR_POSEIDON2_HALF_FULL_ROUNDS - 1].post[i] }); - let right_output = std::array::from_fn::<_, CHUNK, _>(|i| { + let right_output = from_fn::<_, CHUNK, _>(|i| { local.inner.ending_full_rounds[BABY_BEAR_POSEIDON2_HALF_FULL_ROUNDS - 1].post[i + CHUNK] }); - let next_left_input = std::array::from_fn::<_, CHUNK, _>(|i| next.inner.inputs[i]); - let next_right_input = std::array::from_fn::<_, CHUNK, _>(|i| next.inner.inputs[i + CHUNK]); + let next_left_input = from_fn::<_, CHUNK, _>(|i| next.inner.inputs[i]); + let next_right_input = from_fn::<_, CHUNK, _>(|i| next.inner.inputs[i + CHUNK]); builder.assert_bool(incorporate_row); builder.assert_bool(incorporate_sibling); diff --git a/extensions/native/compiler/src/ir/verify_batch.rs b/extensions/native/compiler/src/ir/verify_batch.rs index c8df8c156b..a099351a9e 100644 --- a/extensions/native/compiler/src/ir/verify_batch.rs +++ b/extensions/native/compiler/src/ir/verify_batch.rs @@ -1,6 +1,9 @@ use crate::ir::{Array, Builder, Config, DslIr, Ext, Felt, Usize, Var}; impl Builder { + /// - Requires `dimensions.len() == opened_values.len()` + /// - `proof` is an array of arrays where inner arrays are of length `CHUNK` + /// - `commit.len() = CHUNK` pub fn verify_batch_felt( &mut self, dimensions: &Array>, @@ -17,6 +20,11 @@ impl Builder { commit.clone(), )); } + + /// Version of [`verify_batch_felt`] where `opened_values` are extension field elements. + /// - Requires `dimensions.len() == opened_values.len()` + /// - `proof` is an array of arrays where inner arrays are of length `CHUNK` + /// - `commit.len() = CHUNK` pub fn verify_batch_ext( &mut self, dimensions: &Array>, From dd0fa748b5ca70ec4d2a8ce35adf0b1e7de99bdd Mon Sep 17 00:00:00 2001 From: Jonathan Wang <31040440+jonathanpwang@users.noreply.github.com> Date: Tue, 21 Jan 2025 00:21:21 -0500 Subject: [PATCH 48/50] chore: remove magic bus idx 7 --- crates/vm/src/arch/testing/mod.rs | 10 +++++++++- extensions/native/circuit/src/extension.rs | 6 +++--- .../native/circuit/src/poseidon2/chip.rs | 18 +++++++----------- extensions/native/circuit/src/poseidon2/mod.rs | 2 +- .../native/circuit/src/poseidon2/tests.rs | 13 +++++++------ 5 files changed, 27 insertions(+), 22 deletions(-) diff --git a/crates/vm/src/arch/testing/mod.rs b/crates/vm/src/arch/testing/mod.rs index f16b33db84..0636147c57 100644 --- a/crates/vm/src/arch/testing/mod.rs +++ b/crates/vm/src/arch/testing/mod.rs @@ -30,7 +30,7 @@ use program::ProgramTester; use rand::{rngs::StdRng, RngCore, SeedableRng}; use tracing::Level; -use super::{ExecutionBus, InstructionExecutor}; +use super::{ExecutionBus, InstructionExecutor, SystemPort}; use crate::{ arch::{ExecutionState, MemoryConfig}, system::{ @@ -166,6 +166,14 @@ impl VmChipTestBuilder { } } + pub fn system_port(&self) -> SystemPort { + SystemPort { + execution_bus: self.execution.bus, + program_bus: self.program.bus, + memory_bridge: self.memory_bridge(), + } + } + pub fn execution_bus(&self) -> ExecutionBus { self.execution.bus } diff --git a/extensions/native/circuit/src/extension.rs b/extensions/native/circuit/src/extension.rs index cc1bf8cfd9..89c2917c65 100644 --- a/extensions/native/circuit/src/extension.rs +++ b/extensions/native/circuit/src/extension.rs @@ -1,3 +1,4 @@ +use air::VerifyBatchBus; use branch_native_adapter::BranchNativeAdapterChip; use derive_more::derive::From; use jal_native_adapter::JalNativeAdapterChip; @@ -198,13 +199,12 @@ impl VmExtension for Native { )?; let poseidon2_chip = NativePoseidon2Chip::new( - execution_bus, - program_bus, - memory_bridge, + builder.system_port(), VerifyBatchOpcode::default_offset(), Poseidon2Opcode::default_offset(), offline_memory.clone(), Poseidon2Config::default(), + VerifyBatchBus(builder.new_bus_idx()), ); inventory.add_executor( poseidon2_chip, diff --git a/extensions/native/circuit/src/poseidon2/chip.rs b/extensions/native/circuit/src/poseidon2/chip.rs index bb611d5702..bd9fe55648 100644 --- a/extensions/native/circuit/src/poseidon2/chip.rs +++ b/extensions/native/circuit/src/poseidon2/chip.rs @@ -1,11 +1,8 @@ use std::sync::{Arc, Mutex}; use openvm_circuit::{ - arch::{ExecutionBridge, ExecutionBus, ExecutionError, ExecutionState, InstructionExecutor}, - system::{ - memory::{offline_checker::MemoryBridge, MemoryController, OfflineMemory, RecordId}, - program::ProgramBus, - }, + arch::{ExecutionBridge, ExecutionError, ExecutionState, InstructionExecutor, SystemPort}, + system::memory::{MemoryController, OfflineMemory, RecordId}, }; use openvm_instructions::{instruction::Instruction, program::DEFAULT_PC_STEP, VmOpcode}; use openvm_native_compiler::{ @@ -161,18 +158,17 @@ impl Stateful> impl NativePoseidon2Chip { pub fn new( - execution_bus: ExecutionBus, - program_bus: ProgramBus, - memory_bridge: MemoryBridge, + port: SystemPort, verify_batch_offset: usize, perm_pos2_offset: usize, offline_memory: Arc>>, poseidon2_config: Poseidon2Config, + verify_batch_bus: VerifyBatchBus, ) -> Self { let air = NativePoseidon2Air { - execution_bridge: ExecutionBridge::new(execution_bus, program_bus), - memory_bridge, - internal_bus: VerifyBatchBus(7), + execution_bridge: ExecutionBridge::new(port.execution_bus, port.program_bus), + memory_bridge: port.memory_bridge, + internal_bus: verify_batch_bus, subair: Arc::new(Poseidon2SubAir::new(poseidon2_config.constants.into())), verify_batch_offset, simple_offset: perm_pos2_offset, diff --git a/extensions/native/circuit/src/poseidon2/mod.rs b/extensions/native/circuit/src/poseidon2/mod.rs index b891197828..af503e20f4 100644 --- a/extensions/native/circuit/src/poseidon2/mod.rs +++ b/extensions/native/circuit/src/poseidon2/mod.rs @@ -1,4 +1,4 @@ -mod air; +pub mod air; pub mod chip; mod columns; #[cfg(test)] diff --git a/extensions/native/circuit/src/poseidon2/tests.rs b/extensions/native/circuit/src/poseidon2/tests.rs index 5ca799bf88..8718f3f843 100644 --- a/extensions/native/circuit/src/poseidon2/tests.rs +++ b/extensions/native/circuit/src/poseidon2/tests.rs @@ -31,11 +31,14 @@ use openvm_stark_sdk::{ }; use rand::{rngs::StdRng, Rng}; +use super::air::VerifyBatchBus; use crate::{ poseidon2::{chip::NativePoseidon2Chip, CHUNK}, NativeConfig, }; +const VERIFY_BATCH_BUS: VerifyBatchBus = VerifyBatchBus(7); + fn compute_commit( dim: &[usize], opened: &[Vec], @@ -152,13 +155,12 @@ fn test(cases: [Case; N]) { let mut tester = VmChipTestBuilder::default(); let mut chip = NativePoseidon2Chip::::new( - tester.execution_bus(), - tester.program_bus(), - tester.memory_bridge(), + tester.system_port(), VerifyBatchOpcode::default_offset(), Poseidon2Opcode::default_offset(), tester.offline_memory_mutex_arc(), Poseidon2Config::default(), + VERIFY_BATCH_BUS, ); let mut rng = create_seeded_rng(); @@ -385,13 +387,12 @@ fn tester_with_random_poseidon2_ops(num_ops: usize) -> VmChipTester::new( - tester.execution_bus(), - tester.program_bus(), - tester.memory_bridge(), + tester.system_port(), VerifyBatchOpcode::default_offset(), Poseidon2Opcode::default_offset(), tester.offline_memory_mutex_arc(), Poseidon2Config::default(), + VERIFY_BATCH_BUS, ); let mut rng = create_seeded_rng(); From 3f642548aad283e5329bb9193986105ee0caf713 Mon Sep 17 00:00:00 2001 From: Jonathan Wang <31040440+jonathanpwang@users.noreply.github.com> Date: Tue, 21 Jan 2025 00:41:46 -0500 Subject: [PATCH 49/50] nit: cleanup std::array:: --- extensions/native/circuit/src/poseidon2/air.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/native/circuit/src/poseidon2/air.rs b/extensions/native/circuit/src/poseidon2/air.rs index 1a2bb0286c..59d7984bae 100644 --- a/extensions/native/circuit/src/poseidon2/air.rs +++ b/extensions/native/circuit/src/poseidon2/air.rs @@ -474,7 +474,7 @@ impl Air .when(AB::Expr::ONE - end_top_level) .assert_one(next.incorporate_sibling); - let row_hash = std::array::from_fn(|i| { + let row_hash = from_fn(|i| { (start_top_level * left_output[i]) + ((AB::Expr::ONE - start_top_level) * right_input[i]) }); From 3c5c4a34ee5a0f5f91fb9ff5b57fb6cbd8ce773e Mon Sep 17 00:00:00 2001 From: Jonathan Wang <31040440+jonathanpwang@users.noreply.github.com> Date: Tue, 21 Jan 2025 01:50:49 -0500 Subject: [PATCH 50/50] chore: update stark-backend rev --- Cargo.lock | 2 -- Cargo.toml | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index de4823caf3..a3c0372314 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4462,7 +4462,6 @@ dependencies = [ [[package]] name = "openvm-stark-backend" version = "0.2.0-alpha" -source = "git+https://github.com/openvm-org/stark-backend.git?rev=47a0bda#47a0bdabb827bcb95eac0b3ff15c599b5e89236d" dependencies = [ "async-trait", "cfg-if", @@ -4489,7 +4488,6 @@ dependencies = [ [[package]] name = "openvm-stark-sdk" version = "0.2.0-alpha" -source = "git+https://github.com/openvm-org/stark-backend.git?rev=47a0bda#47a0bdabb827bcb95eac0b3ff15c599b5e89236d" dependencies = [ "derive_more 0.99.18", "ff 0.13.0", diff --git a/Cargo.toml b/Cargo.toml index 5b970f28ef..78c9a5e2b9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -106,8 +106,8 @@ lto = "thin" [workspace.dependencies] # Stark Backend -openvm-stark-backend = { git = "https://github.com/openvm-org/stark-backend.git", rev = "47a0bda", default-features = false } -openvm-stark-sdk = { git = "https://github.com/openvm-org/stark-backend.git", rev = "47a0bda", default-features = false } +openvm-stark-backend = { git = "https://github.com/openvm-org/stark-backend.git", rev = "d2788c", default-features = false } +openvm-stark-sdk = { git = "https://github.com/openvm-org/stark-backend.git", rev = "d2788c", default-features = false } # OpenVM openvm-sdk = { path = "crates/sdk", default-features = false }