From 325f872adb537e43c2104ee5032df80be628baff Mon Sep 17 00:00:00 2001 From: Jonathan Wang <31040440+jonathanpwang@users.noreply.github.com> Date: Mon, 16 Dec 2024 17:27:28 -0500 Subject: [PATCH 01/11] chore: remove old link in keccak readme (#1103) --- extensions/keccak256/circuit/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/keccak256/circuit/README.md b/extensions/keccak256/circuit/README.md index 56f4226f9b..5085c961cc 100644 --- a/extensions/keccak256/circuit/README.md +++ b/extensions/keccak256/circuit/README.md @@ -25,7 +25,7 @@ It seems to handle padding in a single AIR row there is no alternate to having ` The absorb step must correctly constrain that the input bytes are XORed with the end-state in the last round and equals the next permutation's `preimage`. The end-state is accessed via `a_prime_prime_prime()`. Note that both `preimage` and `a_prime_prime_prime()` are represented as `u16`s. However we can only XOR at most 8-bit limbs. Without changing the `keccak-f` AIR itself, we can use a trick: if we already have a 16-bit limb `x` and we also provide a 8-bit limb `hi = x >> 8`, assuming `x` and `hi` have been range checked, we can use the expression `lo = x - hi * 256` for the low byte. If `lo` is range checked to `8`-bits, this constrains a valid byte decomposition of `x` into `hi, lo`. This means in terms of trace cells, it is equivalent to provide `x, hi` versus `hi, lo`. -The constraints are separated into those that don't involve interactions in [air.rs](./air.rs) and those that do in [bridge.rs](./bridge.rs). Notably we use an XOR lookup table for byte XORs in the absorb step. +The constraints are in [air.rs](./air.rs). Notably we use an XOR lookup table for byte XORs in the absorb step. ## Future Improvement From 3ca85e83a2ab9e6a5fadfcb6f5047e171a817981 Mon Sep 17 00:00:00 2001 From: Arayi Khalatyan <127004086+arayikhalatyan@users.noreply.github.com> Date: Mon, 16 Dec 2024 17:36:47 -0500 Subject: [PATCH 02/11] Fix: short fix of a link in bigint extension doc (#1105) --- book/src/custom-extensions/bigint.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/src/custom-extensions/bigint.md b/book/src/custom-extensions/bigint.md index 1d79481277..96ee245b06 100644 --- a/book/src/custom-extensions/bigint.md +++ b/book/src/custom-extensions/bigint.md @@ -111,7 +111,7 @@ When using the `I256` struct with `target_os = "zkvm"`, the struct utilizes effi ### Example matrix multiplication using `I256` -See the full example [here](https://github.com/openvm-org/openvm/blob/main/crates/toolchain/tests/programs/examples/signed-matrix-power.rs). +See the full example [here](https://github.com/openvm-org/openvm/blob/main/crates/toolchain/tests/programs/examples/matrix-power-signed.rs). ```rust #![cfg_attr(not(feature = "std"), no_main)] From 2950707749996203a7fc909402379b0d58ab1e46 Mon Sep 17 00:00:00 2001 From: yanziseeker <153156292+AdventureSeeker987@users.noreply.github.com> Date: Wed, 18 Dec 2024 02:34:11 +0800 Subject: [PATCH 03/11] Update README.md (#1108) --- benchmarks/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmarks/README.md b/benchmarks/README.md index 4b6a8a5d89..d3bd2ba55a 100644 --- a/benchmarks/README.md +++ b/benchmarks/README.md @@ -29,7 +29,7 @@ lint or use rust-analyzer on the crate while in the workspace, so the recommende ### Adding the Benchmark -Our proving benchmarks are written as standalone rust binaries. Add one by making a new file in [bin](./src/bin) by following the [fibonacci example](./bin/fibonacci.rs). We currently only run aggregation proofs when feature "aggregation" is on (off by default). Any general benchmarking utility functions can be added to the library in [`src`](./src). There are utility functions `build_bench_program` which compiles the guest program crate with target set to `openvm` and reads the output RISC-V ELF file. +Our proving benchmarks are written as standalone rust binaries. Add one by making a new file in [bin](./src/bin) by following the [fibonacci example](./src/bin/fibonacci.rs). We currently only run aggregation proofs when feature "aggregation" is on (off by default). Any general benchmarking utility functions can be added to the library in [`src`](./src). There are utility functions `build_bench_program` which compiles the guest program crate with target set to `openvm` and reads the output RISC-V ELF file. This can then be fed into `bench_from_exe` which will generate a proof of the execution of the ELF (any other `VmExe`) from a given `VmConfig`. #### Providing Inputs From 3be4942c0dd3caa53866b2dddb384d6df1e86c6f Mon Sep 17 00:00:00 2001 From: Zach Langley Date: Tue, 17 Dec 2024 14:34:53 -0500 Subject: [PATCH 04/11] perf: Use u32 for `address_space` and `address` internally (#1101) * perf: Use usize for address_space internally everywhere * u32 instead of usize * Comments --- crates/sdk/src/verifier/leaf/mod.rs | 4 +- crates/toolchain/instructions/src/exe.rs | 2 +- crates/toolchain/transpiler/src/util.rs | 9 +- crates/vm/src/arch/config.rs | 2 +- .../src/system/memory/manager/dimensions.rs | 9 +- .../vm/src/system/memory/manager/interface.rs | 2 +- crates/vm/src/system/memory/manager/memory.rs | 132 +++++++++--------- crates/vm/src/system/memory/manager/mod.rs | 41 +++--- crates/vm/src/system/memory/merkle/mod.rs | 10 +- .../vm/src/system/memory/merkle/tests/mod.rs | 49 +++---- crates/vm/src/system/memory/merkle/trace.rs | 17 ++- crates/vm/src/system/memory/persistent.rs | 22 +-- crates/vm/src/system/memory/tree/mod.rs | 4 +- .../src/system/memory/tree/public_values.rs | 19 +-- crates/vm/src/system/memory/volatile/mod.rs | 19 +-- crates/vm/src/system/memory/volatile/tests.rs | 29 ++-- crates/vm/tests/integration_test.rs | 9 +- 17 files changed, 179 insertions(+), 200 deletions(-) diff --git a/crates/sdk/src/verifier/leaf/mod.rs b/crates/sdk/src/verifier/leaf/mod.rs index 5cc1cfa113..71659bbc3d 100644 --- a/crates/sdk/src/verifier/leaf/mod.rs +++ b/crates/sdk/src/verifier/leaf/mod.rs @@ -112,9 +112,7 @@ impl LeafVmVerifierConfig { builder: &mut Builder, ) -> ([Felt; DIGEST_SIZE], [Felt; DIGEST_SIZE]) { let memory_dimensions = self.app_system_config.memory_config.memory_dimensions(); - let pv_as = F::from_canonical_usize( - PUBLIC_VALUES_ADDRESS_SPACE_OFFSET + memory_dimensions.as_offset, - ); + let pv_as = PUBLIC_VALUES_ADDRESS_SPACE_OFFSET + memory_dimensions.as_offset; let pv_start_idx = memory_dimensions.label_to_index((pv_as, 0)); let pv_height = log2_strict_usize(self.app_system_config.num_public_values / DIGEST_SIZE); let proof_len = memory_dimensions.overall_height() - pv_height; diff --git a/crates/toolchain/instructions/src/exe.rs b/crates/toolchain/instructions/src/exe.rs index 9e5299a22f..fb84ec7da5 100644 --- a/crates/toolchain/instructions/src/exe.rs +++ b/crates/toolchain/instructions/src/exe.rs @@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize}; use crate::program::Program; /// Memory image is a map from (address space, address) to word. -pub type MemoryImage = BTreeMap<(F, F), F>; +pub type MemoryImage = BTreeMap<(u32, u32), F>; /// Stores the starting address, end address, and name of a set of function. pub type FnBounds = BTreeMap; diff --git a/crates/toolchain/transpiler/src/util.rs b/crates/toolchain/transpiler/src/util.rs index 8e5cfcefa9..b1f8a24e7c 100644 --- a/crates/toolchain/transpiler/src/util.rs +++ b/crates/toolchain/transpiler/src/util.rs @@ -1,8 +1,11 @@ use std::collections::BTreeMap; use openvm_instructions::{ - exe::MemoryImage, instruction::Instruction, riscv::RV32_REGISTER_NUM_LIMBS, - utils::isize_to_field, SystemOpcode, VmOpcode, + exe::MemoryImage, + instruction::Instruction, + riscv::{RV32_MEMORY_AS, RV32_REGISTER_NUM_LIMBS}, + utils::isize_to_field, + SystemOpcode, VmOpcode, }; use openvm_stark_backend::p3_field::PrimeField32; use rrs_lib::instruction_formats::{BType, IType, ITypeShamt, JType, RType, SType, UType}; @@ -167,7 +170,7 @@ pub fn elf_memory_image_to_openvm_memory_image( for (addr, word) in memory_image { for (i, byte) in word.to_le_bytes().into_iter().enumerate() { result.insert( - (F::TWO, F::from_canonical_u32(addr + i as u32)), + (RV32_MEMORY_AS, addr + i as u32), F::from_canonical_u8(byte), ); } diff --git a/crates/vm/src/arch/config.rs b/crates/vm/src/arch/config.rs index 715a0f1542..41a926d92b 100644 --- a/crates/vm/src/arch/config.rs +++ b/crates/vm/src/arch/config.rs @@ -47,7 +47,7 @@ pub struct MemoryConfig { /// The maximum height of the address space. This means the trie has `as_height` layers for searching the address space. The allowed address spaces are those in the range `[as_offset, as_offset + 2^as_height)` where `as_offset` is currently fixed to `1` to not allow address space `0` in memory. pub as_height: usize, /// The offset of the address space. - pub as_offset: usize, + pub as_offset: u32, pub pointer_max_bits: usize, pub clk_max_bits: usize, /// Limb size used by the range checker diff --git a/crates/vm/src/system/memory/manager/dimensions.rs b/crates/vm/src/system/memory/manager/dimensions.rs index e91fe86988..ebe3612198 100644 --- a/crates/vm/src/system/memory/manager/dimensions.rs +++ b/crates/vm/src/system/memory/manager/dimensions.rs @@ -1,5 +1,5 @@ use derive_new::new; -use openvm_stark_backend::{p3_field::PrimeField32, p3_util::log2_strict_usize}; +use openvm_stark_backend::p3_util::log2_strict_usize; use crate::{arch::MemoryConfig, system::memory::CHUNK}; @@ -12,7 +12,7 @@ pub struct MemoryDimensions { /// Pointer height pub address_height: usize, /// Address space offset - pub as_offset: usize, + pub as_offset: u32, } impl MemoryDimensions { @@ -20,10 +20,9 @@ impl MemoryDimensions { self.as_height + self.address_height } /// Convert an address label (address space, block id) to its index in the memory merkle tree. - pub fn label_to_index(&self, label: (F, usize)) -> usize { + pub fn label_to_index(&self, label: (u32, u32)) -> u64 { let (addr_space, block_id) = label; - ((addr_space.as_canonical_u32() as usize - self.as_offset) << self.address_height) - + block_id + (((addr_space - self.as_offset) as u64) << self.address_height) + block_id as u64 } } diff --git a/crates/vm/src/system/memory/manager/interface.rs b/crates/vm/src/system/memory/manager/interface.rs index a3a69d8b1a..a950afdb45 100644 --- a/crates/vm/src/system/memory/manager/interface.rs +++ b/crates/vm/src/system/memory/manager/interface.rs @@ -21,7 +21,7 @@ pub enum MemoryInterface { } impl MemoryInterface { - pub fn touch_address(&mut self, addr_space: F, pointer: F) { + pub fn touch_address(&mut self, addr_space: u32, pointer: u32) { match self { MemoryInterface::Volatile { boundary_chip } => { boundary_chip.touch_address(addr_space, pointer); diff --git a/crates/vm/src/system/memory/manager/memory.rs b/crates/vm/src/system/memory/manager/memory.rs index ba9f4c176c..c38016219f 100644 --- a/crates/vm/src/system/memory/manager/memory.rs +++ b/crates/vm/src/system/memory/manager/memory.rs @@ -48,11 +48,11 @@ impl MemoryReadRecord { pub const INITIAL_TIMESTAMP: u32 = 0; /// (address_space, pointer) -type Address = (usize, usize); +type Address = (u32, u32); #[derive(Clone, Copy, PartialEq, Eq, Debug)] struct BlockData { - pointer: usize, + pointer: u32, size: usize, timestamp: u32, } @@ -76,16 +76,15 @@ impl Memory { let mut block_data = FxHashMap::default(); let mut data = FxHashMap::default(); for (&(address_space, block_idx), values) in initial_memory { - let address_space_usize = address_space.as_canonical_u32() as usize; - let pointer = block_idx * N; + let pointer = block_idx * N as u32; let block = BlockData { pointer, size: N, timestamp: INITIAL_TIMESTAMP, }; for (i, value) in values.iter().enumerate() { - data.insert((address_space_usize, pointer + i), *value); - block_data.insert((address_space_usize, pointer + i), block); + data.insert((address_space, pointer + i as u32), *value); + block_data.insert((address_space, pointer + i as u32), block); } } Self { @@ -113,8 +112,8 @@ impl Memory { /// Writes an array of values to the memory at the specified address space and start index. pub fn write( &mut self, - address_space: usize, - pointer: usize, + address_space: u32, + pointer: u32, values: [F; N], ) -> (MemoryWriteRecord, Vec>) { assert!(N.is_power_of_two()); @@ -127,13 +126,13 @@ impl Memory { let prev_data = array::from_fn(|i| { self.data - .insert((address_space, pointer + i), values[i]) + .insert((address_space, pointer + i as u32), values[i]) .unwrap_or(F::ZERO) }); let record = MemoryWriteRecord { - address_space: F::from_canonical_usize(address_space), - pointer: F::from_canonical_usize(pointer), + address_space: F::from_canonical_u32(address_space), + pointer: F::from_canonical_u32(pointer), timestamp: self.timestamp, prev_timestamp, data: values, @@ -146,8 +145,8 @@ impl Memory { /// Reads an array of values from the memory at the specified address space and start index. pub fn read( &mut self, - address_space: usize, - pointer: usize, + address_space: u32, + pointer: u32, ) -> (MemoryReadRecord, Vec>) { assert!(N.is_power_of_two()); @@ -158,8 +157,8 @@ impl Memory { debug_assert!(prev_timestamp < self.timestamp); let record = MemoryReadRecord { - address_space: F::from_canonical_usize(address_space), - pointer: F::from_canonical_usize(pointer), + address_space: F::from_canonical_u32(address_space), + pointer: F::from_canonical_u32(pointer), timestamp: self.timestamp, prev_timestamp, data: self.range_array::(address_space, pointer), @@ -179,7 +178,7 @@ impl Memory { let to_access: FxHashSet<_> = self .block_data .keys() - .map(|&(address_space, pointer)| (address_space, (pointer / N) * N)) + .map(|&(address_space, pointer)| (address_space, (pointer / N as u32) * N as u32)) .collect(); for &(address_space, pointer) in to_access.iter() { @@ -193,11 +192,11 @@ impl Memory { for (address_space, pointer) in to_access { let block = self.block_data.get(&(address_space, pointer)).unwrap(); - debug_assert_eq!(block.pointer % N, 0); + debug_assert_eq!(block.pointer % N as u32, 0); debug_assert_eq!(block.size, N); equipartition.insert( - (F::from_canonical_usize(address_space), pointer / N), + (address_space, pointer / N as u32), TimestampedValues { timestamp: block.timestamp, values: self.range_array::(address_space, pointer), @@ -211,8 +210,8 @@ impl Memory { // Modifies the partition to ensure that there is a block starting at (address_space, query). fn split_to_make_boundary( &mut self, - address_space: usize, - query: usize, + address_space: u32, + query: u32, records: &mut Vec>, ) { let original_block = self.block_containing(address_space, query); @@ -230,44 +229,43 @@ impl Memory { // Split. records.push(AccessAdapterRecord { timestamp, - address_space: F::from_canonical_usize(address_space), - start_index: F::from_canonical_usize(cur_ptr), - data: data - [cur_ptr - original_block.pointer..cur_ptr - original_block.pointer + cur_size] + address_space: F::from_canonical_u32(address_space), + start_index: F::from_canonical_u32(cur_ptr), + data: data[(cur_ptr - original_block.pointer) as usize + ..(cur_ptr - original_block.pointer) as usize + cur_size] .to_vec(), kind: AccessAdapterRecordKind::Split, }); let half_size = cur_size / 2; + let half_size_u32 = half_size as u32; + let mid_ptr = cur_ptr + half_size_u32; - if query <= cur_ptr + half_size { + if query <= mid_ptr { // The right is finalized; add it to the partition. let block = BlockData { - pointer: cur_ptr + half_size, + pointer: mid_ptr, size: half_size, timestamp, }; - for i in 0..half_size { - self.block_data - .insert((address_space, cur_ptr + half_size + i), block); + for i in 0..half_size_u32 { + self.block_data.insert((address_space, mid_ptr + i), block); } } - if query >= cur_ptr + half_size { + if query >= cur_ptr + half_size_u32 { // The left is finalized; add it to the partition. let block = BlockData { pointer: cur_ptr, size: half_size, timestamp, }; - for i in 0..half_size { + for i in 0..half_size_u32 { self.block_data.insert((address_space, cur_ptr + i), block); } } - - if cur_ptr + half_size <= query { - cur_ptr += half_size; + if mid_ptr <= query { + cur_ptr = mid_ptr; } - if cur_ptr == query { break; } @@ -277,8 +275,8 @@ impl Memory { fn access_updating_timestamp( &mut self, - address_space: usize, - pointer: usize, + address_space: u32, + pointer: u32, size: usize, records: &mut Vec>, ) -> u32 { @@ -286,7 +284,7 @@ impl Memory { let mut prev_timestamp = None; - for i in 0..size { + for i in 0..size as u32 { let block = self .block_data .get_mut(&(address_space, pointer + i)) @@ -300,13 +298,13 @@ impl Memory { fn access( &mut self, - address_space: usize, - pointer: usize, + address_space: u32, + pointer: u32, size: usize, records: &mut Vec>, ) { self.split_to_make_boundary(address_space, pointer, records); - self.split_to_make_boundary(address_space, pointer + size, records); + self.split_to_make_boundary(address_space, pointer + size as u32, records); let block_data = self .block_data @@ -315,8 +313,8 @@ impl Memory { .unwrap_or_else(|| { for i in 0..size { self.block_data.insert( - (address_space, pointer + i), - self.initial_block_data(pointer + i), + (address_space, pointer + i as u32), + self.initial_block_data(pointer + i as u32), ); } self.initial_block_data(pointer) @@ -330,7 +328,12 @@ impl Memory { // Now recursively access left and right blocks to ensure they are in the partition. let half_size = size / 2; self.access(address_space, pointer, half_size, records); - self.access(address_space, pointer + half_size, half_size, records); + self.access( + address_space, + pointer + half_size as u32, + half_size, + records, + ); self.merge_block_with_next(address_space, pointer, records); } @@ -341,8 +344,8 @@ impl Memory { /// do not have the same size. fn merge_block_with_next( &mut self, - address_space: usize, - pointer: usize, + address_space: u32, + pointer: u32, records: &mut Vec>, ) { let left_block = self.block_data.get(&(address_space, pointer)).unwrap(); @@ -352,12 +355,12 @@ impl Memory { let right_timestamp = self .block_data - .get(&(address_space, pointer + size)) + .get(&(address_space, pointer + size as u32)) .map(|b| b.timestamp) .unwrap_or(INITIAL_TIMESTAMP); let timestamp = max(left_timestamp, right_timestamp); - for i in 0..2 * size { + for i in 0..2 * size as u32 { self.block_data.insert( (address_space, pointer + i), BlockData { @@ -369,8 +372,8 @@ impl Memory { } records.push(AccessAdapterRecord { timestamp, - address_space: F::from_canonical_usize(address_space), - start_index: F::from_canonical_usize(pointer), + address_space: F::from_canonical_u32(address_space), + start_index: F::from_canonical_u32(pointer), data: self.range_vec(address_space, pointer, 2 * size), kind: AccessAdapterRecordKind::Merge { left_timestamp, @@ -379,7 +382,7 @@ impl Memory { }); } - fn block_containing(&mut self, address_space: usize, pointer: usize) -> BlockData { + fn block_containing(&mut self, address_space: u32, pointer: u32) -> BlockData { if let Some(block_data) = self.block_data.get(&(address_space, pointer)) { *block_data } else { @@ -387,8 +390,9 @@ impl Memory { } } - fn initial_block_data(&self, pointer: usize) -> BlockData { - let aligned_pointer = (pointer / self.initial_block_size) * self.initial_block_size; + fn initial_block_data(&self, pointer: u32) -> BlockData { + let aligned_pointer = + (pointer / self.initial_block_size as u32) * self.initial_block_size as u32; BlockData { pointer: aligned_pointer, size: self.initial_block_size, @@ -396,17 +400,17 @@ impl Memory { } } - pub fn get(&self, address_space: usize, pointer: usize) -> F { + pub fn get(&self, address_space: u32, pointer: u32) -> F { *self.data.get(&(address_space, pointer)).unwrap_or(&F::ZERO) } - fn range_array(&self, address_space: usize, pointer: usize) -> [F; N] { - array::from_fn(|i| self.get(address_space, pointer + i)) + fn range_array(&self, address_space: u32, pointer: u32) -> [F; N] { + array::from_fn(|i| self.get(address_space, pointer + i as u32)) } - fn range_vec(&self, address_space: usize, pointer: usize, len: usize) -> Vec { + fn range_vec(&self, address_space: u32, pointer: u32, len: usize) -> Vec { (0..len) - .map(|i| self.get(address_space, pointer + i)) + .map(|i| self.get(address_space, pointer + i as u32)) .collect() } } @@ -811,7 +815,7 @@ mod tests { let (final_memory, records) = memory.finalize::<8>(); assert_eq!(final_memory.len(), 4); assert_eq!( - final_memory.get(&(bb!(1), 0)), + final_memory.get(&(1, 0)), Some(&TimestampedValues { values: bba![1, 2, 3, 4, 0, 0, 0, 0], timestamp: 1, @@ -819,7 +823,7 @@ mod tests { ); // start_index = 16 corresponds to label = 2 assert_eq!( - final_memory.get(&(bb!(1), 2)), + final_memory.get(&(1, 2)), Some(&TimestampedValues { values: bba![1, 1, 1, 1, 1, 1, 1, 1], timestamp: 2, @@ -827,7 +831,7 @@ mod tests { ); // start_index = 24 corresponds to label = 3 assert_eq!( - final_memory.get(&(bb!(1), 3)), + final_memory.get(&(1, 3)), Some(&TimestampedValues { values: bba![1, 1, 1, 1, 1, 1, 1, 1], timestamp: 2, @@ -835,7 +839,7 @@ mod tests { ); // start_index = 64 corresponds to label = 8 assert_eq!( - final_memory.get(&(bb!(2), 8)), + final_memory.get(&(2, 8)), Some(&TimestampedValues { values: bba![8, 7, 6, 5, 4, 3, 2, 1], timestamp: 3, @@ -852,8 +856,8 @@ mod tests { // Initialize initial memory with blocks at indices 0 and 2 let mut initial_memory = Equipartition::::new(); - initial_memory.insert((F::ONE, 0), bba![1, 2, 3, 4, 5, 6, 7, 8]); // Block 0, pointers 0–8 - initial_memory.insert((F::ONE, 2), bba![1, 2, 3, 4, 5, 6, 7, 8]); // Block 2, pointers 16–24 + initial_memory.insert((1, 0), bba![1, 2, 3, 4, 5, 6, 7, 8]); // Block 0, pointers 0–8 + initial_memory.insert((1, 2), bba![1, 2, 3, 4, 5, 6, 7, 8]); // Block 2, pointers 16–24 let mut memory = Memory::new(&initial_memory); diff --git a/crates/vm/src/system/memory/manager/mod.rs b/crates/vm/src/system/memory/manager/mod.rs index 595257a3c4..21ceb931a9 100644 --- a/crates/vm/src/system/memory/manager/mod.rs +++ b/crates/vm/src/system/memory/manager/mod.rs @@ -67,22 +67,22 @@ pub struct TimestampedValues { pub type MemoryControllerRef = Rc>>; -/// A equipartition of memory, with timestamps and values. +/// An equipartition of memory, with timestamps and values. /// /// The key is a pair `(address_space, label)`, where `label` is the index of the block in the /// partition. I.e., the starting address of the block is `(address_space, label * N)`. /// /// If a key is not present in the map, then the block is uninitialized (and therefore zero). pub type TimestampedEquipartition = - BTreeMap<(F, usize), TimestampedValues>; + BTreeMap<(u32, u32), TimestampedValues>; -/// A equipartition of memory values. +/// An equipartition of memory values. /// /// The key is a pair `(address_space, label)`, where `label` is the index of the block in the /// partition. I.e., the starting address of the block is `(address_space, label * N)`. /// /// If a key is not present in the map, then the block is uninitialized (and therefore zero). -pub type Equipartition = BTreeMap<(F, usize), [F; N]>; +pub type Equipartition = BTreeMap<(u32, u32), [F; N]>; #[derive(Debug, Getters)] pub struct MemoryController { @@ -346,6 +346,7 @@ impl MemoryController { } pub fn read(&mut self, address_space: F, pointer: F) -> MemoryReadRecord { + let address_space_u32 = address_space.as_canonical_u32(); let ptr_u32 = pointer.as_canonical_u32(); assert!( address_space == F::ZERO || ptr_u32 < (1 << self.mem_config.pointer_max_bits), @@ -367,16 +368,14 @@ impl MemoryController { }; } - let (record, adapter_records) = self - .memory - .read::(address_space.as_canonical_u32() as usize, ptr_u32 as usize); + let (record, adapter_records) = self.memory.read::(address_space_u32, ptr_u32); for record in adapter_records { self.access_adapters.add_record(record); } for i in 0..N as u32 { - let ptr = F::from_canonical_u32(ptr_u32 + i); - self.interface_chip.touch_address(address_space, ptr); + self.interface_chip + .touch_address(address_space_u32, ptr_u32 + i); } record @@ -393,9 +392,9 @@ impl MemoryController { /// /// Any value returned is unconstrained. pub fn unsafe_read(&self, addr_space: F, ptr: F) -> [F; N] { - let addr_space = addr_space.as_canonical_u32() as usize; - let ptr = ptr.as_canonical_u32() as usize; - from_fn(|i| self.memory.get(addr_space, ptr + i)) + let addr_space = addr_space.as_canonical_u32(); + let ptr = ptr.as_canonical_u32(); + from_fn(|i| self.memory.get(addr_space, ptr + i as u32)) } pub fn write_cell(&mut self, address_space: F, pointer: F, data: F) -> MemoryWriteRecord { @@ -409,24 +408,21 @@ impl MemoryController { data: [F; N], ) -> MemoryWriteRecord { assert_ne!(address_space, F::ZERO); + let address_space_u32 = address_space.as_canonical_u32(); let ptr_u32 = pointer.as_canonical_u32(); assert!( ptr_u32 < (1 << self.mem_config.pointer_max_bits), "memory out of bounds: {ptr_u32:?}", ); - let (record, adapter_records) = self.memory.write( - address_space.as_canonical_u32() as usize, - ptr_u32 as usize, - data, - ); + let (record, adapter_records) = self.memory.write(address_space_u32, ptr_u32, data); for record in adapter_records { self.access_adapters.add_record(record); } for i in 0..N as u32 { - let ptr = F::from_canonical_u32(ptr_u32 + i); - self.interface_chip.touch_address(address_space, ptr); + self.interface_chip + .touch_address(address_space_u32, ptr_u32 + i); } record @@ -715,12 +711,11 @@ impl MemoryAuxColsFactory { pub fn memory_image_to_equipartition( memory_image: MemoryImage, -) -> Equipartition { +) -> Equipartition { let mut result = Equipartition::new(); for ((addr_space, addr), word) in memory_image { - let addr_usize = addr.as_canonical_u32() as usize; - let shift = addr_usize % N; - let key = (addr_space, addr_usize / N); + let shift = (addr % N as u32) as usize; + let key = (addr_space, addr / N as u32); result.entry(key).or_insert([F::ZERO; N])[shift] = word; } result diff --git a/crates/vm/src/system/memory/merkle/mod.rs b/crates/vm/src/system/memory/merkle/mod.rs index ad01e25c8b..6dc75ecf6c 100644 --- a/crates/vm/src/system/memory/merkle/mod.rs +++ b/crates/vm/src/system/memory/merkle/mod.rs @@ -17,7 +17,7 @@ mod tests; #[derive(Debug)] pub struct MemoryMerkleChip { pub air: MemoryMerkleAir, - touched_nodes: FxHashSet<(usize, usize, usize)>, + touched_nodes: FxHashSet<(usize, u32, u32)>, num_touched_nonleaves: usize, final_state: Option>, overridden_height: Option, @@ -56,7 +56,7 @@ impl MemoryMerkleChip { self.overridden_height = Some(override_height); } - fn touch_node(&mut self, height: usize, as_label: usize, address_label: usize) { + fn touch_node(&mut self, height: usize, as_label: u32, address_label: u32) { if self.touched_nodes.insert((height, as_label, address_label)) { assert_ne!(height, self.air.memory_dimensions.overall_height()); if height != 0 { @@ -70,11 +70,11 @@ impl MemoryMerkleChip { } } - pub fn touch_address(&mut self, address_space: F, address: F) { + pub fn touch_address(&mut self, address_space: u32, address: u32) { self.touch_node( 0, - (address_space.as_canonical_u32() as usize) - self.air.memory_dimensions.as_offset, - (address.as_canonical_u32() as usize) / CHUNK, + address_space - self.air.memory_dimensions.as_offset, + address / CHUNK as u32, ); } } diff --git a/crates/vm/src/system/memory/merkle/tests/mod.rs b/crates/vm/src/system/memory/merkle/tests/mod.rs index fb800c2d13..11353a1a0f 100644 --- a/crates/vm/src/system/memory/merkle/tests/mod.rs +++ b/crates/vm/src/system/memory/merkle/tests/mod.rs @@ -6,11 +6,8 @@ use std::{ }; use openvm_stark_backend::{ - interaction::InteractionType, - p3_field::{AbstractField, PrimeField32}, - p3_matrix::dense::RowMajorMatrix, - prover::types::AirProofInput, - Chip, ChipUsageGetter, + interaction::InteractionType, p3_field::AbstractField, p3_matrix::dense::RowMajorMatrix, + prover::types::AirProofInput, Chip, ChipUsageGetter, }; use openvm_stark_sdk::{ config::baby_bear_poseidon2::BabyBearPoseidon2Engine, @@ -40,7 +37,7 @@ const COMPRESSION_BUS: DirectCompressionBus = DirectCompressionBus(POSEIDON2_DIR fn test( memory_dimensions: MemoryDimensions, initial_memory: &Equipartition, - touched_labels: BTreeSet<(BabyBear, usize)>, + touched_labels: BTreeSet<(u32, u32)>, final_memory: &Equipartition, ) { let MemoryDimensions { @@ -52,7 +49,7 @@ fn test( // checking validity of test data for (&(address_space, label), value) in final_memory { - assert!((address_space.as_canonical_u32() as usize) - as_offset < (1 << as_height)); + assert!(address_space - as_offset < (1 << as_height)); assert!(label < (1 << address_height)); if initial_memory.get(&(address_space, label)) != Some(value) { assert!(touched_labels.contains(&(address_space, label))); @@ -75,11 +72,8 @@ fn test( let mut chip = MemoryMerkleChip::::new(memory_dimensions, merkle_bus, COMPRESSION_BUS); for &(address_space, label) in touched_labels.iter() { - for i in 0..CHUNK { - chip.touch_address( - address_space, - BabyBear::from_canonical_usize(label * CHUNK + i), - ); + for i in 0..CHUNK as u32 { + chip.touch_address(address_space, label * CHUNK as u32 + i); } } @@ -96,8 +90,8 @@ fn test( let mut interaction = |interaction_type: InteractionType, is_compress: bool, height: usize, - as_label: usize, - address_label: usize, + as_label: u32, + address_label: u32, hash: [BabyBear; CHUNK]| { let expand_direction = if is_compress { BabyBear::NEG_ONE @@ -111,8 +105,8 @@ fn test( dummy_interaction_trace_rows.extend([ expand_direction, BabyBear::from_canonical_usize(height), - BabyBear::from_canonical_usize(as_label), - BabyBear::from_canonical_usize(address_label), + BabyBear::from_canonical_u32(as_label), + BabyBear::from_canonical_u32(address_label), ]); dummy_interaction_trace_rows.extend(hash); }; @@ -121,7 +115,7 @@ fn test( let initial_values = *initial_memory .get(&(address_space, address_label)) .unwrap_or(&[BabyBear::ZERO; CHUNK]); - let as_label = address_space.as_canonical_u32() as usize - as_offset; + let as_label = address_space - as_offset; interaction( InteractionType::Send, false, @@ -163,12 +157,12 @@ fn test( fn random_test( height: usize, - max_value: usize, + max_value: u32, mut num_initial_addresses: usize, mut num_touched_addresses: usize, ) { let mut rng = create_seeded_rng(); - let mut next_usize = || rng.next_u64() as usize; + let mut next_u32 = || rng.next_u64() as u32; let mut initial_memory = Equipartition::new(); let mut final_memory = Equipartition::new(); @@ -176,15 +170,15 @@ fn random_test( let mut touched_labels = BTreeSet::new(); while num_initial_addresses != 0 || num_touched_addresses != 0 { - let address_space = BabyBear::from_canonical_usize((next_usize() & 1) + 1); - let label = next_usize() % (1 << height); + let address_space = (next_u32() & 1) + 1; + let label = next_u32() % (1 << height); if seen_labels.insert(label) { - let is_initial = next_usize() & 1 == 0; + let is_initial = next_u32() & 1 == 0; let initial_values = - array::from_fn(|_| BabyBear::from_canonical_usize(next_usize() % max_value)); - let is_touched = next_usize() & 1 == 0; - let value_changes = next_usize() & 1 == 0; + array::from_fn(|_| BabyBear::from_canonical_u32(next_u32() % max_value)); + let is_touched = next_u32() & 1 == 0; + let value_changes = next_u32() & 1 == 0; if is_initial && num_initial_addresses != 0 { num_initial_addresses -= 1; @@ -195,9 +189,8 @@ fn random_test( num_touched_addresses -= 1; touched_labels.insert((address_space, label)); if value_changes || !is_initial { - let changed_values = array::from_fn(|_| { - BabyBear::from_canonical_usize(next_usize() % max_value) - }); + let changed_values = + array::from_fn(|_| BabyBear::from_canonical_u32(next_u32() % max_value)); final_memory.insert((address_space, label), changed_values); } } diff --git a/crates/vm/src/system/memory/merkle/trace.rs b/crates/vm/src/system/memory/merkle/trace.rs index 8045e7fc92..68f5caa30a 100644 --- a/crates/vm/src/system/memory/merkle/trace.rs +++ b/crates/vm/src/system/memory/merkle/trace.rs @@ -119,7 +119,7 @@ impl ChipUsageGetter for MemoryMerkleChip { memory_dimensions: MemoryDimensions, final_memory: &'a Equipartition, - touched_nodes: &'a FxHashSet<(usize, usize, usize)>, + touched_nodes: &'a FxHashSet<(usize, u32, u32)>, trace_rows: &'a mut Vec>, } @@ -128,13 +128,12 @@ impl TreeHelper<'_, CHUNK, F> { &mut self, height: usize, initial_node: &MemoryNode, - as_label: usize, - address_label: usize, + as_label: u32, + address_label: u32, hasher: &mut impl HasherChip, ) -> MemoryNode { if height == 0 { - let address_space = - F::from_canonical_usize(as_label + self.memory_dimensions.as_offset); + let address_space = as_label + self.memory_dimensions.as_offset; let leaf_values = *self .final_memory .get(&(address_space, address_label)) @@ -215,8 +214,8 @@ impl TreeHelper<'_, CHUNK, F> { fn add_trace_row( &mut self, parent_height: usize, - as_label: usize, - address_label: usize, + as_label: u32, + address_label: u32, node: &MemoryNode, direction_changes: Option<[bool; 2]>, ) { @@ -232,8 +231,8 @@ impl TreeHelper<'_, CHUNK, F> { height_section: F::from_bool(parent_height > self.memory_dimensions.address_height), parent_height: F::from_canonical_usize(parent_height), is_root: F::from_bool(parent_height == self.memory_dimensions.overall_height()), - parent_as_label: F::from_canonical_usize(as_label), - parent_address_label: F::from_canonical_usize(address_label), + parent_as_label: F::from_canonical_u32(as_label), + parent_address_label: F::from_canonical_u32(address_label), parent_hash: *hash, left_child_hash: left.hash(), right_child_hash: right.hash(), diff --git a/crates/vm/src/system/memory/persistent.rs b/crates/vm/src/system/memory/persistent.rs index 57eb71d633..bbd0ab0b9e 100644 --- a/crates/vm/src/system/memory/persistent.rs +++ b/crates/vm/src/system/memory/persistent.rs @@ -87,7 +87,7 @@ impl Air for PersistentBoundaryA // direction = -1 => is_final = 1 local.expand_direction.into(), AB::Expr::ZERO, - local.address_space - AB::F::from_canonical_usize(self.memory_dims.as_offset), + local.address_space - AB::F::from_canonical_u32(self.memory_dims.as_offset), local.leaf_label.into(), ]; expand_fields.extend(local.hash.map(Into::into)); @@ -128,14 +128,14 @@ pub struct PersistentBoundaryChip { #[derive(Debug)] enum TouchedLabels { - Running(FxHashSet<(F, usize)>), + Running(FxHashSet<(u32, u32)>), Final(Vec>), } #[derive(Debug)] struct FinalTouchedLabel { - address_space: F, - label: usize, + address_space: u32, + label: u32, init_values: [F; CHUNK], final_values: [F; CHUNK], init_exists: bool, @@ -151,7 +151,7 @@ impl Default for TouchedLabels { } impl TouchedLabels { - fn touch(&mut self, address_space: F, label: usize) { + fn touch(&mut self, address_space: u32, label: u32) { match self { TouchedLabels::Running(touched_labels) => { touched_labels.insert((address_space, label)); @@ -190,8 +190,8 @@ impl PersistentBoundaryChip { self.overridden_height = Some(overridden_height); } - pub fn touch_address(&mut self, address_space: F, pointer: F) { - let label = pointer.as_canonical_u32() as usize / CHUNK; + pub fn touch_address(&mut self, address_space: u32, pointer: u32) { + let label = pointer / CHUNK as u32; self.touched_labels.touch(address_space, label); } @@ -272,8 +272,8 @@ where let (initial_row, final_row) = row.split_at_mut(width); *initial_row.borrow_mut() = PersistentBoundaryCols { expand_direction: Val::::ONE, - address_space: touched_label.address_space, - leaf_label: Val::::from_canonical_usize(touched_label.label), + address_space: Val::::from_canonical_u32(touched_label.address_space), + leaf_label: Val::::from_canonical_u32(touched_label.label), values: touched_label.init_values, hash: touched_label.init_hash, timestamp: if touched_label.init_exists { @@ -285,8 +285,8 @@ where *final_row.borrow_mut() = PersistentBoundaryCols { expand_direction: Val::::NEG_ONE, - address_space: touched_label.address_space, - leaf_label: Val::::from_canonical_usize(touched_label.label), + address_space: Val::::from_canonical_u32(touched_label.address_space), + leaf_label: Val::::from_canonical_u32(touched_label.label), values: touched_label.final_values, hash: touched_label.final_hash, timestamp: Val::::from_canonical_u32(touched_label.final_timestamp), diff --git a/crates/vm/src/system/memory/tree/mod.rs b/crates/vm/src/system/memory/tree/mod.rs index 00a8281152..51e58a2ab8 100644 --- a/crates/vm/src/system/memory/tree/mod.rs +++ b/crates/vm/src/system/memory/tree/mod.rs @@ -66,9 +66,9 @@ impl MemoryNode { } fn from_memory( - memory: &BTreeMap, + memory: &BTreeMap, height: usize, - from: usize, + from: u64, hasher: &impl Hasher, ) -> MemoryNode { let mut range = memory.range(from..from + (1 << height)); diff --git a/crates/vm/src/system/memory/tree/public_values.rs b/crates/vm/src/system/memory/tree/public_values.rs index 385e1582bd..f31ca63226 100644 --- a/crates/vm/src/system/memory/tree/public_values.rs +++ b/crates/vm/src/system/memory/tree/public_values.rs @@ -8,7 +8,7 @@ use crate::{ system::memory::{dimensions::MemoryDimensions, tree::MemoryNode, Equipartition}, }; -pub const PUBLIC_VALUES_ADDRESS_SPACE_OFFSET: usize = 2; +pub const PUBLIC_VALUES_ADDRESS_SPACE_OFFSET: u32 = 2; /// Merkle proof for user public values in the memory state. #[derive(Clone, Debug, Serialize, Deserialize)] @@ -111,11 +111,8 @@ pub fn extract_public_values( final_memory: &Equipartition, ) -> Vec { // All (addr, value) pairs in the public value address space. - let f_as_start = - F::from_canonical_usize(PUBLIC_VALUES_ADDRESS_SPACE_OFFSET + memory_dimensions.as_offset); - let f_as_end = F::from_canonical_usize( - PUBLIC_VALUES_ADDRESS_SPACE_OFFSET + memory_dimensions.as_offset + 1, - ); + let f_as_start = PUBLIC_VALUES_ADDRESS_SPACE_OFFSET + memory_dimensions.as_offset; + let f_as_end = PUBLIC_VALUES_ADDRESS_SPACE_OFFSET + memory_dimensions.as_offset + 1; let used_pvs: Vec<_> = final_memory .range((f_as_start, 0)..(f_as_end, 0)) @@ -123,7 +120,7 @@ pub fn extract_public_values( value .iter() .enumerate() - .map(|(i, &v)| (*block_id * CHUNK + i, v)) + .map(|(i, &v)| (*block_id as usize * CHUNK + i, v)) }) .collect(); if let Some(last_pv) = used_pvs.last() { @@ -161,13 +158,9 @@ mod tests { vm_config.memory_config.as_height = 4; vm_config.memory_config.pointer_max_bits = 5; let memory_dimensions = vm_config.memory_config.memory_dimensions(); - let pv_as = F::from_canonical_usize( - PUBLIC_VALUES_ADDRESS_SPACE_OFFSET + memory_dimensions.as_offset, - ); + let pv_as = PUBLIC_VALUES_ADDRESS_SPACE_OFFSET + memory_dimensions.as_offset; let num_public_values = 16; - let memory: MemoryImage = [((pv_as, F::from_canonical_u32(15)), F::ONE)] - .into_iter() - .collect(); + let memory: MemoryImage = [((pv_as, 15), F::ONE)].into_iter().collect(); let mut expected_pvs = F::zero_vec(num_public_values); expected_pvs[15] = F::ONE; diff --git a/crates/vm/src/system/memory/volatile/mod.rs b/crates/vm/src/system/memory/volatile/mod.rs index 04e0c68579..a831d768b3 100644 --- a/crates/vm/src/system/memory/volatile/mod.rs +++ b/crates/vm/src/system/memory/volatile/mod.rs @@ -1,6 +1,5 @@ use std::{ borrow::{Borrow, BorrowMut}, - collections::HashSet, sync::Arc, }; @@ -24,6 +23,7 @@ use openvm_stark_backend::{ rap::{AnyRap, BaseAirWithPublicValues, PartitionedBaseAir}, Chip, ChipUsageGetter, }; +use rustc_hash::FxHashSet; use super::TimestampedEquipartition; use crate::system::memory::{ @@ -133,7 +133,7 @@ impl Air for VolatileBoundaryAir { #[derive(Debug)] pub struct VolatileBoundaryChip { pub air: VolatileBoundaryAir, - touched_addresses: HashSet<(F, F)>, + touched_addresses: FxHashSet<(u32, u32)>, range_checker: Arc, overridden_height: Option, final_memory: Option>, @@ -154,18 +154,18 @@ impl VolatileBoundaryChip { pointer_max_bits, range_bus, ), - touched_addresses: HashSet::new(), + touched_addresses: FxHashSet::default(), range_checker, overridden_height: None, final_memory: None, } } - pub fn touch_address(&mut self, addr_space: F, pointer: F) { + pub fn touch_address(&mut self, addr_space: u32, pointer: u32) { self.touched_addresses.insert((addr_space, pointer)); } - pub fn all_addresses(&self) -> Vec<(F, F)> { + pub fn all_addresses(&self) -> Vec<(u32, u32)> { self.touched_addresses.iter().cloned().collect() } } @@ -220,8 +220,8 @@ where // `pointer` is the same as `label` since the equipartition has block size 1 let [data] = timestamped_values.values; let row: &mut VolatileBoundaryCols<_> = row.borrow_mut(); - row.addr_space = *addr_space; - row.pointer = Val::::from_canonical_usize(*ptr); + row.addr_space = Val::::from_canonical_u32(*addr_space); + row.pointer = Val::::from_canonical_u32(*ptr); row.initial_data = Val::::ZERO; row.final_data = data; row.final_timestamp = Val::::from_canonical_u32(timestamped_values.timestamp); @@ -235,7 +235,10 @@ where ( &self.range_checker, &[row.addr_space, row.pointer], - &[next_addr_space, Val::::from_canonical_usize(next_ptr)], + &[ + Val::::from_canonical_u32(next_addr_space), + Val::::from_canonical_u32(next_ptr), + ], ), ((&mut row.addr_lt_aux).into(), &mut out), ); diff --git a/crates/vm/src/system/memory/volatile/tests.rs b/crates/vm/src/system/memory/volatile/tests.rs index eaaf3bb674..cec8f05cd9 100644 --- a/crates/vm/src/system/memory/volatile/tests.rs +++ b/crates/vm/src/system/memory/volatile/tests.rs @@ -2,10 +2,7 @@ use std::{collections::HashSet, iter, sync::Arc}; use openvm_circuit_primitives::var_range::{VariableRangeCheckerBus, VariableRangeCheckerChip}; use openvm_stark_backend::{ - p3_field::{AbstractField, PrimeField32}, - p3_matrix::dense::RowMajorMatrix, - prover::types::AirProofInput, - Chip, + p3_field::AbstractField, p3_matrix::dense::RowMajorMatrix, prover::types::AirProofInput, Chip, }; use openvm_stark_sdk::{ config::baby_bear_poseidon2::{BabyBearPoseidon2Config, BabyBearPoseidon2Engine}, @@ -30,17 +27,17 @@ fn boundary_air_test() { const MEMORY_BUS: usize = 1; const RANGE_CHECKER_BUS: usize = 3; - const MAX_ADDRESS_SPACE: usize = 4; + const MAX_ADDRESS_SPACE: u32 = 4; const LIMB_BITS: usize = 15; - const MAX_VAL: usize = 1 << LIMB_BITS; + const MAX_VAL: u32 = 1 << LIMB_BITS; const DECOMP: usize = 8; let memory_bus = MemoryBus(MEMORY_BUS); let num_addresses = 10; let mut distinct_addresses = HashSet::new(); while distinct_addresses.len() < num_addresses { - let addr_space = Val::from_canonical_usize(rng.gen_range(0..MAX_ADDRESS_SPACE)); - let pointer = Val::from_canonical_usize(rng.gen_range(0..MAX_VAL)); + let addr_space = rng.gen_range(0..MAX_ADDRESS_SPACE); + let pointer = rng.gen_range(0..MAX_VAL); distinct_addresses.insert((addr_space, pointer)); } @@ -52,11 +49,11 @@ fn boundary_air_test() { let mut final_memory = TimestampedEquipartition::new(); for (addr_space, pointer) in distinct_addresses.iter().cloned() { - let final_data = Val::from_canonical_usize(rng.gen_range(0..MAX_VAL)); + let final_data = Val::from_canonical_u32(rng.gen_range(0..MAX_VAL)); let final_clk = rng.gen_range(1..MAX_VAL) as u32; final_memory.insert( - (addr_space, pointer.as_canonical_u32() as usize), + (addr_space, pointer), TimestampedValues { values: [final_data], timestamp: final_clk, @@ -75,8 +72,8 @@ fn boundary_air_test() { .flat_map(|(addr_space, pointer)| { vec![ Val::ONE, - *addr_space, - *pointer, + Val::from_canonical_u32(*addr_space), + Val::from_canonical_u32(*pointer), Val::ZERO, Val::ZERO, Val::ONE, @@ -91,14 +88,12 @@ fn boundary_air_test() { distinct_addresses .iter() .flat_map(|(addr_space, pointer)| { - let timestamped_value = final_memory - .get(&(*addr_space, pointer.as_canonical_u32() as usize)) - .unwrap(); + let timestamped_value = final_memory.get(&(*addr_space, *pointer)).unwrap(); vec![ Val::ONE, - *addr_space, - *pointer, + Val::from_canonical_u32(*addr_space), + Val::from_canonical_u32(*pointer), timestamped_value.values[0], Val::from_canonical_u32(timestamped_value.timestamp), Val::ONE, diff --git a/crates/vm/tests/integration_test.rs b/crates/vm/tests/integration_test.rs index c07790c4e4..224388ac2a 100644 --- a/crates/vm/tests/integration_test.rs +++ b/crates/vm/tests/integration_test.rs @@ -352,12 +352,9 @@ fn test_vm_initial_memory() { ), ]); - let init_memory: BTreeMap<_, _> = [( - (BabyBear::ONE, BabyBear::from_canonical_u32(7)), - BabyBear::from_canonical_u32(101), - )] - .into_iter() - .collect(); + let init_memory: BTreeMap<_, _> = [((1, 7), BabyBear::from_canonical_u32(101))] + .into_iter() + .collect(); let config = NativeConfig::aggregation(0, 3).with_continuations(); let exe = VmExe { From 15d85585205a6430f565f1567004341e1d81db08 Mon Sep 17 00:00:00 2001 From: Jonathan Wang <31040440+jonathanpwang@users.noreply.github.com> Date: Tue, 17 Dec 2024 14:35:07 -0500 Subject: [PATCH 05/11] chore: fix CI for fork repos (#1112) --- .github/workflows/benchmark-call.yml | 1 + .github/workflows/benchmarks.yml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/benchmark-call.yml b/.github/workflows/benchmark-call.yml index 03f346cdd6..2d74817d8f 100644 --- a/.github/workflows/benchmark-call.yml +++ b/.github/workflows/benchmark-call.yml @@ -283,6 +283,7 @@ jobs: echo "BENCHMARK_RESULTS_PATH=${BENCHMARK_RESULTS_PATH}" >> $GITHUB_ENV - name: Update PR github pages with new bench results + if: github.event.pull_request.head.repo.fork == false # forks do not have write access run: | mkdir -p ${BENCHMARK_RESULTS_PATH} s3_md_file="${METRIC_NAME}-${current_sha}.md" diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index e88ff179d6..9c6f912c15 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -213,7 +213,7 @@ jobs: git config --global user.name "github-actions[bot]" - name: Update github pages with new bench results - if: github.event_name == 'pull_request' || (github.event_name == 'push' && github.ref == 'refs/heads/main') + if: (github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == false) || (github.event_name == 'push' && github.ref == 'refs/heads/main') run: | mkdir -p ${BENCHMARK_RESULTS_PATH} cp /tmp/benchmark-results/summary.md ${BENCHMARK_RESULTS_PATH}/summary.md From 3a83db068b2393beabeab498c866be73334e3981 Mon Sep 17 00:00:00 2001 From: osrm <90407222+osrm@users.noreply.github.com> Date: Wed, 18 Dec 2024 05:05:44 +0900 Subject: [PATCH 06/11] docs: cleanup documents (#1111) * fix invalid link README.md [air.rs](./air.rs) is invalid. Changed to [air.rs](./src/air.rs) * fix invalid link README.md "[Writing the Guest Program](../../benchmarks/README.md#writing-the-guest-program" is invalid. Changed to "[Writing the Guest Program](../../../benchmarks/README.md#writing-the-guest-program" * fix invalid link README.md "[fibonacci example](./bin/fibonacci.rs)" is not working. Changed to "[fibonacci example](./src/bin/fibonacci.rs)". * fix invalid link sdk.md "[Overview of Basic Usage](./overview.md)" is invalid. Changed to "[Overview of Basic Usage](../writing-apps/overview.md)". * fix invalid link benchmarks.md "[github workflows](./.github/workflows/benchmark-call.yml)" is invalid link. Changed to "[github workflows](../../.github/workflows/benchmark-call.yml)". * fix typo ISA.md - "an memory access" -> "a memory access" The phrase "an memory access" is considered incorrect because the article "an" is used before words that begin with a vowel sound. Since "memory" starts with a consonant sound, the correct article to use is "a," making it "a memory access." --- book/src/advanced-usage/sdk.md | 2 +- crates/toolchain/tests/README.md | 2 +- docs/crates/benchmarks.md | 2 +- docs/specs/ISA.md | 2 +- extensions/keccak256/circuit/README.md | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/book/src/advanced-usage/sdk.md b/book/src/advanced-usage/sdk.md index da21629b2c..50baf12a86 100644 --- a/book/src/advanced-usage/sdk.md +++ b/book/src/advanced-usage/sdk.md @@ -2,7 +2,7 @@ While the CLI provides a convenient way to build, prove, and verify programs, you may want more fine-grained control over the process. The OpenVM Rust SDK allows you to customize various aspects of the workflow programmatically. -For more information on the basic CLI flow, see [Overview of Basic Usage](./overview.md). Writing a guest program is the same as in the CLI. +For more information on the basic CLI flow, see [Overview of Basic Usage](../writing-apps/overview.md). Writing a guest program is the same as in the CLI. ## Imports and Setup diff --git a/crates/toolchain/tests/README.md b/crates/toolchain/tests/README.md index dff2f7afdd..89372257b2 100644 --- a/crates/toolchain/tests/README.md +++ b/crates/toolchain/tests/README.md @@ -6,7 +6,7 @@ This crate includes tests for OpenVM toolchain that involve starting from a Rust 1. Add a new guest program file to [programs/examples](./programs/examples). -See [Writing the Guest Program](../../benchmarks/README.md#writing-the-guest-program) for more detailed instructions. +See [Writing the Guest Program](../../../benchmarks/README.md#writing-the-guest-program) for more detailed instructions. The `programs` directory is a single crate to make it easier to add small test programs. The crate is **not** part of the main workspace. Your IDE will likely not lint or use rust-analyzer on the crate while in the workspace, so you should open a separate IDE workspace from `programs` while writing your guest program. diff --git a/docs/crates/benchmarks.md b/docs/crates/benchmarks.md index bc9d811242..20fdf62433 100644 --- a/docs/crates/benchmarks.md +++ b/docs/crates/benchmarks.md @@ -3,7 +3,7 @@ ### Latest Benchmark Results Latest benchmark results can be found [here](https://github.com/openvm-org/openvm/blob/benchmark-results/index.md). -These are run via [github workflows](./.github/workflows/benchmark-call.yml) and should always be up to date with the latest `main` branch. +These are run via [github workflows](../../.github/workflows/benchmark-call.yml) and should always be up to date with the latest `main` branch. ### Adding a Benchmark diff --git a/docs/specs/ISA.md b/docs/specs/ISA.md index ad8337ed1f..87d17418f3 100644 --- a/docs/specs/ISA.md +++ b/docs/specs/ISA.md @@ -21,7 +21,7 @@ The program code is committed as a cached trace. The validity of the program cod Memory is comprised of addressable cells, each cell containing a single field element. Instructions of the VM may access (read or write) memory as single cells or as a contiguous list of cells. Such a contiguous list is called a _block_, and -an memory access (read/write) to a block is a _block access_. +a memory access (read/write) to a block is a _block access_. The architecture distinguishes between block accesses of different sizes as this has significant performance implications. The number of cells in a block access is restricted to powers of two, of which the following are supported: 1, 2, 4, 8, 16, 32, 64. Block accesses must be aligned, meaning that in a block access of size $N$, the starting pointer must be divisible by $N$ (as an integer). diff --git a/extensions/keccak256/circuit/README.md b/extensions/keccak256/circuit/README.md index 5085c961cc..afd6881f65 100644 --- a/extensions/keccak256/circuit/README.md +++ b/extensions/keccak256/circuit/README.md @@ -25,7 +25,7 @@ It seems to handle padding in a single AIR row there is no alternate to having ` The absorb step must correctly constrain that the input bytes are XORed with the end-state in the last round and equals the next permutation's `preimage`. The end-state is accessed via `a_prime_prime_prime()`. Note that both `preimage` and `a_prime_prime_prime()` are represented as `u16`s. However we can only XOR at most 8-bit limbs. Without changing the `keccak-f` AIR itself, we can use a trick: if we already have a 16-bit limb `x` and we also provide a 8-bit limb `hi = x >> 8`, assuming `x` and `hi` have been range checked, we can use the expression `lo = x - hi * 256` for the low byte. If `lo` is range checked to `8`-bits, this constrains a valid byte decomposition of `x` into `hi, lo`. This means in terms of trace cells, it is equivalent to provide `x, hi` versus `hi, lo`. -The constraints are in [air.rs](./air.rs). Notably we use an XOR lookup table for byte XORs in the absorb step. +The constraints are in [air.rs](./src/air.rs). Notably we use an XOR lookup table for byte XORs in the absorb step. ## Future Improvement From 3b505ac9b02da23d695fbc0f07dd0266482e8841 Mon Sep 17 00:00:00 2001 From: Zach Langley Date: Tue, 17 Dec 2024 18:50:27 -0500 Subject: [PATCH 07/11] fix: Incorrect lazy initialization of memory data structure (#1113) --- crates/toolchain/tests/src/pairing_tests.rs | 2 +- crates/vm/src/system/memory/manager/memory.rs | 31 +++++++------------ 2 files changed, 13 insertions(+), 20 deletions(-) diff --git a/crates/toolchain/tests/src/pairing_tests.rs b/crates/toolchain/tests/src/pairing_tests.rs index 65912bfb86..91faf1e2be 100644 --- a/crates/toolchain/tests/src/pairing_tests.rs +++ b/crates/toolchain/tests/src/pairing_tests.rs @@ -219,7 +219,7 @@ mod bn254 { let io_all = io0.into_iter().chain(io1).collect::>(); - new_air_test_with_min_segments(get_testing_config(), openvm_exe, vec![io_all], 1, false); + new_air_test_with_min_segments(get_testing_config(), openvm_exe, vec![io_all], 1, true); Ok(()) } diff --git a/crates/vm/src/system/memory/manager/memory.rs b/crates/vm/src/system/memory/manager/memory.rs index c38016219f..b7d2b1860d 100644 --- a/crates/vm/src/system/memory/manager/memory.rs +++ b/crates/vm/src/system/memory/manager/memory.rs @@ -287,8 +287,8 @@ impl Memory { for i in 0..size as u32 { let block = self .block_data - .get_mut(&(address_space, pointer + i)) - .unwrap(); + .entry((address_space, pointer + i)) + .or_insert_with(|| Self::initial_block_data(pointer + i, self.initial_block_size)); debug_assert!(i == 0 || prev_timestamp == Some(block.timestamp)); prev_timestamp = Some(block.timestamp); block.timestamp = self.timestamp; @@ -310,15 +310,7 @@ impl Memory { .block_data .get(&(address_space, pointer)) .copied() - .unwrap_or_else(|| { - for i in 0..size { - self.block_data.insert( - (address_space, pointer + i as u32), - self.initial_block_data(pointer + i as u32), - ); - } - self.initial_block_data(pointer) - }); + .unwrap_or_else(|| Self::initial_block_data(pointer, self.initial_block_size)); if block_data.pointer == pointer && block_data.size == size { return; @@ -348,10 +340,12 @@ impl Memory { pointer: u32, records: &mut Vec>, ) { - let left_block = self.block_data.get(&(address_space, pointer)).unwrap(); + let left_block = self.block_data.get(&(address_space, pointer)); - let left_timestamp = left_block.timestamp; - let size = left_block.size; + let left_timestamp = left_block.map(|b| b.timestamp).unwrap_or(INITIAL_TIMESTAMP); + let size = left_block + .map(|b| b.size) + .unwrap_or(self.initial_block_size); let right_timestamp = self .block_data @@ -386,16 +380,15 @@ impl Memory { if let Some(block_data) = self.block_data.get(&(address_space, pointer)) { *block_data } else { - self.initial_block_data(pointer) + Self::initial_block_data(pointer, self.initial_block_size) } } - fn initial_block_data(&self, pointer: u32) -> BlockData { - let aligned_pointer = - (pointer / self.initial_block_size as u32) * self.initial_block_size as u32; + fn initial_block_data(pointer: u32, initial_block_size: usize) -> BlockData { + let aligned_pointer = (pointer / initial_block_size as u32) * initial_block_size as u32; BlockData { pointer: aligned_pointer, - size: self.initial_block_size, + size: initial_block_size, timestamp: INITIAL_TIMESTAMP, } } From 4250ce882f1290902de163efd279330c87ba163f Mon Sep 17 00:00:00 2001 From: Golovanov399 Date: Wed, 18 Dec 2024 03:48:10 +0300 Subject: [PATCH 08/11] [fix] `build_guest_package` didn't target any specific package, there was no way to build examples with it (#1114) * Fix the functionality, change the signature * Update the book --- benchmarks/src/utils.rs | 2 +- book/src/writing-apps/build.md | 22 +++------- crates/cli/src/commands/build.rs | 42 +++++++++--------- crates/sdk/src/lib.rs | 4 +- crates/toolchain/build/src/lib.rs | 66 +++++++++++++++-------------- crates/toolchain/tests/src/utils.rs | 13 ++++-- 6 files changed, 73 insertions(+), 76 deletions(-) diff --git a/benchmarks/src/utils.rs b/benchmarks/src/utils.rs index aad24b8af8..592d046efd 100644 --- a/benchmarks/src/utils.rs +++ b/benchmarks/src/utils.rs @@ -60,7 +60,7 @@ pub fn build_bench_program(program_name: &str) -> Result { let target_dir = tempdir()?; // Build guest with default features let guest_opts = GuestOptions::default().with_target_dir(target_dir.path()); - if let Err(Some(code)) = build_guest_package(&pkg, &guest_opts, None) { + if let Err(Some(code)) = build_guest_package(&pkg, &guest_opts, None, &None) { std::process::exit(code); } // Assumes the package has a single target binary diff --git a/book/src/writing-apps/build.md b/book/src/writing-apps/build.md index 2dbac169e3..dc61d50f63 100644 --- a/book/src/writing-apps/build.md +++ b/book/src/writing-apps/build.md @@ -37,34 +37,24 @@ The following flags are available for the `cargo openvm build` command: cargo openvm build --features my_feature ``` -- `--bin` +- `--bin ` - **Description**: Restricts the build to binary targets. If your project has multiple target types (binaries, libraries, examples, etc.), using `--bin` ensures only binary targets are considered. + **Description**: Restricts the build to the binary target with the given name, similar to `cargo build --bin `. If your project has multiple target types (binaries, libraries, examples, etc.), using `--bin ` narrows down the build to the binary target with the given name. **Usage Example**: ```bash - cargo openvm build --bin + cargo openvm build --bin my_bin ``` -- `--example` +- `--example ` - **Description**: Restricts the build to example targets. Projects often include code samples or demos under the examples directory, and this flag focuses on compiling those. + **Description**: Restricts the build to the example target with the given name, similar to `cargo build --example `. Projects often include code samples or demos under the examples directory, and this flag focuses on compiling a specific example. **Usage Example**: ```bash - cargo openvm build --example - ``` - -- `--name ` - - **Description**: Filters targets by name. Only targets whose names contain the given substring will be built. - - **Usage Example**: To build only targets that have `client` in their name: - - ```bash - cargo openvm build --name client + cargo openvm build --example my_example ``` - `--no-transpile` diff --git a/crates/cli/src/commands/build.rs b/crates/cli/src/commands/build.rs index 95841b646f..4db666e821 100644 --- a/crates/cli/src/commands/build.rs +++ b/crates/cli/src/commands/build.rs @@ -42,9 +42,6 @@ pub struct BuildArgs { #[clap(flatten, help = "Filter the target to build")] pub bin_type_filter: BinTypeFilter, - #[arg(long, help = "Target name substring filter")] - pub name: Option, - #[arg( long, default_value = "false", @@ -73,31 +70,30 @@ pub struct BuildArgs { #[derive(Clone, clap::Args)] #[group(required = false, multiple = false)] pub struct BinTypeFilter { - #[arg( - long, - help = "Specifies that the target should be a binary kind when set" - )] - pub bin: bool, + #[arg(long, help = "Specifies that the bin target to build")] + pub bin: Option, - #[arg( - long, - help = "Specifies that the target should be an example kind when set" - )] - pub example: bool, + #[arg(long, help = "Specifies that the example target to build")] + pub example: Option, } // Returns the path to the ELF file if it is unique. pub(crate) fn build(build_args: &BuildArgs) -> Result> { println!("[openvm] Building the package..."); - let target_filter = TargetFilter { - name_substr: build_args.name.clone(), - kind: if build_args.bin_type_filter.bin { - Some("bin".to_string()) - } else if build_args.bin_type_filter.example { - Some("example".to_string()) - } else { - None - }, + let target_filter = if let Some(bin) = &build_args.bin_type_filter.bin { + Some(TargetFilter { + name: bin.clone(), + kind: "bin".to_string(), + }) + } else { + build_args + .bin_type_filter + .example + .as_ref() + .map(|example| TargetFilter { + name: example.clone(), + kind: "example".to_string(), + }) }; let guest_options = GuestOptions { features: build_args.features.clone(), @@ -106,7 +102,7 @@ pub(crate) fn build(build_args: &BuildArgs) -> Result> { let pkg = get_package(&build_args.manifest_dir); // We support builds of libraries with 0 or >1 executables. - let elf_path = match build_guest_package(&pkg, &guest_options, None) { + let elf_path = match build_guest_package(&pkg, &guest_options, None, &target_filter) { Ok(target_dir) => { find_unique_executable(&build_args.manifest_dir, &target_dir, &target_filter) } diff --git a/crates/sdk/src/lib.rs b/crates/sdk/src/lib.rs index 8a182fc0cf..433d0acac7 100644 --- a/crates/sdk/src/lib.rs +++ b/crates/sdk/src/lib.rs @@ -71,10 +71,10 @@ impl Sdk { &self, guest_opts: GuestOptions, pkg_dir: P, - target_filter: &TargetFilter, + target_filter: &Option, ) -> Result { let pkg = get_package(pkg_dir.as_ref()); - let target_dir = match build_guest_package(&pkg, &guest_opts, None) { + let target_dir = match build_guest_package(&pkg, &guest_opts, None, target_filter) { Ok(target_dir) => target_dir, Err(Some(code)) => { return Err(eyre::eyre!("Failed to build guest: code = {}", code)); diff --git a/crates/toolchain/build/src/lib.rs b/crates/toolchain/build/src/lib.rs index 7b95e1729f..b8e0c8a36f 100644 --- a/crates/toolchain/build/src/lib.rs +++ b/crates/toolchain/build/src/lib.rs @@ -69,11 +69,20 @@ pub fn get_target_dir(manifest_path: impl AsRef) -> PathBuf { } /// Returns the target executable directory given `target_dir` and `profile`. -pub fn get_dir_with_profile(target_dir: impl AsRef, profile: &str) -> PathBuf { - target_dir +pub fn get_dir_with_profile( + target_dir: impl AsRef, + profile: &str, + examples: bool, +) -> PathBuf { + let res = target_dir .as_ref() .join("riscv32im-risc0-zkvm-elf") - .join(profile) + .join(profile); + if examples { + res.join("examples") + } else { + res + } } /// When called from a build.rs, returns the current package being built. @@ -241,6 +250,7 @@ pub fn build_guest_package( pkg: &Package, guest_opts: &GuestOptions, runtime_lib: Option<&str>, + target_filter: &Option, ) -> Result> { if is_skip_build() { return Err(None); @@ -279,6 +289,13 @@ pub fn build_guest_package( target_dir.to_str().unwrap(), ]); + if let Some(target_filter) = target_filter { + cmd.args([ + format!("--{}", target_filter.kind).as_str(), + target_filter.name.as_str(), + ]); + } + let profile = if let Some(profile) = &guest_opts.profile { profile } else if is_debug() { @@ -320,31 +337,24 @@ pub fn build_guest_package( if !res.success() { Err(res.code()) } else { - Ok(get_dir_with_profile(&target_dir, profile)) + Ok(get_dir_with_profile( + &target_dir, + profile, + target_filter + .as_ref() + .map(|t| t.kind == "example") + .unwrap_or(false), + )) } } /// A filter for selecting a target from a package. #[derive(Default)] pub struct TargetFilter { - /// A substring of the target name to match. - pub name_substr: Option, + /// The target name to match. + pub name: String, /// The kind of target to match. - pub kind: Option, -} - -impl TargetFilter { - /// Set substring of target name to match. - pub fn with_name_substr(mut self, name_substr: String) -> Self { - self.name_substr = Some(name_substr); - self - } - - /// Set kind of target to match. - pub fn with_kind(mut self, kind: String) -> Self { - self.kind = Some(kind); - self - } + pub kind: String, } /// Finds the unique executable target in the given package and target directory, @@ -352,22 +362,16 @@ impl TargetFilter { pub fn find_unique_executable, Q: AsRef>( pkg_dir: P, target_dir: Q, - target_filter: &TargetFilter, + target_filter: &Option, ) -> eyre::Result { let pkg = get_package(pkg_dir.as_ref()); let elf_paths = pkg .targets .into_iter() .filter(move |target| { - if let Some(name_substr) = &target_filter.name_substr { - if !target.name.contains(name_substr) { - return false; - } - } - if let Some(kind) = &target_filter.kind { - if !target.kind.iter().any(|k| k == kind) { - return false; - } + if let Some(target_filter) = target_filter { + return target.kind.iter().any(|k| k == &target_filter.kind) + && target.name == target_filter.name; } true }) diff --git a/crates/toolchain/tests/src/utils.rs b/crates/toolchain/tests/src/utils.rs index ab44c61144..eb743efd04 100644 --- a/crates/toolchain/tests/src/utils.rs +++ b/crates/toolchain/tests/src/utils.rs @@ -4,7 +4,7 @@ use std::{ }; use eyre::Result; -use openvm_build::{build_guest_package, get_package, is_debug, GuestOptions}; +use openvm_build::{build_guest_package, get_package, is_debug, GuestOptions, TargetFilter}; use openvm_transpiler::{elf::Elf, openvm_platform::memory::MEM_SIZE}; use tempfile::tempdir; @@ -41,10 +41,17 @@ pub fn build_example_program_at_path_with_features>( let target_dir = tempdir()?; // Build guest with default features let guest_opts = GuestOptions::default() - .with_options(["--example", example_name]) .with_features(features) .with_target_dir(target_dir.path()); - if let Err(Some(code)) = build_guest_package(&pkg, &guest_opts, None) { + if let Err(Some(code)) = build_guest_package( + &pkg, + &guest_opts, + None, + &Some(TargetFilter { + name: example_name.to_string(), + kind: "example".to_string(), + }), + ) { std::process::exit(code); } // Assumes the package has a single target binary From d226647eb343d72f9a4c01bc8aacc87dad1c29c7 Mon Sep 17 00:00:00 2001 From: Yi Sun Date: Tue, 17 Dec 2024 23:46:17 -0600 Subject: [PATCH 09/11] chore: fix links and comments (#1115) * chore: fix links and comments * chore: make link relative --- ci/scripts/utils.sh | 6 +++--- docs/specs/circuit.md | 2 +- extensions/native/compiler/Cargo.toml | 2 +- .../pairing/circuit/src/pairing_chip/miller_double_step.rs | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ci/scripts/utils.sh b/ci/scripts/utils.sh index 7c45813bb8..c93bfc9877 100644 --- a/ci/scripts/utils.sh +++ b/ci/scripts/utils.sh @@ -2,7 +2,7 @@ generate_markdown() { local metric_path="$1" local metric_name="$2" local s3_metrics_path="$3" - local afs_root="$4" + local openvm_root="$4" if [[ -f $metric_path ]]; then prev_path="${s3_metrics_path}/main-${metric_name}.json" @@ -10,10 +10,10 @@ generate_markdown() { if [[ $count -gt 0 ]]; then s5cmd cp $prev_path prev.json - python3 ${afs_root}/ci/scripts/metric_unify/main.py $metric_path --prev prev.json --aggregation-json ${afs_root}/ci/scripts/metric_unify/aggregation.json > results.md + python3 ${openvm_root}/ci/scripts/metric_unify/main.py $metric_path --prev prev.json --aggregation-json ${openvm_root}/ci/scripts/metric_unify/aggregation.json > results.md else echo "No previous benchmark on main branch found" - python3 ${afs_root}/ci/scripts/metric_unify/main.py $metric_path --aggregation-json ${afs_root}/ci/scripts/metric_unify/aggregation.json > results.md + python3 ${openvm_root}/ci/scripts/metric_unify/main.py $metric_path --aggregation-json ${openvm_root}/ci/scripts/metric_unify/aggregation.json > results.md fi else echo "No benchmark metrics found at ${metric_path}" diff --git a/docs/specs/circuit.md b/docs/specs/circuit.md index 5c5e3e2daf..82a8fad7dc 100644 --- a/docs/specs/circuit.md +++ b/docs/specs/circuit.md @@ -59,7 +59,7 @@ operands (setting the rest to zero) without paying the cost for the unused opera **Note:** each chip receives an _offset_ on construction, and this offset basically means "where does the class of operations which this chip supports start". For example, if a `FieldArithmeticChip` has offset `0x100`, then its `SUB` operation would be encoded with opcode `0x100 + 1` and not just `1`. -See [ISA spec](https://github.com/axiom-crypto/afs-prototype/blob/main/docs/specs/vm/ISA.md#instruction-list) for +See [ISA spec](./ISA.md) for details. Each chip receives `(timestamp, pc)` on EXECUTION_BUS and "after" diff --git a/extensions/native/compiler/Cargo.toml b/extensions/native/compiler/Cargo.toml index 3251847b2a..569e36cf90 100644 --- a/extensions/native/compiler/Cargo.toml +++ b/extensions/native/compiler/Cargo.toml @@ -20,7 +20,7 @@ openvm-circuit-primitives = { workspace = true } openvm-stark-sdk = { workspace = true } openvm-circuit = { workspace = true } openvm-rv32im-transpiler = { workspace = true } -# disable jemalloc to be compatible with afs-starkbackend +# disable jemalloc to be compatible with stark-backend snark-verifier-sdk = { workspace = true, optional = true } tracing.workspace = true diff --git a/extensions/pairing/circuit/src/pairing_chip/miller_double_step.rs b/extensions/pairing/circuit/src/pairing_chip/miller_double_step.rs index f57460510c..16a3997603 100644 --- a/extensions/pairing/circuit/src/pairing_chip/miller_double_step.rs +++ b/extensions/pairing/circuit/src/pairing_chip/miller_double_step.rs @@ -55,7 +55,7 @@ impl< } } -// Ref: https://github.com/axiom-crypto/afs-prototype/blob/f7d6fa7b8ef247e579740eb652fcdf5a04259c28/lib/ecc-execution/src/common/miller_step.rs#L7 +// Ref: https://github.com/openvm-org/openvm/blob/f7d6fa7b8ef247e579740eb652fcdf5a04259c28/lib/ecc-execution/src/common/miller_step.rs#L7 pub fn miller_double_step_expr( config: ExprBuilderConfig, range_bus: VariableRangeCheckerBus, From 74f0b4f69c420cc50f08c966309b04c47d3238f2 Mon Sep 17 00:00:00 2001 From: Xinding Wei Date: Wed, 18 Dec 2024 08:50:55 -0800 Subject: [PATCH 10/11] Update openvm-stark-backend to v0.1.1-alpha (#1117) --- Cargo.lock | 5 +++-- Cargo.toml | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8a204162c6..eb0ac9efee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4225,7 +4225,7 @@ dependencies = [ [[package]] name = "openvm-stark-backend" version = "0.1.0-alpha" -source = "git+https://github.com/openvm-org/stark-backend.git?tag=v0.1.0-alpha#83b0d048c00bbfba60a5b0732cf923bed5dd6e76" +source = "git+https://github.com/openvm-org/stark-backend.git?tag=v0.1.1-alpha#a995f8e03662e0a18c89edbe4424933b99c42e52" dependencies = [ "async-trait", "cfg-if", @@ -4242,6 +4242,7 @@ dependencies = [ "p3-uni-stark", "p3-util", "rayon", + "rustc-hash 2.1.0", "serde", "thiserror 1.0.69", "tikv-jemallocator", @@ -4251,7 +4252,7 @@ dependencies = [ [[package]] name = "openvm-stark-sdk" version = "0.1.0-alpha" -source = "git+https://github.com/openvm-org/stark-backend.git?tag=v0.1.0-alpha#83b0d048c00bbfba60a5b0732cf923bed5dd6e76" +source = "git+https://github.com/openvm-org/stark-backend.git?tag=v0.1.1-alpha#a995f8e03662e0a18c89edbe4424933b99c42e52" dependencies = [ "derive_more 0.99.18", "ff 0.13.0", diff --git a/Cargo.toml b/Cargo.toml index 5ffb5b07a2..4224139302 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -106,8 +106,8 @@ openvm-platform = { path = "crates/toolchain/platform", default-features = false openvm-transpiler = { path = "crates/toolchain/transpiler", default-features = false } openvm-circuit = { path = "crates/vm", default-features = false } openvm-circuit-derive = { path = "crates/vm/derive", default-features = false } -openvm-stark-backend = { git = "https://github.com/openvm-org/stark-backend.git", tag = "v0.1.0-alpha", default-features = false } -openvm-stark-sdk = { git = "https://github.com/openvm-org/stark-backend.git", tag = "v0.1.0-alpha", default-features = false } +openvm-stark-backend = { git = "https://github.com/openvm-org/stark-backend.git", tag = "v0.1.1-alpha", default-features = false } +openvm-stark-sdk = { git = "https://github.com/openvm-org/stark-backend.git", tag = "v0.1.1-alpha", default-features = false } # Extensions openvm-algebra-circuit = { path = "extensions/algebra/circuit", default-features = false } From 56caed25346902c25258d03a8e0f4b7dca708066 Mon Sep 17 00:00:00 2001 From: Arayi Khalatyan <127004086+arayikhalatyan@users.noreply.github.com> Date: Wed, 18 Dec 2024 17:43:17 -0500 Subject: [PATCH 11/11] feat: modularize toolchain tests (#1102) * feat: modularize toolchain tests * feat: name integration tests and update layout * feat: remove new_air_test_with_segments * feat: add to CI * feat: initial program lints * feat: book examples crate * fix: integration-tests.md --- .github/workflows/algebra-extension.yml | 6 + .github/workflows/bigint-extension.yml | 6 + .github/workflows/cli.yml | 41 ++-- .github/workflows/ecc.yml | 14 +- .github/workflows/keccak256-extension.yml | 6 + .github/workflows/riscv.yml | 2 +- .github/workflows/rv32im-extension.yml | 6 + .github/workflows/toolchain.yml | 6 - Cargo.lock | 127 ++++++++++ Cargo.toml | 6 + .../tests/programs/.cargo/config.toml | 10 - .../tests/programs/examples/empty.rs | 6 - .../tests/{programs => src}/README.md | 2 +- crates/toolchain/tests/src/basic_tests.rs | 228 ------------------ crates/toolchain/tests/src/ecc_tests.rs | 214 ---------------- crates/toolchain/tests/src/lib.rs | 93 ++++++- crates/toolchain/tests/src/utils.rs | 75 ------ .../tests/tests/riscv_test_vectors.rs | 6 +- .../toolchain/tests/tests/transpiler_tests.rs | 4 +- crates/vm/src/utils/stark_utils.rs | 52 +--- docs/crates/integration-tests.md | 62 +++++ docs/repo/layout.md | 13 +- examples/algebra/Cargo.toml | 22 ++ examples/algebra/openvm.toml | 8 + examples/algebra/src/main.rs | 45 ++++ examples/ecc/Cargo.toml | 23 ++ examples/ecc/openvm.toml | 11 + examples/ecc/src/main.rs | 39 +++ examples/i256/Cargo.toml | 19 ++ examples/i256/openvm.toml | 4 + examples/i256/src/main.rs | 40 +++ examples/keccak/Cargo.toml | 20 ++ examples/keccak/openvm.toml | 4 + examples/keccak/src/main.rs | 27 +++ examples/pairing/Cargo.toml | 27 +++ examples/pairing/openvm.toml | 15 ++ examples/pairing/src/main.rs | 58 +++++ examples/u256/Cargo.toml | 19 ++ examples/u256/openvm.toml | 4 + examples/u256/src/main.rs | 40 +++ extensions/algebra/tests/Cargo.toml | 30 +++ extensions/algebra/tests/programs/Cargo.toml | 32 +++ .../programs/examples/complex-secp256k1.rs | 0 .../programs/examples/complex-two-modulos.rs | 3 +- .../tests/programs/examples/little.rs | 6 +- .../tests/programs/examples/moduli_setup.rs | 0 extensions/algebra/tests/src/lib.rs | 92 +++++++ extensions/bigint/tests/Cargo.toml | 30 +++ extensions/bigint/tests/programs/Cargo.toml | 29 +++ .../tests/programs/examples/book-example2.rs | 40 +++ .../programs/examples/matrix-power-signed.rs | 0 .../examples/matrix-power-unsigned.rs | 0 extensions/bigint/tests/src/lib.rs | 48 ++++ extensions/ecc/tests/Cargo.toml | 29 +++ extensions/ecc/tests/programs/Cargo.toml | 45 ++++ .../tests/programs/examples/decompress.rs | 0 .../ecc}/tests/programs/examples/ec.rs | 0 .../ecc}/tests/programs/examples/ecdsa.rs | 0 extensions/ecc/tests/src/lib.rs | 101 ++++++++ extensions/keccak256/tests/Cargo.toml | 27 +++ .../keccak256/tests/programs/Cargo.toml | 29 +++ .../tests/programs/examples/keccak.rs | 0 extensions/keccak256/tests/src/lib.rs | 31 +++ extensions/pairing/tests/Cargo.toml | 34 +++ .../pairing}/tests/programs/Cargo.toml | 24 +- .../tests/programs/examples/final_exp_hint.rs | 0 .../tests/programs/examples/fp12_mul.rs | 0 .../tests/programs/examples/pairing_check.rs | 0 .../tests/programs/examples/pairing_line.rs | 0 .../programs/examples/pairing_miller_loop.rs | 0 .../programs/examples/pairing_miller_step.rs | 0 .../pairing/tests/src/lib.rs | 162 +++++++++---- extensions/rv32im/tests/Cargo.toml | 28 +++ extensions/rv32im/tests/programs/Cargo.toml | 29 +++ .../tests/programs/examples/collatz.rs | 0 .../tests/programs/examples/fibonacci.rs | 0 .../rv32im}/tests/programs/examples/hint.rs | 0 .../rv32im}/tests/programs/examples/print.rs | 0 .../rv32im}/tests/programs/examples/read.rs | 0 .../rv32im}/tests/programs/examples/reveal.rs | 0 .../tests/programs/examples/tiny-mem-test.rs | 0 extensions/rv32im/tests/src/lib.rs | 187 ++++++++++++++ 82 files changed, 1745 insertions(+), 701 deletions(-) delete mode 100644 crates/toolchain/tests/programs/.cargo/config.toml delete mode 100644 crates/toolchain/tests/programs/examples/empty.rs rename crates/toolchain/tests/{programs => src}/README.md (96%) delete mode 100644 crates/toolchain/tests/src/basic_tests.rs delete mode 100644 crates/toolchain/tests/src/ecc_tests.rs create mode 100644 docs/crates/integration-tests.md create mode 100644 examples/algebra/Cargo.toml create mode 100644 examples/algebra/openvm.toml create mode 100644 examples/algebra/src/main.rs create mode 100644 examples/ecc/Cargo.toml create mode 100644 examples/ecc/openvm.toml create mode 100644 examples/ecc/src/main.rs create mode 100644 examples/i256/Cargo.toml create mode 100644 examples/i256/openvm.toml create mode 100644 examples/i256/src/main.rs create mode 100644 examples/keccak/Cargo.toml create mode 100644 examples/keccak/openvm.toml create mode 100644 examples/keccak/src/main.rs create mode 100644 examples/pairing/Cargo.toml create mode 100644 examples/pairing/openvm.toml create mode 100644 examples/pairing/src/main.rs create mode 100644 examples/u256/Cargo.toml create mode 100644 examples/u256/openvm.toml create mode 100644 examples/u256/src/main.rs create mode 100644 extensions/algebra/tests/Cargo.toml create mode 100644 extensions/algebra/tests/programs/Cargo.toml rename crates/toolchain/tests/programs/examples/complex.rs => extensions/algebra/tests/programs/examples/complex-secp256k1.rs (100%) rename {crates/toolchain => extensions/algebra}/tests/programs/examples/complex-two-modulos.rs (86%) rename {crates/toolchain => extensions/algebra}/tests/programs/examples/little.rs (92%) rename {crates/toolchain => extensions/algebra}/tests/programs/examples/moduli_setup.rs (100%) create mode 100644 extensions/algebra/tests/src/lib.rs create mode 100644 extensions/bigint/tests/Cargo.toml create mode 100644 extensions/bigint/tests/programs/Cargo.toml create mode 100644 extensions/bigint/tests/programs/examples/book-example2.rs rename {crates/toolchain => extensions/bigint}/tests/programs/examples/matrix-power-signed.rs (100%) rename crates/toolchain/tests/programs/examples/matrix-power.rs => extensions/bigint/tests/programs/examples/matrix-power-unsigned.rs (100%) create mode 100644 extensions/bigint/tests/src/lib.rs create mode 100644 extensions/ecc/tests/Cargo.toml create mode 100644 extensions/ecc/tests/programs/Cargo.toml rename {crates/toolchain => extensions/ecc}/tests/programs/examples/decompress.rs (100%) rename {crates/toolchain => extensions/ecc}/tests/programs/examples/ec.rs (100%) rename {crates/toolchain => extensions/ecc}/tests/programs/examples/ecdsa.rs (100%) create mode 100644 extensions/ecc/tests/src/lib.rs create mode 100644 extensions/keccak256/tests/Cargo.toml create mode 100644 extensions/keccak256/tests/programs/Cargo.toml rename {crates/toolchain => extensions/keccak256}/tests/programs/examples/keccak.rs (100%) create mode 100644 extensions/keccak256/tests/src/lib.rs create mode 100644 extensions/pairing/tests/Cargo.toml rename {crates/toolchain => extensions/pairing}/tests/programs/Cargo.toml (72%) rename {crates/toolchain => extensions/pairing}/tests/programs/examples/final_exp_hint.rs (100%) rename {crates/toolchain => extensions/pairing}/tests/programs/examples/fp12_mul.rs (100%) rename {crates/toolchain => extensions/pairing}/tests/programs/examples/pairing_check.rs (100%) rename {crates/toolchain => extensions/pairing}/tests/programs/examples/pairing_line.rs (100%) rename {crates/toolchain => extensions/pairing}/tests/programs/examples/pairing_miller_loop.rs (100%) rename {crates/toolchain => extensions/pairing}/tests/programs/examples/pairing_miller_step.rs (100%) rename crates/toolchain/tests/src/pairing_tests.rs => extensions/pairing/tests/src/lib.rs (79%) create mode 100644 extensions/rv32im/tests/Cargo.toml create mode 100644 extensions/rv32im/tests/programs/Cargo.toml rename {crates/toolchain => extensions/rv32im}/tests/programs/examples/collatz.rs (100%) rename {crates/toolchain => extensions/rv32im}/tests/programs/examples/fibonacci.rs (100%) rename {crates/toolchain => extensions/rv32im}/tests/programs/examples/hint.rs (100%) rename {crates/toolchain => extensions/rv32im}/tests/programs/examples/print.rs (100%) rename {crates/toolchain => extensions/rv32im}/tests/programs/examples/read.rs (100%) rename {crates/toolchain => extensions/rv32im}/tests/programs/examples/reveal.rs (100%) rename {crates/toolchain => extensions/rv32im}/tests/programs/examples/tiny-mem-test.rs (100%) create mode 100644 extensions/rv32im/tests/src/lib.rs diff --git a/.github/workflows/algebra-extension.yml b/.github/workflows/algebra-extension.yml index cbc1d60c03..86d0b22365 100644 --- a/.github/workflows/algebra-extension.yml +++ b/.github/workflows/algebra-extension.yml @@ -34,3 +34,9 @@ jobs: working-directory: extensions/algebra/circuit run: | cargo nextest run --cargo-profile=fast + + - name: Run algebra integration tests + working-directory: extensions/algebra/tests + run: | + rustup component add rust-src --toolchain nightly-2024-10-30 + cargo nextest run --cargo-profile=fast diff --git a/.github/workflows/bigint-extension.yml b/.github/workflows/bigint-extension.yml index 901973a48d..d4f11e637d 100644 --- a/.github/workflows/bigint-extension.yml +++ b/.github/workflows/bigint-extension.yml @@ -34,3 +34,9 @@ jobs: working-directory: extensions/bigint/circuit run: | cargo nextest run --cargo-profile=fast + + - name: Run bigint integration tests + working-directory: extensions/bigint/tests + run: | + rustup component add rust-src --toolchain nightly-2024-10-30 + cargo nextest run --cargo-profile=fast diff --git a/.github/workflows/cli.yml b/.github/workflows/cli.yml index f939196356..c41924b180 100644 --- a/.github/workflows/cli.yml +++ b/.github/workflows/cli.yml @@ -10,6 +10,7 @@ on: - "crates/vm/**" - "crates/sdk/**" - "crates/cli/**" + - "examples/**" concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }} @@ -23,13 +24,12 @@ env: jobs: app-level-cli: runs-on: - - runs-on - - run-id=${{ github.run_id }} - - family=m7 + - runs-on=${{ github.run_id }} + - runner=32cpu-linux-arm64 steps: - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable + - uses: dtolnay/rust-toolchain@nightly - uses: Swatinem/rust-cache@v2 with: cache-on-failure: true @@ -37,25 +37,26 @@ jobs: - name: Install solc # svm should support arm64 linux run: (hash svm 2>/dev/null || cargo install --version 0.2.23 svm-rs) && svm install 0.8.19 && solc --version - - name: Install architecture specific tools + - name: Install tools run: | - arch=$(uname -m) - case $arch in - arm64|aarch64) - rustup component add rust-src --toolchain nightly-2024-10-30-aarch64-unknown-linux-gnu - ;; - x86_64|amd64) - rustup component add rust-src --toolchain nightly-2024-10-30-x86_64-unknown-linux-gnu - ;; - *) - echo "Unsupported architecture: $arch" - exit 1 - ;; - esac + rustup component add rust-src --toolchain nightly-2024-10-30 - - name: Setup halo2 + - name: Install cargo-openvm + working-directory: crates/cli + run: | + cargo install --force --locked --path . + + - name: Build book examples + working-directory: examples run: | - bash ./extensions/native/recursion/trusted_setup_s3.sh + for dir in */; do + if [ -f "${dir}Cargo.toml" ]; then + echo "Building ${dir%/}" + cd "$dir" + cargo openvm build + cd .. + fi + done # TODO: CLI build, transpile, run, (keygen), prove, contract, verify - name: Run app-level CLI commands diff --git a/.github/workflows/ecc.yml b/.github/workflows/ecc.yml index 16fe9814a6..bc8e017d1b 100644 --- a/.github/workflows/ecc.yml +++ b/.github/workflows/ecc.yml @@ -50,13 +50,25 @@ jobs: run: | RUST_MIN_STACK=8388608 cargo nextest run --cargo-profile=fast + - name: Run pairing integration tests + working-directory: extensions/pairing/tests + run: | + rustup component add rust-src --toolchain nightly-2024-10-30 + RUST_MIN_STACK=8388608 cargo nextest run --cargo-profile=fast + - name: Install cargo-openvm working-directory: crates/cli run: | - rustup component add rust-src --toolchain nightly-2024-10-30-aarch64-unknown-linux-gnu + rustup component add rust-src --toolchain nightly-2024-10-30 cargo install --force --locked --path . - name: Build openvm-ecc-guest crate for openvm working-directory: extensions/ecc/guest run: | cargo openvm build --no-transpile + + - name: Run ecc integration tests + working-directory: extensions/ecc/tests + run: | + rustup component add rust-src --toolchain nightly-2024-10-30 + cargo nextest run --cargo-profile=fast diff --git a/.github/workflows/keccak256-extension.yml b/.github/workflows/keccak256-extension.yml index 9b99d0f1a9..49ebcebbea 100644 --- a/.github/workflows/keccak256-extension.yml +++ b/.github/workflows/keccak256-extension.yml @@ -34,3 +34,9 @@ jobs: working-directory: extensions/keccak256/circuit run: | cargo nextest run --cargo-profile=fast + + - name: Run keccak256 integration tests + working-directory: extensions/keccak256/tests + run: | + rustup component add rust-src --toolchain nightly-2024-10-30 + cargo nextest run --cargo-profile=fast diff --git a/.github/workflows/riscv.yml b/.github/workflows/riscv.yml index 7b4c27e77d..2cfe053fa4 100644 --- a/.github/workflows/riscv.yml +++ b/.github/workflows/riscv.yml @@ -49,5 +49,5 @@ jobs: - name: Run RISC-V test vector tests working-directory: crates/toolchain/tests run: | - rustup component add rust-src --toolchain nightly-2024-10-30-aarch64-unknown-linux-gnu + rustup component add rust-src --toolchain nightly-2024-10-30 cargo nextest run --cargo-profile=fast --run-ignored only -- test_rv32im_riscv_vector_runtime diff --git a/.github/workflows/rv32im-extension.yml b/.github/workflows/rv32im-extension.yml index 9e69f3a7ef..8431bf845a 100644 --- a/.github/workflows/rv32im-extension.yml +++ b/.github/workflows/rv32im-extension.yml @@ -34,3 +34,9 @@ jobs: working-directory: extensions/rv32im/circuit run: | cargo nextest run --cargo-profile=fast + + - name: Run rv32im integration tests + working-directory: extensions/rv32im/tests + run: | + rustup component add rust-src --toolchain nightly-2024-10-30 + cargo nextest run --cargo-profile=fast diff --git a/.github/workflows/toolchain.yml b/.github/workflows/toolchain.yml index 2064a0e9d8..5a0c6b852c 100644 --- a/.github/workflows/toolchain.yml +++ b/.github/workflows/toolchain.yml @@ -31,9 +31,3 @@ jobs: with: cache-on-failure: true - uses: taiki-e/install-action@nextest - - - name: Run toolchain tests - working-directory: crates/toolchain/tests - run: | - rustup component add rust-src --toolchain nightly-2024-10-30-aarch64-unknown-linux-gnu - RUST_MIN_STACK=8388608 cargo nextest run --cargo-profile=fast diff --git a/Cargo.lock b/Cargo.lock index eb0ac9efee..d397fcacdb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3479,6 +3479,27 @@ dependencies = [ "syn 2.0.90", ] +[[package]] +name = "openvm-algebra-tests" +version = "0.1.0-alpha" +dependencies = [ + "eyre", + "num-bigint-dig", + "openvm", + "openvm-algebra-circuit", + "openvm-algebra-transpiler", + "openvm-build", + "openvm-circuit", + "openvm-circuit-primitives-derive", + "openvm-ecc-circuit", + "openvm-instructions", + "openvm-platform", + "openvm-rv32im-transpiler", + "openvm-stark-sdk", + "openvm-toolchain-tests", + "openvm-transpiler", +] + [[package]] name = "openvm-algebra-transpiler" version = "0.1.0-alpha" @@ -3572,6 +3593,26 @@ dependencies = [ "strum_macros", ] +[[package]] +name = "openvm-bigint-integration-tests" +version = "0.1.0-alpha" +dependencies = [ + "eyre", + "num-bigint-dig", + "openvm", + "openvm-bigint-circuit", + "openvm-bigint-transpiler", + "openvm-build", + "openvm-circuit", + "openvm-circuit-primitives-derive", + "openvm-instructions", + "openvm-platform", + "openvm-rv32im-transpiler", + "openvm-stark-sdk", + "openvm-toolchain-tests", + "openvm-transpiler", +] + [[package]] name = "openvm-bigint-transpiler" version = "0.1.0-alpha" @@ -3765,6 +3806,27 @@ dependencies = [ "strum_macros", ] +[[package]] +name = "openvm-ecc-integration-tests" +version = "0.1.0-alpha" +dependencies = [ + "eyre", + "openvm-algebra-circuit", + "openvm-algebra-transpiler", + "openvm-circuit", + "openvm-circuit-primitives-derive", + "openvm-ecc-circuit", + "openvm-ecc-guest", + "openvm-ecc-transpiler", + "openvm-instructions", + "openvm-keccak256-transpiler", + "openvm-rv32im-transpiler", + "openvm-sdk", + "openvm-stark-sdk", + "openvm-toolchain-tests", + "openvm-transpiler", +] + [[package]] name = "openvm-ecc-sw-setup" version = "0.1.0-alpha" @@ -3855,6 +3917,25 @@ dependencies = [ "tiny-keccak", ] +[[package]] +name = "openvm-keccak256-integration-tests" +version = "0.1.0-alpha" +dependencies = [ + "eyre", + "openvm", + "openvm-build", + "openvm-circuit", + "openvm-circuit-primitives-derive", + "openvm-instructions", + "openvm-keccak256-circuit", + "openvm-keccak256-transpiler", + "openvm-platform", + "openvm-rv32im-transpiler", + "openvm-stark-sdk", + "openvm-toolchain-tests", + "openvm-transpiler", +] + [[package]] name = "openvm-keccak256-transpiler" version = "0.1.0-alpha" @@ -4061,6 +4142,32 @@ dependencies = [ "subtle", ] +[[package]] +name = "openvm-pairing-integration-tests" +version = "0.1.0-alpha" +dependencies = [ + "eyre", + "num-bigint-dig", + "openvm", + "openvm-algebra-circuit", + "openvm-algebra-transpiler", + "openvm-build", + "openvm-circuit", + "openvm-circuit-primitives-derive", + "openvm-ecc-circuit", + "openvm-ecc-guest", + "openvm-instructions", + "openvm-pairing-circuit", + "openvm-pairing-guest", + "openvm-pairing-transpiler", + "openvm-platform", + "openvm-rv32im-transpiler", + "openvm-stark-sdk", + "openvm-toolchain-tests", + "openvm-transpiler", + "rand", +] + [[package]] name = "openvm-pairing-transpiler" version = "0.1.0-alpha" @@ -4162,6 +4269,26 @@ dependencies = [ "strum_macros", ] +[[package]] +name = "openvm-rv32im-integration-tests" +version = "0.1.0-alpha" +dependencies = [ + "eyre", + "openvm", + "openvm-build", + "openvm-circuit", + "openvm-circuit-primitives-derive", + "openvm-instructions", + "openvm-platform", + "openvm-rv32im-circuit", + "openvm-rv32im-transpiler", + "openvm-stark-sdk", + "openvm-toolchain-tests", + "openvm-transpiler", + "serde", + "test-case", +] + [[package]] name = "openvm-rv32im-transpiler" version = "0.1.0-alpha" diff --git a/Cargo.toml b/Cargo.toml index 4224139302..c8625a62d1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,12 +29,15 @@ members = [ "extensions/algebra/transpiler", "extensions/algebra/guest", "extensions/algebra/moduli-setup", + "extensions/algebra/tests", "extensions/bigint/circuit", "extensions/bigint/transpiler", "extensions/bigint/guest", + "extensions/bigint/tests", "extensions/keccak256/circuit", "extensions/keccak256/transpiler", "extensions/keccak256/guest", + "extensions/keccak256/tests", "extensions/native/circuit", "extensions/native/compiler", "extensions/native/compiler/derive", @@ -46,10 +49,13 @@ members = [ "extensions/ecc/transpiler", "extensions/ecc/guest", "extensions/ecc/sw-setup", + "extensions/ecc/tests", "extensions/pairing/circuit", "extensions/pairing/transpiler", "extensions/pairing/guest", + "extensions/pairing/tests", "extensions/rv32-adapters", + "extensions/rv32im/tests", ] exclude = ["crates/sdk/example"] resolver = "2" diff --git a/crates/toolchain/tests/programs/.cargo/config.toml b/crates/toolchain/tests/programs/.cargo/config.toml deleted file mode 100644 index af89db93c0..0000000000 --- a/crates/toolchain/tests/programs/.cargo/config.toml +++ /dev/null @@ -1,10 +0,0 @@ -# # Uncomment to build for openvm -# [build] -# target = "riscv32im-risc0-zkvm-elf" -# -# [target.riscv32im-risc0-zkvm-elf] -# rustflags = ["-C", "passes=lower-atomic", "-C", "link-arg=-Ttext=0x002008000"] -# -# [unstable] -# build-std = ["core", "alloc", "proc_macro", "panic_abort", "std"] -# build-std-features = ["compiler-builtins-mem"] diff --git a/crates/toolchain/tests/programs/examples/empty.rs b/crates/toolchain/tests/programs/examples/empty.rs deleted file mode 100644 index 59aed62604..0000000000 --- a/crates/toolchain/tests/programs/examples/empty.rs +++ /dev/null @@ -1,6 +0,0 @@ -#![no_main] -#![no_std] - -openvm::entry!(main); - -pub fn main() {} diff --git a/crates/toolchain/tests/programs/README.md b/crates/toolchain/tests/src/README.md similarity index 96% rename from crates/toolchain/tests/programs/README.md rename to crates/toolchain/tests/src/README.md index 21a475d5ed..3134c7a697 100644 --- a/crates/toolchain/tests/programs/README.md +++ b/crates/toolchain/tests/src/README.md @@ -29,4 +29,4 @@ To disassemble the ELF to read the instructions, [install cargo-binutils](https: rust-objdump -d target/riscv32im-risc0-zkvm-elf/debug/examples/openvm-fibonacci-program ``` -where `-d` is short for `--disassemble`. +where `-d` is short for `--disassemble`. \ No newline at end of file diff --git a/crates/toolchain/tests/src/basic_tests.rs b/crates/toolchain/tests/src/basic_tests.rs deleted file mode 100644 index 13b6d8c487..0000000000 --- a/crates/toolchain/tests/src/basic_tests.rs +++ /dev/null @@ -1,228 +0,0 @@ -use eyre::Result; -use openvm_bigint_circuit::Int256Rv32Config; -use openvm_bigint_transpiler::Int256TranspilerExtension; -use openvm_circuit::{ - arch::{hasher::poseidon2::vm_poseidon2_hasher, instructions::exe::VmExe, VmExecutor}, - system::memory::tree::public_values::UserPublicValuesProof, - utils::new_air_test_with_min_segments, -}; -use openvm_keccak256_circuit::Keccak256Rv32Config; -use openvm_keccak256_transpiler::Keccak256TranspilerExtension; -use openvm_rv32im_circuit::{Rv32IConfig, Rv32ImConfig}; -use openvm_rv32im_transpiler::{ - Rv32ITranspilerExtension, Rv32IoTranspilerExtension, Rv32MTranspilerExtension, -}; -use openvm_stark_sdk::{openvm_stark_backend::p3_field::AbstractField, p3_baby_bear::BabyBear}; -use openvm_transpiler::{elf::ELF_DEFAULT_MAX_NUM_PUBLIC_VALUES, transpiler::Transpiler, FromElf}; -use test_case::test_case; - -use crate::utils::{build_example_program, build_example_program_with_features}; - -type F = BabyBear; - -#[test_case("fibonacci", 1)] -fn test_rv32i_prove(example_name: &str, min_segments: usize) -> Result<()> { - let elf = build_example_program(example_name)?; - let exe = VmExe::from_elf( - elf, - Transpiler::::default() - .with_extension(Rv32ITranspilerExtension) - .with_extension(Rv32MTranspilerExtension) - .with_extension(Rv32IoTranspilerExtension), - )?; - let config = Rv32IConfig::default(); - new_air_test_with_min_segments(config, exe, vec![], min_segments, true); - Ok(()) -} - -#[test_case("collatz", 1)] -fn test_rv32im_prove(example_name: &str, min_segments: usize) -> Result<()> { - let elf = build_example_program(example_name)?; - let exe = VmExe::from_elf( - elf, - Transpiler::::default() - .with_extension(Rv32ITranspilerExtension) - .with_extension(Rv32IoTranspilerExtension) - .with_extension(Rv32MTranspilerExtension), - )?; - let config = Rv32ImConfig::default(); - new_air_test_with_min_segments(config, exe, vec![], min_segments, true); - Ok(()) -} - -// #[test_case("fibonacci", 1)] -#[test_case("collatz", 1)] -fn test_rv32im_std_prove(example_name: &str, min_segments: usize) -> Result<()> { - let elf = build_example_program_with_features(example_name, ["std"])?; - let exe = VmExe::from_elf( - elf, - Transpiler::::default() - .with_extension(Rv32ITranspilerExtension) - .with_extension(Rv32IoTranspilerExtension) - .with_extension(Rv32MTranspilerExtension), - )?; - let config = Rv32ImConfig::default(); - new_air_test_with_min_segments(config, exe, vec![], min_segments, true); - Ok(()) -} - -#[test] -fn test_read_vec_runtime() -> Result<()> { - let elf = build_example_program("hint")?; - let exe = VmExe::from_elf( - elf, - Transpiler::::default() - .with_extension(Rv32ITranspilerExtension) - .with_extension(Rv32MTranspilerExtension) - .with_extension(Rv32IoTranspilerExtension), - )?; - let config = Rv32IConfig::default(); - let executor = VmExecutor::::new(config); - executor.execute(exe, vec![[0, 1, 2, 3].map(F::from_canonical_u8).to_vec()])?; - Ok(()) -} - -#[test] -fn test_read_runtime() -> Result<()> { - let elf = build_example_program("read")?; - let exe = VmExe::from_elf( - elf, - Transpiler::::default() - .with_extension(Rv32ITranspilerExtension) - .with_extension(Rv32MTranspilerExtension) - .with_extension(Rv32IoTranspilerExtension), - )?; - let config = Rv32IConfig::default(); - let executor = VmExecutor::::new(config); - - #[derive(serde::Serialize)] - struct Foo { - bar: u32, - baz: Vec, - } - let foo = Foo { - bar: 42, - baz: vec![0, 1, 2, 3], - }; - let serialized_foo = openvm::serde::to_vec(&foo).unwrap(); - let input = serialized_foo - .into_iter() - .flat_map(|w| w.to_le_bytes()) - .map(F::from_canonical_u8) - .collect(); - executor.execute(exe, vec![input]).unwrap(); - Ok(()) -} - -#[test] -fn test_reveal_runtime() -> Result<()> { - let elf = build_example_program("reveal")?; - let exe = VmExe::from_elf( - elf, - Transpiler::::default() - .with_extension(Rv32ITranspilerExtension) - .with_extension(Rv32MTranspilerExtension) - .with_extension(Rv32IoTranspilerExtension), - )?; - let config = Rv32IConfig::default(); - let executor = VmExecutor::::new(config.clone()); - let final_memory = executor.execute(exe, vec![])?.unwrap(); - let hasher = vm_poseidon2_hasher(); - let pv_proof = UserPublicValuesProof::compute( - config.system.memory_config.memory_dimensions(), - ELF_DEFAULT_MAX_NUM_PUBLIC_VALUES, - &hasher, - &final_memory, - ); - assert_eq!( - pv_proof.public_values, - [123, 0, 456, 0u32, 0u32, 0u32, 0u32, 0u32] - .into_iter() - .flat_map(|x| x.to_le_bytes()) - .map(F::from_canonical_u8) - .collect::>() - ); - Ok(()) -} - -#[test] -fn test_keccak256_runtime() -> Result<()> { - let elf = build_example_program("keccak")?; - let openvm_exe = VmExe::from_elf( - elf, - Transpiler::::default() - .with_extension(Keccak256TranspilerExtension) - .with_extension(Rv32ITranspilerExtension) - .with_extension(Rv32MTranspilerExtension) - .with_extension(Rv32IoTranspilerExtension), - )?; - let executor = VmExecutor::::new(Keccak256Rv32Config::default()); - executor.execute(openvm_exe, vec![])?; - Ok(()) -} - -#[test] -fn test_print_runtime() -> Result<()> { - let elf = build_example_program("print")?; - let exe = VmExe::from_elf( - elf, - Transpiler::::default() - .with_extension(Rv32ITranspilerExtension) - .with_extension(Rv32MTranspilerExtension) - .with_extension(Rv32IoTranspilerExtension), - )?; - let config = Rv32IConfig::default(); - let executor = VmExecutor::::new(config); - executor.execute(exe, vec![])?; - Ok(()) -} - -#[test] -fn test_matrix_power_runtime() -> Result<()> { - let elf = build_example_program("matrix-power")?; - let openvm_exe = VmExe::from_elf( - elf, - Transpiler::::default() - .with_extension(Rv32ITranspilerExtension) - .with_extension(Rv32MTranspilerExtension) - .with_extension(Rv32IoTranspilerExtension) - .with_extension(Int256TranspilerExtension), - )?; - let config = Int256Rv32Config::default(); - let executor = VmExecutor::::new(config); - executor.execute(openvm_exe, vec![])?; - Ok(()) -} - -#[test] -fn test_matrix_power_signed_runtime() -> Result<()> { - let elf = build_example_program("matrix-power-signed")?; - let openvm_exe = VmExe::from_elf( - elf, - Transpiler::::default() - .with_extension(Rv32ITranspilerExtension) - .with_extension(Rv32MTranspilerExtension) - .with_extension(Rv32IoTranspilerExtension) - .with_extension(Int256TranspilerExtension), - )?; - let config = Int256Rv32Config::default(); - let executor = VmExecutor::::new(config); - executor.execute(openvm_exe, vec![])?; - Ok(()) -} - -#[test] -fn test_tiny_mem_test_runtime() -> Result<()> { - let elf = build_example_program_with_features("tiny-mem-test", ["heap-embedded-alloc"])?; - let exe = VmExe::from_elf( - elf, - Transpiler::::default() - .with_extension(Rv32ITranspilerExtension) - .with_extension(Rv32MTranspilerExtension) - .with_extension(Rv32IoTranspilerExtension), - )?; - let config = Rv32ImConfig::default(); - let executor = VmExecutor::::new(config); - executor.execute(exe, vec![])?; - Ok(()) -} diff --git a/crates/toolchain/tests/src/ecc_tests.rs b/crates/toolchain/tests/src/ecc_tests.rs deleted file mode 100644 index 510a94e5e3..0000000000 --- a/crates/toolchain/tests/src/ecc_tests.rs +++ /dev/null @@ -1,214 +0,0 @@ -use std::str::FromStr; - -use derive_more::derive::From; -use eyre::Result; -use num_bigint_dig::BigUint; -use openvm_algebra_circuit::{ - ModularExtension, ModularExtensionExecutor, ModularExtensionPeriphery, Rv32ModularConfig, - Rv32ModularWithFp2Config, -}; -use openvm_algebra_transpiler::{Fp2TranspilerExtension, ModularTranspilerExtension}; -use openvm_circuit::{ - arch::{ - instructions::exe::VmExe, SystemConfig, SystemExecutor, SystemPeriphery, VmChipComplex, - VmConfig, VmInventoryError, - }, - derive::{AnyEnum, InstructionExecutor, VmConfig}, - utils::new_air_test_with_min_segments, -}; -use openvm_circuit_primitives_derive::{Chip, ChipUsageGetter}; -use openvm_ecc_circuit::{ - CurveConfig, Rv32WeierstrassConfig, WeierstrassExtension, WeierstrassExtensionExecutor, - WeierstrassExtensionPeriphery, SECP256K1_CONFIG, -}; -use openvm_ecc_transpiler::EccTranspilerExtension; -use openvm_keccak256_circuit::{Keccak256, Keccak256Executor, Keccak256Periphery}; -use openvm_keccak256_transpiler::Keccak256TranspilerExtension; -use openvm_rv32im_circuit::{ - Rv32I, Rv32IExecutor, Rv32IPeriphery, Rv32Io, Rv32IoExecutor, Rv32IoPeriphery, Rv32M, - Rv32MExecutor, Rv32MPeriphery, -}; -use openvm_rv32im_transpiler::{ - Rv32ITranspilerExtension, Rv32IoTranspilerExtension, Rv32MTranspilerExtension, -}; -use openvm_stark_backend::p3_field::{AbstractField, PrimeField32}; -use openvm_stark_sdk::p3_baby_bear::BabyBear; -use openvm_transpiler::{transpiler::Transpiler, FromElf}; -use serde::{Deserialize, Serialize}; - -use crate::utils::{build_example_program, build_example_program_with_features}; - -type F = BabyBear; - -#[test] -fn test_moduli_setup_runtime() -> Result<()> { - let elf = build_example_program("moduli_setup")?; - let openvm_exe = VmExe::from_elf( - elf, - Transpiler::::default() - .with_extension(Rv32ITranspilerExtension) - .with_extension(Rv32MTranspilerExtension) - .with_extension(Rv32IoTranspilerExtension) - .with_extension(ModularTranspilerExtension), - )?; - - let moduli = ["4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787", "1000000000000000003", "2305843009213693951"] - .map(|s| num_bigint_dig::BigUint::from_str(s).unwrap()); - let config = Rv32ModularConfig::new(moduli.to_vec()); - new_air_test_with_min_segments(config, openvm_exe, vec![], 1, false); - Ok(()) -} - -#[test] -fn test_modular_runtime() -> Result<()> { - let elf = build_example_program("little")?; - let openvm_exe = VmExe::from_elf( - elf, - Transpiler::::default() - .with_extension(Rv32ITranspilerExtension) - .with_extension(Rv32MTranspilerExtension) - .with_extension(Rv32IoTranspilerExtension) - .with_extension(ModularTranspilerExtension), - )?; - let config = Rv32ModularConfig::new(vec![SECP256K1_CONFIG.modulus.clone()]); - new_air_test_with_min_segments(config, openvm_exe, vec![], 1, false); - Ok(()) -} - -#[test] -fn test_complex_runtime() -> Result<()> { - let elf = build_example_program("complex")?; - let openvm_exe = VmExe::from_elf( - elf, - Transpiler::::default() - .with_extension(Rv32ITranspilerExtension) - .with_extension(Rv32MTranspilerExtension) - .with_extension(Rv32IoTranspilerExtension) - .with_extension(Fp2TranspilerExtension) - .with_extension(ModularTranspilerExtension), - )?; - let config = Rv32ModularWithFp2Config::new(vec![SECP256K1_CONFIG.modulus.clone()]); - // Always run prove, as this caught a bug before. - new_air_test_with_min_segments(config, openvm_exe, vec![], 1, true); - Ok(()) -} - -#[test] -fn test_complex_two_moduli_runtime() -> Result<()> { - let elf = build_example_program("complex-two-modulos")?; - let openvm_exe = VmExe::from_elf( - elf, - Transpiler::::default() - .with_extension(Rv32ITranspilerExtension) - .with_extension(Rv32MTranspilerExtension) - .with_extension(Rv32IoTranspilerExtension) - .with_extension(Fp2TranspilerExtension) - .with_extension(ModularTranspilerExtension), - )?; - let config = Rv32ModularWithFp2Config::new(vec![ - BigUint::from_str("998244353").unwrap(), - BigUint::from_str("1000000007").unwrap(), - ]); - new_air_test_with_min_segments(config, openvm_exe, vec![], 1, false); - Ok(()) -} - -#[test] -fn test_ec_runtime() -> Result<()> { - let elf = build_example_program_with_features("ec", ["k256"])?; - let openvm_exe = VmExe::from_elf( - elf, - Transpiler::::default() - .with_extension(Rv32ITranspilerExtension) - .with_extension(Rv32MTranspilerExtension) - .with_extension(Rv32IoTranspilerExtension) - .with_extension(EccTranspilerExtension) - .with_extension(ModularTranspilerExtension), - )?; - let config = Rv32WeierstrassConfig::new(vec![SECP256K1_CONFIG.clone()]); - new_air_test_with_min_segments(config, openvm_exe, vec![], 1, false); - Ok(()) -} - -#[test] -fn test_decompress() -> Result<()> { - use openvm_ecc_guest::halo2curves::{group::Curve, secp256k1::Secp256k1Affine}; - - let elf = build_example_program_with_features("decompress", ["k256"])?; - let openvm_exe = VmExe::from_elf( - elf, - Transpiler::::default() - .with_extension(Rv32ITranspilerExtension) - .with_extension(Rv32MTranspilerExtension) - .with_extension(Rv32IoTranspilerExtension) - .with_extension(EccTranspilerExtension) - .with_extension(ModularTranspilerExtension), - )?; - let config = Rv32WeierstrassConfig::new(vec![SECP256K1_CONFIG.clone()]); - - let p = Secp256k1Affine::generator(); - let p = (p + p + p).to_affine(); - println!("decompressed: {:?}", p); - let coords: Vec<_> = [p.x.to_bytes(), p.y.to_bytes()] - .concat() - .into_iter() - .map(AbstractField::from_canonical_u8) - .collect(); - new_air_test_with_min_segments(config, openvm_exe, vec![coords], 1, false); - Ok(()) -} - -#[derive(Clone, Debug, VmConfig, Serialize, Deserialize)] -pub struct Rv32ModularKeccak256Config { - #[system] - pub system: SystemConfig, - #[extension] - pub base: Rv32I, - #[extension] - pub mul: Rv32M, - #[extension] - pub io: Rv32Io, - #[extension] - pub modular: ModularExtension, - #[extension] - pub keccak: Keccak256, - #[extension] - pub weierstrass: WeierstrassExtension, -} - -impl Rv32ModularKeccak256Config { - pub fn new(curves: Vec) -> Self { - let primes: Vec = curves - .iter() - .flat_map(|c| [c.modulus.clone(), c.scalar.clone()]) - .collect(); - Self { - system: SystemConfig::default().with_continuations(), - base: Default::default(), - mul: Default::default(), - io: Default::default(), - modular: ModularExtension::new(primes), - keccak: Default::default(), - weierstrass: WeierstrassExtension::new(curves), - } - } -} - -#[test] -fn test_ecdsa_runtime() -> Result<()> { - let elf = build_example_program_with_features("ecdsa", ["k256"])?; - let config = Rv32ModularKeccak256Config::new(vec![SECP256K1_CONFIG.clone()]); - - let openvm_exe = VmExe::from_elf( - elf, - Transpiler::::default() - .with_extension(Rv32ITranspilerExtension) - .with_extension(Rv32MTranspilerExtension) - .with_extension(Rv32IoTranspilerExtension) - .with_extension(Keccak256TranspilerExtension) - .with_extension(EccTranspilerExtension) - .with_extension(ModularTranspilerExtension), - )?; - new_air_test_with_min_segments(config, openvm_exe, vec![], 1, true); - Ok(()) -} diff --git a/crates/toolchain/tests/src/lib.rs b/crates/toolchain/tests/src/lib.rs index 4fcef2270b..8e87641d1c 100644 --- a/crates/toolchain/tests/src/lib.rs +++ b/crates/toolchain/tests/src/lib.rs @@ -1,10 +1,87 @@ -//! Unit tests for OpenVM toolchain starting from rust +use std::{ + fs::read, + path::{Path, PathBuf}, +}; -pub mod utils; +use eyre::Result; +use openvm_build::{build_guest_package, get_package, is_debug, GuestOptions, TargetFilter}; +use openvm_transpiler::{elf::Elf, openvm_platform::memory::MEM_SIZE}; +use tempfile::tempdir; -#[cfg(test)] -pub mod basic_tests; -#[cfg(test)] -pub mod ecc_tests; -#[cfg(test)] -pub mod pairing_tests; +#[macro_export] +macro_rules! get_programs_dir { + () => {{ + let mut dir = ::std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).to_path_buf(); + dir.push("programs"); + dir + }}; + ($subdir:expr) => {{ + let mut dir = ::std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).to_path_buf(); + dir.push($subdir); + dir + }}; +} + +pub fn decode_elf(elf_path: impl AsRef) -> Result { + let dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + let data = read(dir.join(elf_path))?; + Elf::decode(&data, MEM_SIZE as u32) +} + +pub fn build_example_program(example_name: &str) -> Result { + build_example_program_with_features::<&str>(example_name, []) +} + +pub fn build_example_program_with_features>( + example_name: &str, + features: impl IntoIterator, +) -> Result { + let manifest_dir = get_programs_dir!(); + build_example_program_at_path_with_features(manifest_dir, example_name, features) +} + +pub fn build_example_program_at_path(manifest_dir: PathBuf, example_name: &str) -> Result { + build_example_program_at_path_with_features::<&str>(manifest_dir, example_name, []) +} + +pub fn build_example_program_at_path_with_features>( + manifest_dir: PathBuf, + example_name: &str, + features: impl IntoIterator, +) -> Result { + let pkg = get_package(manifest_dir); + let target_dir = tempdir()?; + // Build guest with default features + let guest_opts = GuestOptions::default() + .with_features(features) + .with_target_dir(target_dir.path()); + if let Err(Some(code)) = build_guest_package( + &pkg, + &guest_opts, + None, + &Some(TargetFilter { + name: example_name.to_string(), + kind: "example".to_string(), + }), + ) { + std::process::exit(code); + } + // Assumes the package has a single target binary + let profile = if is_debug() { "debug" } else { "release" }; + let elf_path = pkg + .targets + .iter() + .find(|target| target.name == example_name) + .map(|target| { + target_dir + .as_ref() + .join("riscv32im-risc0-zkvm-elf") + .join(profile) + .join("examples") + .join(&target.name) + .to_path_buf() + }) + .expect("Could not find target binary"); + let data = read(elf_path)?; + Elf::decode(&data, MEM_SIZE as u32) +} diff --git a/crates/toolchain/tests/src/utils.rs b/crates/toolchain/tests/src/utils.rs index eb743efd04..e69de29bb2 100644 --- a/crates/toolchain/tests/src/utils.rs +++ b/crates/toolchain/tests/src/utils.rs @@ -1,75 +0,0 @@ -use std::{ - fs::read, - path::{Path, PathBuf}, -}; - -use eyre::Result; -use openvm_build::{build_guest_package, get_package, is_debug, GuestOptions, TargetFilter}; -use openvm_transpiler::{elf::Elf, openvm_platform::memory::MEM_SIZE}; -use tempfile::tempdir; - -fn get_programs_dir() -> PathBuf { - let mut dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).to_path_buf(); - dir.push("programs"); - dir -} - -pub fn decode_elf(elf_path: impl AsRef) -> Result { - let dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - let data = read(dir.join(elf_path))?; - Elf::decode(&data, MEM_SIZE as u32) -} - -pub fn build_example_program(example_name: &str) -> Result { - build_example_program_with_features::<&str>(example_name, []) -} - -pub fn build_example_program_with_features>( - example_name: &str, - features: impl IntoIterator, -) -> Result { - let manifest_dir = get_programs_dir(); - build_example_program_at_path_with_features(manifest_dir, example_name, features) -} - -pub fn build_example_program_at_path_with_features>( - manifest_dir: PathBuf, - example_name: &str, - features: impl IntoIterator, -) -> Result { - let pkg = get_package(manifest_dir); - let target_dir = tempdir()?; - // Build guest with default features - let guest_opts = GuestOptions::default() - .with_features(features) - .with_target_dir(target_dir.path()); - if let Err(Some(code)) = build_guest_package( - &pkg, - &guest_opts, - None, - &Some(TargetFilter { - name: example_name.to_string(), - kind: "example".to_string(), - }), - ) { - std::process::exit(code); - } - // Assumes the package has a single target binary - let profile = if is_debug() { "debug" } else { "release" }; - let elf_path = pkg - .targets - .iter() - .find(|target| target.name == example_name) - .map(|target| { - target_dir - .as_ref() - .join("riscv32im-risc0-zkvm-elf") - .join(profile) - .join("examples") - .join(&target.name) - .to_path_buf() - }) - .expect("Could not find target binary"); - let data = read(elf_path)?; - Elf::decode(&data, MEM_SIZE as u32) -} diff --git a/crates/toolchain/tests/tests/riscv_test_vectors.rs b/crates/toolchain/tests/tests/riscv_test_vectors.rs index 655381cf12..9516b0cd7b 100644 --- a/crates/toolchain/tests/tests/riscv_test_vectors.rs +++ b/crates/toolchain/tests/tests/riscv_test_vectors.rs @@ -3,14 +3,14 @@ use std::{fs::read_dir, path::PathBuf}; use eyre::Result; use openvm_circuit::{ arch::{instructions::exe::VmExe, VmExecutor}, - utils::new_air_test_with_min_segments, + utils::air_test, }; use openvm_rv32im_circuit::Rv32ImConfig; use openvm_rv32im_transpiler::{ Rv32ITranspilerExtension, Rv32IoTranspilerExtension, Rv32MTranspilerExtension, }; use openvm_stark_sdk::p3_baby_bear::BabyBear; -use openvm_toolchain_tests::utils::decode_elf; +use openvm_toolchain_tests::decode_elf; use openvm_transpiler::{transpiler::Transpiler, FromElf}; type F = BabyBear; @@ -80,7 +80,7 @@ fn test_rv32im_riscv_vector_prove() -> Result<()> { )?; let result = std::panic::catch_unwind(|| { - new_air_test_with_min_segments(config.clone(), exe, vec![], 1, true); + air_test(config.clone(), exe); }); match result { diff --git a/crates/toolchain/tests/tests/transpiler_tests.rs b/crates/toolchain/tests/tests/transpiler_tests.rs index 75af59b1a2..da224dd14b 100644 --- a/crates/toolchain/tests/tests/transpiler_tests.rs +++ b/crates/toolchain/tests/tests/transpiler_tests.rs @@ -18,7 +18,7 @@ use openvm_circuit::{ VmInventoryError, }, derive::{AnyEnum, InstructionExecutor, VmConfig}, - utils::new_air_test_with_min_segments, + utils::air_test, }; use openvm_circuit_primitives_derive::{Chip, ChipUsageGetter}; use openvm_ecc_guest::k256::{SECP256K1_MODULUS, SECP256K1_ORDER}; @@ -159,6 +159,6 @@ fn test_terminate_prove() -> Result<()> { .with_extension(Rv32IoTranspilerExtension) .with_extension(ModularTranspilerExtension), )?; - new_air_test_with_min_segments(config, openvm_exe, vec![], 1, true); + air_test(config, openvm_exe); Ok(()) } diff --git a/crates/vm/src/utils/stark_utils.rs b/crates/vm/src/utils/stark_utils.rs index 8281763489..2edae976b4 100644 --- a/crates/vm/src/utils/stark_utils.rs +++ b/crates/vm/src/utils/stark_utils.rs @@ -1,9 +1,7 @@ -use std::borrow::Borrow; - use openvm_instructions::{exe::VmExe, program::Program}; use openvm_stark_backend::{ config::{StarkGenericConfig, Val}, - p3_field::{AbstractField, PrimeField32}, + p3_field::PrimeField32, verifier::VerificationError, Chip, }; @@ -16,12 +14,9 @@ use openvm_stark_sdk::{ p3_baby_bear::BabyBear, }; -use crate::{ - arch::{ - vm::{VirtualMachine, VmExecutor}, - ExitCode, Streams, VmConfig, VmMemoryState, CONNECTOR_AIR_ID, - }, - system::connector::VmConnectorPvs, +use crate::arch::{ + vm::{VirtualMachine, VmExecutor}, + Streams, VmConfig, VmMemoryState, }; pub fn air_test(config: VC, exe: impl Into>) @@ -59,45 +54,6 @@ where final_memory } -/// Executes the VM and returns the final memory state. -pub fn new_air_test_with_min_segments( - config: VC, - exe: impl Into>, - input: impl Into>, - min_segments: usize, - always_prove: bool, -) -> Option> -where - VC: VmConfig, - VC::Executor: Chip, - VC::Periphery: Chip, -{ - setup_tracing(); - let engine = BabyBearPoseidon2Engine::new(FriParameters::standard_fast()); - let vm = VirtualMachine::new(engine, config); - let pk = vm.keygen(); - let mut result = vm.execute_and_generate(exe, input).unwrap(); - let connector_pvs = &result.per_segment.last().unwrap().per_air[CONNECTOR_AIR_ID] - .1 - .raw - .public_values[..]; - let pvs: &VmConnectorPvs<_> = connector_pvs.borrow(); - assert_eq!( - pvs.exit_code, - AbstractField::from_canonical_u32(ExitCode::Success as u32), - "Runtime did not exit successfully" - ); - let final_memory = result.final_memory.take(); - if std::env::var("RUN_AIR_TEST_PROVING").is_ok() || always_prove { - let proofs = vm.prove(&pk, result); - - assert!(proofs.len() >= min_segments); - vm.verify(&pk.get_vk(), proofs) - .expect("segment proofs should verify"); - } - final_memory -} - // TODO[jpw]: this should be deleted once tests switch to new API /// Generates the VM STARK circuit, in the form of AIRs and traces, but does not /// do any proving. Output is the payload of everything the prover needs. diff --git a/docs/crates/integration-tests.md b/docs/crates/integration-tests.md new file mode 100644 index 0000000000..ac02a59b3d --- /dev/null +++ b/docs/crates/integration-tests.md @@ -0,0 +1,62 @@ +# How to write integration tests for an extension + +Make a `test` crate inside the extension folder. As an example, here is the structure of the `rv32im-extension-test` crate: + +``` +extensions/rv32im/tests/ +├── Cargo.toml +├── src +│ └── lib.rs +├── programs +│ └── Cargo.toml +│ └── examples +│ └── example1.rs +│ └── example2.rs +│ └── ... +``` + +The `examples` folder contains the test programs in `rust`. + +`fibonacci.rs` example: +```rust +#![cfg_attr(not(feature = "std"), no_main)] +#![cfg_attr(not(feature = "std"), no_std)] + +openvm::entry!(main); + +pub fn main() { + let n = core::hint::black_box(1 << 10); + let mut a: u32 = 0; + let mut b: u32 = 1; + for _ in 1..n { + let sum = a + b; + a = b; + b = sum; + } + if a == 0 { + panic!(); + } +} +``` + + +And then to `transpile`, `run`, and `prove` the above program, in the `src/lib.rs` file, you can do: + +```rust +#[test] +fn test_fibonacci_prove() -> Result<()> { + let elf = build_example_program_at_path(get_programs_dir!(), "fibonacci")?; + let exe = VmExe::from_elf( + elf, + Transpiler::::default() + .with_extension(Rv32ITranspilerExtension) + .with_extension(Rv32MTranspilerExtension) + .with_extension(Rv32IoTranspilerExtension), + )?; + let config = Rv32IConfig::default(); + air_test(config, exe, vec![]); + Ok(()) +} +``` + +Note: If the crate with example is not in `./programs`, specify the relative path with `get_programs_dir!("path to the programs crate")`. diff --git a/docs/repo/layout.md b/docs/repo/layout.md index 32c0f53135..39dd77020e 100644 --- a/docs/repo/layout.md +++ b/docs/repo/layout.md @@ -11,6 +11,7 @@ The main components of the repository are: - [Toolchain](#toolchain) - [Circuit Framework](#circuit-framework) - [Circuit Foundations](#circuit-foundations) + - [Examples](#examples) - [Extensions](#extensions) - [RV32IM](#rv32im) - [Native Recursion](#native-recursion) @@ -49,7 +50,7 @@ Command-line binary to compile, execute, and prove guest programs is in [`cli`]( - [`openvm-instructions`](../../crates/toolchain/instructions): OpenVM instruction struct and trait definitions. Also includes some system instruction definitions. - [`openvm-instructions-derive`](../../crates/toolchain/instructions/derive): Procedural macros to derive traits for OpenVM instructions. - [`openvm-macros-common`](../../crates/toolchain/macros): Common library for parsing utilities shared across procedural macros used for custom instruction setup in guest programs. -- [`openvm-toolchain-tests`](../../crates/toolchain/tests): Testing of Rust toolchain including all official RISC-V 32-bit IM test vectors. Currently this is a monolithic crate with tests across many different extensions. We will soon refactor the tests to be more modular. +- [`openvm-toolchain-tests`](../../crates/toolchain/tests): Includes all official RISC-V 32-bit IM test vectors and transpiler tests. Also, provides utilities for writing integration tests for custom extensions. ### Circuit Framework @@ -63,6 +64,10 @@ Command-line binary to compile, execute, and prove guest programs is in [`cli`]( - [`openvm-poseidon2-air`](../../crates/circuits/poseidon2-air): Standalone poseidon2 AIR implementation which is configurable based on the desired maximum constraint degree. - [`openvm-mod-circuit-builder`](../../crates/circuits/mod-builder): General builder for generating a chip for any modular arithmetic expression for a modulus known at compile time. +### Examples + +- [`examples`](../../examples): Examples of guest programs using the OpenVM framework. All of the examples can be built and run using the CLI. + ### Extensions The toolchain, ISA, and VM are simultaenously extendable. All non-system functionality is implemented via extensions, which may be moved to standalone repositories in the future but are presently in this repository for maintainer convenience. @@ -73,6 +78,7 @@ The toolchain, ISA, and VM are simultaenously extendable. All non-system functio - [`openvm-rv32im-transpiler`](../../extensions/rv32im/transpiler): Transpiler extension for RV32IM instructions and IO instructions. - [`openvm-rv32im-guest`](../../extensions/rv32im/guest): Guest library for RV32IM instructions and IO instructions. This is re-exported by the `openvm` crate for convenience. - [`openvm-rv32-adapters`](../../extensions/rv32-adapters): Circuit adapters for other circuit extensions to use to be compatible with the RISC-V 32-bit architecture. +- [`openvm-rv32im-tests`](../../extensions/rv32im/tests): Integration tests for the RV32IM extension. #### Native Recursion @@ -85,12 +91,14 @@ The toolchain, ISA, and VM are simultaenously extendable. All non-system functio - [`openvm-keccak256-circuit`](../../extensions/keccak256/circuit): Circuit extension for the `keccak256` hash function. - [`openvm-keccak256-transpiler`](../../extensions/keccak256/transpiler): Transpiler extension for the `keccak256` hash function. - [`openvm-keccak256-guest`](../../extensions/keccak256/guest): Guest library with intrinsic function for the `keccak256` hash function. +- [`openvm-keccak256-tests`](../../extensions/keccak256/tests): Integration tests for the keccak256 extension. #### Big Integers - [`openvm-bigint-circuit`](../../extensions/bigint/circuit): Circuit extension for `I256` and `U256` big integer operations. - [`openvm-bigint-transpiler`](../../extensions/bigint/transpiler): Transpiler extension for `I256` and `U256` big integer operations. - [`openvm-bigint-guest`](../../extensions/bigint/guest): Guest library with `I256` and `U256` big integers operations using intrinsics for underlying operations. +- [`openvm-bigint-tests`](../../extensions/bigint/tests): Integration tests for the bigint extension. #### Algebra (Modular Arithmetic) @@ -99,6 +107,7 @@ The toolchain, ISA, and VM are simultaenously extendable. All non-system functio - [`openvm-algebra-guest`](../../extensions/algebra/guest): Guest library with traits for modular arithmetic and complex field extension operations. - [`openvm-algebra-moduli-setup`](../../extensions/algebra/moduli-setup): Procedural macros for use in guest program to generate modular arithmetic struct with custom intrinsics for compile-time modulus. - [`openvm-algebra-complex-macros`](../../extensions/algebra/guest/src/field/complex-macros): Procedural macros for use in guest program to generate complex field struct with custom intrinsics for compile-time modulus. +- [`openvm-algebra-tests`](../../extensions/algebra/tests): Integration tests for the algebra extension. #### Elliptic Curve Cryptography @@ -106,9 +115,11 @@ The toolchain, ISA, and VM are simultaenously extendable. All non-system functio - [`openvm-ecc-transpiler`](../../extensions/ecc/transpiler): Transpiler extension for Weierstrass elliptic curve operations for arbitrary compile-time curve. - [`openvm-ecc-guest`](../../extensions/ecc/guest): Guest library with traits for elliptic curve cryptography. Includes implementations of ECDSA and multi-scalar multiplication. - [`openvm-ecc-sw-setup`](../../extensions/ecc/sw-setup): Procedural macros for use in guest program to generate short Weierstrass curve struct with custom intrinsics for compile-time curve. +- [`openvm-ecc-tests`](../../extensions/ecc/tests): Integration tests for the elliptic curve cryptography extension. #### Elliptic Curve Pairing - [`openvm-pairing-circuit`](../../extensions/pairing/circuit): Circuit extension for optimal Ate pairing on BN254 and BLS12-381 curves. - [`openvm-pairing-transpiler`](../../extensions/pairing/transpiler): Transpiler extension for optimal Ate pairing on BN254 and BLS12-381. - [`openvm-pairing-guest`](../../extensions/pairing/guest): Guest library with optimal Ate pairing on BN254 and BLS12-381 and associated constants. Also includes elliptic curve operations for VM runtime with the `halo2curves` feature gate. +- [`openvm-pairing-tests`](../../extensions/pairing/tests): Integration tests for the pairing extension. \ No newline at end of file diff --git a/examples/algebra/Cargo.toml b/examples/algebra/Cargo.toml new file mode 100644 index 0000000000..c9dec9ab49 --- /dev/null +++ b/examples/algebra/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "algebra-example" +version = "0.0.0" +edition = "2021" + +[workspace] +members = [] + +[dependencies] +openvm = { git = "https://github.com/openvm-org/openvm.git" } +openvm-platform = { git = "https://github.com/openvm-org/openvm.git" } +openvm-algebra-guest = { git = "https://github.com/openvm-org/openvm.git" } +openvm-algebra-complex-macros = { git = "https://github.com/openvm-org/openvm.git" } +serde = { version = "1.0.216", default-features = false } + +[features] +default = [] +std = [ + "serde/std", + "openvm/std", + "openvm-algebra-guest/std", +] diff --git a/examples/algebra/openvm.toml b/examples/algebra/openvm.toml new file mode 100644 index 0000000000..fcdce806e4 --- /dev/null +++ b/examples/algebra/openvm.toml @@ -0,0 +1,8 @@ +[app_vm_config.rv32i] +[app_vm_config.rv32m] +[app_vm_config.io] +[app_vm_config.modular] +supported_modulus = ["998244353","1000000007"] + +[app_vm_config.fp2] +supported_modulus = ["998244353","1000000007"] \ No newline at end of file diff --git a/examples/algebra/src/main.rs b/examples/algebra/src/main.rs new file mode 100644 index 0000000000..6aa6e90355 --- /dev/null +++ b/examples/algebra/src/main.rs @@ -0,0 +1,45 @@ +#![cfg_attr(not(feature = "std"), no_main)] +#![cfg_attr(not(feature = "std"), no_std)] + +use openvm_algebra_guest::{IntMod, moduli_setup::*}; + +openvm::entry!(main); + +// This macro will create two structs, `Mod1` and `Mod2`, +// one for arithmetic modulo 998244353, and the other for arithmetic modulo 1000000007. +moduli_declare! { + Mod1 { modulus = "998244353" }, + Mod2 { modulus = "1000000007" } +} + +// This macro will initialize the moduli. +// Now, `Mod1` is the "zeroth" modular struct, and `Mod2` is the "first" one. +moduli_init! { + "998244353", "1000000007" +} + +// This macro will create two structs, `Complex1` and `Complex2`, +// one for arithmetic in the field $\mathbb{F}_{998244353}[x]/(x^2 + 1)$, +// and the other for arithmetic in the field $\mathbb{F}_{1000000007}[x]/(x^2 + 1)$. +openvm_algebra_complex_macros::complex_declare! { + Complex1 { mod_type = Mod1 }, + Complex2 { mod_type = Mod2 }, +} + +// The order of these structs does not matter, +// given that we specify the `mod_idx` parameters properly. +openvm_algebra_complex_macros::complex_init! { + Complex2 { mod_idx = 1 }, Complex1 { mod_idx = 0 }, +} + +pub fn main() { + // Since we only use an arithmetic operation with `Mod1` and not `Mod2`, + // we only need to call `setup_0()` here. + setup_0(); + setup_all_complex_extensions(); + let a = Complex1::new(Mod1::ZERO, Mod1::from_u32(0x3b8) * Mod1::from_u32(0x100000)); // a = -i in the corresponding field + let b = Complex2::new(Mod2::ZERO, Mod2::from_u32(1000000006)); // b = -i in the corresponding field + assert_eq!(a.clone() * &a * &a * &a * &a, a); // a^5 = a + assert_eq!(b.clone() * &b * &b * &b * &b, b); // b^5 = b + // Note that these assertions would fail, have we provided the `mod_idx` parameters wrongly. +} \ No newline at end of file diff --git a/examples/ecc/Cargo.toml b/examples/ecc/Cargo.toml new file mode 100644 index 0000000000..79c5390720 --- /dev/null +++ b/examples/ecc/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "ecc-example" +version = "0.0.0" +edition = "2021" + +[workspace] +members = [] + +[dependencies] +openvm = { git = "https://github.com/openvm-org/openvm.git" } +openvm-platform = { git = "https://github.com/openvm-org/openvm.git" } +openvm-algebra-guest = { git = "https://github.com/openvm-org/openvm.git" } +openvm-ecc-guest = { git = "https://github.com/openvm-org/openvm.git", features = ["k256"] } +hex-literal = { version = "0.4.1", default-features = false } + +[features] +default = [] +std = [ + "openvm/std", + "openvm-algebra-guest/std", + "openvm-ecc-guest/std", +] + diff --git a/examples/ecc/openvm.toml b/examples/ecc/openvm.toml new file mode 100644 index 0000000000..bd968d85a2 --- /dev/null +++ b/examples/ecc/openvm.toml @@ -0,0 +1,11 @@ +[app_vm_config.rv32i] +[app_vm_config.rv32m] +[app_vm_config.io] +[app_vm_config.modular] +supported_modulus = ["115792089237316195423570985008687907853269984665640564039457584007908834671663", "115792089237316195423570985008687907852837564279074904382605163141518161494337"] + +[[app_vm_config.ecc.supported_curves]] +modulus = "115792089237316195423570985008687907853269984665640564039457584007908834671663" +scalar = "115792089237316195423570985008687907852837564279074904382605163141518161494337" +a = "0" +b = "7" \ No newline at end of file diff --git a/examples/ecc/src/main.rs b/examples/ecc/src/main.rs new file mode 100644 index 0000000000..5ca871b4cf --- /dev/null +++ b/examples/ecc/src/main.rs @@ -0,0 +1,39 @@ +#![cfg_attr(not(feature = "std"), no_main)] +#![cfg_attr(not(feature = "std"), no_std)] + +use openvm_algebra_guest::IntMod; + +openvm::entry!(main); + +use hex_literal::hex; +use openvm_ecc_guest::{ + k256::{Secp256k1Coord, Secp256k1Point}, + weierstrass::WeierstrassPoint, +}; + +openvm_algebra_guest::moduli_setup::moduli_init! { + "0xFFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE FFFFFC2F", + "0xFFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE BAAEDCE6 AF48A03B BFD25E8C D0364141" +} + +openvm_ecc_guest::sw_setup::sw_init! { + Secp256k1Coord, +} + +pub fn main() { + setup_all_moduli(); + setup_all_curves(); + let x1 = Secp256k1Coord::from_u32(1); + let y1 = Secp256k1Coord::from_le_bytes(&hex!( + "EEA7767E580D75BC6FDD7F58D2A84C2614FB22586068DB63B346C6E60AF21842" + )); + let p1 = Secp256k1Point::from_xy_nonidentity(x1, y1).unwrap(); + + let x2 = Secp256k1Coord::from_u32(2); + let y2 = Secp256k1Coord::from_le_bytes(&hex!( + "D1A847A8F879E0AEE32544DA5BA0B3BD1703A1F52867A5601FF6454DD8180499" + )); + let p2 = Secp256k1Point::from_xy_nonidentity(x2, y2).unwrap(); + + let _p3 = &p1 + &p2; +} diff --git a/examples/i256/Cargo.toml b/examples/i256/Cargo.toml new file mode 100644 index 0000000000..f26c2b69ce --- /dev/null +++ b/examples/i256/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "i256-example" +version = "0.0.0" +edition = "2021" + +[workspace] +members = [] + +[dependencies] +openvm = { git = "https://github.com/openvm-org/openvm.git" } +openvm-platform = { git = "https://github.com/openvm-org/openvm.git" } +openvm-bigint-guest = { git = "https://github.com/openvm-org/openvm.git" } + +[features] +default = [] +std = [ + "openvm/std", + "openvm-bigint-guest/std", +] diff --git a/examples/i256/openvm.toml b/examples/i256/openvm.toml new file mode 100644 index 0000000000..91288aade5 --- /dev/null +++ b/examples/i256/openvm.toml @@ -0,0 +1,4 @@ +[app_vm_config.rv32i] +[app_vm_config.rv32m] +[app_vm_config.io] +[app_vm_config.bigint] \ No newline at end of file diff --git a/examples/i256/src/main.rs b/examples/i256/src/main.rs new file mode 100644 index 0000000000..1331abbcc6 --- /dev/null +++ b/examples/i256/src/main.rs @@ -0,0 +1,40 @@ +#![cfg_attr(not(feature = "std"), no_main)] +#![cfg_attr(not(feature = "std"), no_std)] + +openvm::entry!(main); +use core::array; +use openvm_bigint_guest::I256; + +const N: usize = 16; +type Matrix = [[I256; N]; N]; + +pub fn get_matrix(val: i32) -> Matrix { + array::from_fn(|_| array::from_fn(|_| I256::from_i32(val))) +} + +pub fn mult(a: &Matrix, b: &Matrix) -> Matrix { + let mut c = get_matrix(0); + for i in 0..N { + for j in 0..N { + for k in 0..N { + c[i][j] += &a[i][k] * &b[k][j]; + } + } + } + c +} + +pub fn get_identity_matrix() -> Matrix { + let mut res = get_matrix(0); + for i in 0..N { + res[i][i] = I256::from_i32(1); + } + res +} + +pub fn main() { + let a: Matrix = get_identity_matrix(); + let b: Matrix = get_matrix(-28); + let c: Matrix = mult(&a, &b); + assert_eq!(c, b); +} \ No newline at end of file diff --git a/examples/keccak/Cargo.toml b/examples/keccak/Cargo.toml new file mode 100644 index 0000000000..f976a40750 --- /dev/null +++ b/examples/keccak/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "keccak-example" +version = "0.0.0" +edition = "2021" + +[workspace] +members = [] + +[dependencies] +openvm = { git = "https://github.com/openvm-org/openvm.git" } +openvm-platform = { git = "https://github.com/openvm-org/openvm.git" } +openvm-keccak256-guest = { git = "https://github.com/openvm-org/openvm.git" } +hex = { version = "0.4.3", default-features = false, features = ["alloc"] } + +[features] +default = [] +std = [ + "openvm/std", + "openvm-keccak256-guest/std", +] diff --git a/examples/keccak/openvm.toml b/examples/keccak/openvm.toml new file mode 100644 index 0000000000..90e80fa97f --- /dev/null +++ b/examples/keccak/openvm.toml @@ -0,0 +1,4 @@ +[app_vm_config.rv32i] +[app_vm_config.rv32m] +[app_vm_config.io] +[app_vm_config.keccak] \ No newline at end of file diff --git a/examples/keccak/src/main.rs b/examples/keccak/src/main.rs new file mode 100644 index 0000000000..1db5e697b6 --- /dev/null +++ b/examples/keccak/src/main.rs @@ -0,0 +1,27 @@ +#![cfg_attr(not(feature = "std"), no_main)] +#![cfg_attr(not(feature = "std"), no_std)] + +extern crate alloc; + +use alloc::vec::Vec; +use core::hint::black_box; + +use openvm_keccak256_guest::keccak256; +use hex::FromHex; + +openvm::entry!(main); + +pub fn main() { + let test_vectors = [ + ("", "C5D2460186F7233C927E7DB2DCC703C0E500B653CA82273B7BFAD8045D85A470"), + ("CC", "EEAD6DBFC7340A56CAEDC044696A168870549A6A7F6F56961E84A54BD9970B8A"), + ]; + for (input, expected_output) in test_vectors.iter() { + let input = Vec::from_hex(input).unwrap(); + let expected_output = Vec::from_hex(expected_output).unwrap(); + let output = keccak256(&black_box(input)); + if output != *expected_output { + panic!(); + } + } +} \ No newline at end of file diff --git a/examples/pairing/Cargo.toml b/examples/pairing/Cargo.toml new file mode 100644 index 0000000000..3f22ff107b --- /dev/null +++ b/examples/pairing/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "pairing-example" +version = "0.0.0" +edition = "2021" + +[workspace] +members = [] + +[dependencies] +openvm = { git = "https://github.com/openvm-org/openvm.git" } +openvm-platform = { git = "https://github.com/openvm-org/openvm.git" } +openvm-algebra-guest = { git = "https://github.com/openvm-org/openvm.git" } +openvm-algebra-moduli-setup = { git = "https://github.com/openvm-org/openvm.git" } +openvm-algebra-complex-macros = { git = "https://github.com/openvm-org/openvm.git" } +openvm-ecc-guest = { git = "https://github.com/openvm-org/openvm.git" } +openvm-pairing-guest = { git = "https://github.com/openvm-org/openvm.git", features = ["bls12_381"] } +hex-literal = { version = "0.4.1", default-features = false } + +[features] +default = [] +std = [ + "openvm/std", + "openvm-algebra-guest/std", + "openvm-ecc-guest/std", + "openvm-pairing-guest/std", +] + diff --git a/examples/pairing/openvm.toml b/examples/pairing/openvm.toml new file mode 100644 index 0000000000..94337448fe --- /dev/null +++ b/examples/pairing/openvm.toml @@ -0,0 +1,15 @@ +[app_vm_config.rv32i] +[app_vm_config.rv32m] +[app_vm_config.io] +[app_vm_config.pairing] +supported_curves = ["Bls12_381"] + +[app_vm_config.modular] +supported_modulus = [ + "4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787", +] + +[app_vm_config.fp2] +supported_modulus = [ + "4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787", +] \ No newline at end of file diff --git a/examples/pairing/src/main.rs b/examples/pairing/src/main.rs new file mode 100644 index 0000000000..1adf39c20c --- /dev/null +++ b/examples/pairing/src/main.rs @@ -0,0 +1,58 @@ +#![cfg_attr(not(feature = "std"), no_main)] +#![cfg_attr(not(feature = "std"), no_std)] + +use hex_literal::hex; +use openvm_algebra_guest::{field::FieldExtension, IntMod}; +use openvm_ecc_guest::AffinePoint; +use openvm_pairing_guest::{ + bls12_381::{Bls12_381, Fp, Fp2}, + pairing::PairingCheck, +}; + +openvm::entry!(main); + +openvm_algebra_moduli_setup::moduli_init! { + "0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab", + "0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001" +} + +openvm_algebra_complex_macros::complex_init! { + Bls12_381Fp2 { mod_idx = 0 }, +} + +pub fn main() { + setup_0(); + setup_all_complex_extensions(); + + let p0 = AffinePoint::new( + Fp::from_be_bytes(&hex!("17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb")), + Fp::from_be_bytes(&hex!("08b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1")) + ); + let p1 = AffinePoint::new( + Fp2::from_coeffs([ + Fp::from_be_bytes(&hex!("1638533957d540a9d2370f17cc7ed5863bc0b995b8825e0ee1ea1e1e4d00dbae81f14b0bf3611b78c952aacab827a053")), + Fp::from_be_bytes(&hex!("0a4edef9c1ed7f729f520e47730a124fd70662a904ba1074728114d1031e1572c6c886f6b57ec72a6178288c47c33577")) + ]), + Fp2::from_coeffs([ + Fp::from_be_bytes(&hex!("0468fb440d82b0630aeb8dca2b5256789a66da69bf91009cbfe6bd221e47aa8ae88dece9764bf3bd999d95d71e4c9899")), + Fp::from_be_bytes(&hex!("0f6d4552fa65dd2638b361543f887136a43253d9c66c411697003f7a13c308f5422e1aa0a59c8967acdefd8b6e36ccf3")) + ]), + ); + let q0 = AffinePoint::new( + Fp::from_be_bytes(&hex!("0572cbea904d67468808c8eb50a9450c9721db309128012543902d0ac358a62ae28f75bb8f1c7c42c39a8c5529bf0f4e")), + Fp::from_be_bytes(&hex!("166a9d8cabc673a322fda673779d8e3822ba3ecb8670e461f73bb9021d5fd76a4c56d9d4cd16bd1bba86881979749d28")) + ); + let q1 = AffinePoint::new( + Fp2::from_coeffs([ + Fp::from_be_bytes(&hex!("024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8")), + Fp::from_be_bytes(&hex!("13e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e")) + ]), + Fp2::from_coeffs([ + Fp::from_be_bytes(&hex!("0ce5d527727d6e118cc9cdc6da2e351aadfd9baa8cbdd3a76d429a695160d12c923ac9cc3baca289e193548608b82801")), + Fp::from_be_bytes(&hex!("0606c4a02ea734cc32acd2b02bc28b99cb3e287e85a763af267492ab572e99ab3f370d275cec1da1aaa9075ff05f79be")) + ]), + ); + + let res = Bls12_381::pairing_check(&[p0, -q0], &[p1, q1]); + assert!(res.is_ok()); +} diff --git a/examples/u256/Cargo.toml b/examples/u256/Cargo.toml new file mode 100644 index 0000000000..eafbf4b7ea --- /dev/null +++ b/examples/u256/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "u256-example" +version = "0.0.0" +edition = "2021" + +[workspace] +members = [] + +[dependencies] +openvm = { git = "https://github.com/openvm-org/openvm.git" } +openvm-platform = { git = "https://github.com/openvm-org/openvm.git" } +openvm-bigint-guest = { git = "https://github.com/openvm-org/openvm.git" } + +[features] +default = [] +std = [ + "openvm/std", + "openvm-bigint-guest/std", +] diff --git a/examples/u256/openvm.toml b/examples/u256/openvm.toml new file mode 100644 index 0000000000..91288aade5 --- /dev/null +++ b/examples/u256/openvm.toml @@ -0,0 +1,4 @@ +[app_vm_config.rv32i] +[app_vm_config.rv32m] +[app_vm_config.io] +[app_vm_config.bigint] \ No newline at end of file diff --git a/examples/u256/src/main.rs b/examples/u256/src/main.rs new file mode 100644 index 0000000000..1331abbcc6 --- /dev/null +++ b/examples/u256/src/main.rs @@ -0,0 +1,40 @@ +#![cfg_attr(not(feature = "std"), no_main)] +#![cfg_attr(not(feature = "std"), no_std)] + +openvm::entry!(main); +use core::array; +use openvm_bigint_guest::I256; + +const N: usize = 16; +type Matrix = [[I256; N]; N]; + +pub fn get_matrix(val: i32) -> Matrix { + array::from_fn(|_| array::from_fn(|_| I256::from_i32(val))) +} + +pub fn mult(a: &Matrix, b: &Matrix) -> Matrix { + let mut c = get_matrix(0); + for i in 0..N { + for j in 0..N { + for k in 0..N { + c[i][j] += &a[i][k] * &b[k][j]; + } + } + } + c +} + +pub fn get_identity_matrix() -> Matrix { + let mut res = get_matrix(0); + for i in 0..N { + res[i][i] = I256::from_i32(1); + } + res +} + +pub fn main() { + let a: Matrix = get_identity_matrix(); + let b: Matrix = get_matrix(-28); + let c: Matrix = mult(&a, &b); + assert_eq!(c, b); +} \ No newline at end of file diff --git a/extensions/algebra/tests/Cargo.toml b/extensions/algebra/tests/Cargo.toml new file mode 100644 index 0000000000..e3d3bdd1a9 --- /dev/null +++ b/extensions/algebra/tests/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "openvm-algebra-tests" +description = "Integration tests for the OpenVM algebra extension" +version.workspace = true +authors.workspace = true +edition.workspace = true +homepage.workspace = true +repository.workspace = true + +[dependencies] +openvm-circuit-primitives-derive.workspace = true +openvm-instructions = { workspace = true } +openvm-stark-sdk.workspace = true +openvm-circuit = { workspace = true, features = ["test-utils"] } +openvm-transpiler.workspace = true +openvm-build.workspace = true +openvm-algebra-transpiler.workspace = true +openvm-algebra-circuit.workspace = true +openvm-rv32im-transpiler.workspace = true +openvm-platform = { workspace = true } +openvm = { workspace = true } +openvm-toolchain-tests = { path = "../../../crates/toolchain/tests" } +openvm-ecc-circuit.workspace = true +eyre.workspace = true +num-bigint-dig.workspace = true + + +[features] +default = ["parallel"] +parallel = ["openvm-circuit/parallel"] diff --git a/extensions/algebra/tests/programs/Cargo.toml b/extensions/algebra/tests/programs/Cargo.toml new file mode 100644 index 0000000000..a8a92e4ea1 --- /dev/null +++ b/extensions/algebra/tests/programs/Cargo.toml @@ -0,0 +1,32 @@ +[workspace] +[package] +name = "openvm-algebra-test-programs" +version = "0.0.0" +edition = "2021" + +[dependencies] +openvm = { path = "../../../../crates/toolchain/openvm" } +openvm-platform = { path = "../../../../crates/toolchain/platform" } + +openvm-algebra-guest = { path = "../../guest" } +openvm-algebra-moduli-setup = { path = "../../moduli-setup", default-features = false } +openvm-algebra-complex-macros = { path = "../../guest/src/field/complex-macros", default-features = false } +num-bigint-dig = { version = "0.8", default-features = false } +serde = { version = "1.0", default-features = false, features = [ + "alloc", + "derive", +] } + + +[features] +default = [] +std = [ + "serde/std", + "openvm/std", + "openvm-algebra-guest/std", +] + +[profile.release] +panic = "abort" +lto = "thin" # turn on lto = fat to decrease binary size, but this optimizes out some missing extern links so we shouldn't use it for testing +# strip = "symbols" diff --git a/crates/toolchain/tests/programs/examples/complex.rs b/extensions/algebra/tests/programs/examples/complex-secp256k1.rs similarity index 100% rename from crates/toolchain/tests/programs/examples/complex.rs rename to extensions/algebra/tests/programs/examples/complex-secp256k1.rs diff --git a/crates/toolchain/tests/programs/examples/complex-two-modulos.rs b/extensions/algebra/tests/programs/examples/complex-two-modulos.rs similarity index 86% rename from crates/toolchain/tests/programs/examples/complex-two-modulos.rs rename to extensions/algebra/tests/programs/examples/complex-two-modulos.rs index d9aaa76bdb..0b9f20d03b 100644 --- a/crates/toolchain/tests/programs/examples/complex-two-modulos.rs +++ b/extensions/algebra/tests/programs/examples/complex-two-modulos.rs @@ -1,8 +1,7 @@ #![cfg_attr(not(feature = "std"), no_main)] #![cfg_attr(not(feature = "std"), no_std)] -// use openvm_algebra_guest::{field::ComplexConjugate, DivAssignUnsafe, DivUnsafe, IntMod}; -use openvm_algebra_guest::IntMod; +use openvm_algebra_guest::{DivUnsafe, IntMod}; openvm::entry!(main); diff --git a/crates/toolchain/tests/programs/examples/little.rs b/extensions/algebra/tests/programs/examples/little.rs similarity index 92% rename from crates/toolchain/tests/programs/examples/little.rs rename to extensions/algebra/tests/programs/examples/little.rs index d116fd3e41..117080ff68 100644 --- a/crates/toolchain/tests/programs/examples/little.rs +++ b/extensions/algebra/tests/programs/examples/little.rs @@ -22,10 +22,10 @@ pub fn main() { let mut res = Secp256k1Coord::from_u32(1); let inv = res.clone().div_unsafe(&a); - for i in 0..32 { + for pow_bit in pow { for j in 0..8 { - if pow[i] & (1 << j) != 0 { - res = res * &a; + if pow_bit & (1 << j) != 0 { + res *= &a; } a *= a.clone(); } diff --git a/crates/toolchain/tests/programs/examples/moduli_setup.rs b/extensions/algebra/tests/programs/examples/moduli_setup.rs similarity index 100% rename from crates/toolchain/tests/programs/examples/moduli_setup.rs rename to extensions/algebra/tests/programs/examples/moduli_setup.rs diff --git a/extensions/algebra/tests/src/lib.rs b/extensions/algebra/tests/src/lib.rs new file mode 100644 index 0000000000..efc1253df1 --- /dev/null +++ b/extensions/algebra/tests/src/lib.rs @@ -0,0 +1,92 @@ +#[cfg(test)] +mod tests { + use std::str::FromStr; + + use eyre::Result; + use num_bigint_dig::BigUint; + use openvm_algebra_circuit::{Rv32ModularConfig, Rv32ModularWithFp2Config}; + use openvm_algebra_transpiler::{Fp2TranspilerExtension, ModularTranspilerExtension}; + use openvm_circuit::utils::air_test; + use openvm_ecc_circuit::SECP256K1_CONFIG; + use openvm_instructions::exe::VmExe; + use openvm_rv32im_transpiler::{ + Rv32ITranspilerExtension, Rv32IoTranspilerExtension, Rv32MTranspilerExtension, + }; + use openvm_stark_sdk::p3_baby_bear::BabyBear; + use openvm_toolchain_tests::{build_example_program_at_path, get_programs_dir}; + use openvm_transpiler::{transpiler::Transpiler, FromElf}; + + type F = BabyBear; + + #[test] + fn test_moduli_setup() -> Result<()> { + let elf = build_example_program_at_path(get_programs_dir!(), "moduli_setup")?; + let openvm_exe = VmExe::from_elf( + elf, + Transpiler::::default() + .with_extension(Rv32ITranspilerExtension) + .with_extension(Rv32MTranspilerExtension) + .with_extension(Rv32IoTranspilerExtension) + .with_extension(ModularTranspilerExtension), + )?; + + let moduli = ["4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787", "1000000000000000003", "2305843009213693951"] + .map(|s| BigUint::from_str(s).unwrap()); + let config = Rv32ModularConfig::new(moduli.to_vec()); + air_test(config, openvm_exe); + Ok(()) + } + + #[test] + fn test_modular() -> Result<()> { + let elf = build_example_program_at_path(get_programs_dir!(), "little")?; + let openvm_exe = VmExe::from_elf( + elf, + Transpiler::::default() + .with_extension(Rv32ITranspilerExtension) + .with_extension(Rv32MTranspilerExtension) + .with_extension(Rv32IoTranspilerExtension) + .with_extension(ModularTranspilerExtension), + )?; + let config = Rv32ModularConfig::new(vec![SECP256K1_CONFIG.modulus.clone()]); + air_test(config, openvm_exe); + Ok(()) + } + + #[test] + fn test_complex_two_moduli() -> Result<()> { + let elf = build_example_program_at_path(get_programs_dir!(), "complex-two-modulos")?; + let openvm_exe = VmExe::from_elf( + elf, + Transpiler::::default() + .with_extension(Rv32ITranspilerExtension) + .with_extension(Rv32MTranspilerExtension) + .with_extension(Rv32IoTranspilerExtension) + .with_extension(Fp2TranspilerExtension) + .with_extension(ModularTranspilerExtension), + )?; + let config = Rv32ModularWithFp2Config::new(vec![ + BigUint::from_str("998244353").unwrap(), + BigUint::from_str("1000000007").unwrap(), + ]); + air_test(config, openvm_exe); + Ok(()) + } + + #[test] + fn test_complex() -> Result<()> { + let elf = build_example_program_at_path(get_programs_dir!(), "complex-secp256k1")?; + let openvm_exe = VmExe::from_elf( + elf, + Transpiler::::default() + .with_extension(Rv32ITranspilerExtension) + .with_extension(Rv32MTranspilerExtension) + .with_extension(Rv32IoTranspilerExtension) + .with_extension(Fp2TranspilerExtension) + .with_extension(ModularTranspilerExtension), + )?; + let config = Rv32ModularWithFp2Config::new(vec![SECP256K1_CONFIG.modulus.clone()]); + air_test(config, openvm_exe); + Ok(()) + } +} diff --git a/extensions/bigint/tests/Cargo.toml b/extensions/bigint/tests/Cargo.toml new file mode 100644 index 0000000000..b40c508c07 --- /dev/null +++ b/extensions/bigint/tests/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "openvm-bigint-integration-tests" +description = "Integration tests for the OpenVM bigint extension" +version.workspace = true +authors.workspace = true +edition.workspace = true +homepage.workspace = true +repository.workspace = true + +[dependencies] +openvm-circuit-primitives-derive.workspace = true +openvm-instructions = { workspace = true } +openvm-stark-sdk.workspace = true +openvm-circuit = { workspace = true, features = ["test-utils"] } +openvm-transpiler.workspace = true +openvm-build.workspace = true +openvm-bigint-transpiler.workspace = true +openvm-bigint-circuit.workspace = true +openvm-rv32im-transpiler.workspace = true +openvm-platform = { workspace = true } +openvm = { workspace = true } +openvm-toolchain-tests = { path = "../../../crates/toolchain/tests" } +eyre.workspace = true + +[target.'cfg(not(target_os = "zkvm"))'.dependencies] +num-bigint-dig.workspace = true + +[features] +default = ["parallel"] +parallel = ["openvm-circuit/parallel"] diff --git a/extensions/bigint/tests/programs/Cargo.toml b/extensions/bigint/tests/programs/Cargo.toml new file mode 100644 index 0000000000..3725557c4d --- /dev/null +++ b/extensions/bigint/tests/programs/Cargo.toml @@ -0,0 +1,29 @@ +[workspace] +[package] +name = "openvm-bigint-test-programs" +version = "0.0.0" +edition = "2021" + +[dependencies] +openvm = { path = "../../../../crates/toolchain/openvm" } +openvm-platform = { path = "../../../../crates/toolchain/platform" } + +openvm-bigint-guest = { path = "../../guest" } +serde = { version = "1.0", default-features = false, features = [ + "alloc", + "derive", +] } + + +[features] +default = [] +std = [ + "serde/std", + "openvm/std", + "openvm-bigint-guest/std", +] + +[profile.release] +panic = "abort" +lto = "thin" # turn on lto = fat to decrease binary size, but this optimizes out some missing extern links so we shouldn't use it for testing +# strip = "symbols" diff --git a/extensions/bigint/tests/programs/examples/book-example2.rs b/extensions/bigint/tests/programs/examples/book-example2.rs new file mode 100644 index 0000000000..1331abbcc6 --- /dev/null +++ b/extensions/bigint/tests/programs/examples/book-example2.rs @@ -0,0 +1,40 @@ +#![cfg_attr(not(feature = "std"), no_main)] +#![cfg_attr(not(feature = "std"), no_std)] + +openvm::entry!(main); +use core::array; +use openvm_bigint_guest::I256; + +const N: usize = 16; +type Matrix = [[I256; N]; N]; + +pub fn get_matrix(val: i32) -> Matrix { + array::from_fn(|_| array::from_fn(|_| I256::from_i32(val))) +} + +pub fn mult(a: &Matrix, b: &Matrix) -> Matrix { + let mut c = get_matrix(0); + for i in 0..N { + for j in 0..N { + for k in 0..N { + c[i][j] += &a[i][k] * &b[k][j]; + } + } + } + c +} + +pub fn get_identity_matrix() -> Matrix { + let mut res = get_matrix(0); + for i in 0..N { + res[i][i] = I256::from_i32(1); + } + res +} + +pub fn main() { + let a: Matrix = get_identity_matrix(); + let b: Matrix = get_matrix(-28); + let c: Matrix = mult(&a, &b); + assert_eq!(c, b); +} \ No newline at end of file diff --git a/crates/toolchain/tests/programs/examples/matrix-power-signed.rs b/extensions/bigint/tests/programs/examples/matrix-power-signed.rs similarity index 100% rename from crates/toolchain/tests/programs/examples/matrix-power-signed.rs rename to extensions/bigint/tests/programs/examples/matrix-power-signed.rs diff --git a/crates/toolchain/tests/programs/examples/matrix-power.rs b/extensions/bigint/tests/programs/examples/matrix-power-unsigned.rs similarity index 100% rename from crates/toolchain/tests/programs/examples/matrix-power.rs rename to extensions/bigint/tests/programs/examples/matrix-power-unsigned.rs diff --git a/extensions/bigint/tests/src/lib.rs b/extensions/bigint/tests/src/lib.rs new file mode 100644 index 0000000000..866be2f672 --- /dev/null +++ b/extensions/bigint/tests/src/lib.rs @@ -0,0 +1,48 @@ +#[cfg(test)] +mod tests { + use eyre::Result; + use openvm_bigint_circuit::Int256Rv32Config; + use openvm_bigint_transpiler::Int256TranspilerExtension; + use openvm_circuit::utils::air_test; + use openvm_instructions::exe::VmExe; + use openvm_rv32im_transpiler::{ + Rv32ITranspilerExtension, Rv32IoTranspilerExtension, Rv32MTranspilerExtension, + }; + use openvm_stark_sdk::p3_baby_bear::BabyBear; + use openvm_toolchain_tests::{build_example_program_at_path, get_programs_dir}; + use openvm_transpiler::{transpiler::Transpiler, FromElf}; + + type F = BabyBear; + + #[test] + fn test_matrix_power() -> Result<()> { + let elf = build_example_program_at_path(get_programs_dir!(), "matrix-power-unsigned")?; + let openvm_exe = VmExe::from_elf( + elf, + Transpiler::::default() + .with_extension(Rv32ITranspilerExtension) + .with_extension(Rv32MTranspilerExtension) + .with_extension(Rv32IoTranspilerExtension) + .with_extension(Int256TranspilerExtension), + )?; + let config = Int256Rv32Config::default(); + air_test(config, openvm_exe); + Ok(()) + } + + #[test] + fn test_matrix_power_signed() -> Result<()> { + let elf = build_example_program_at_path(get_programs_dir!(), "matrix-power-signed")?; + let openvm_exe = VmExe::from_elf( + elf, + Transpiler::::default() + .with_extension(Rv32ITranspilerExtension) + .with_extension(Rv32MTranspilerExtension) + .with_extension(Rv32IoTranspilerExtension) + .with_extension(Int256TranspilerExtension), + )?; + let config = Int256Rv32Config::default(); + air_test(config, openvm_exe); + Ok(()) + } +} diff --git a/extensions/ecc/tests/Cargo.toml b/extensions/ecc/tests/Cargo.toml new file mode 100644 index 0000000000..ca7c896228 --- /dev/null +++ b/extensions/ecc/tests/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "openvm-ecc-integration-tests" +description = "Integration tests for the OpenVM ecc extension" +version.workspace = true +authors.workspace = true +edition.workspace = true +homepage.workspace = true +repository.workspace = true + +[dependencies] +openvm-circuit-primitives-derive.workspace = true +openvm-instructions = { workspace = true } +openvm-stark-sdk.workspace = true +openvm-circuit = { workspace = true, features = ["test-utils"] } +openvm-transpiler.workspace = true +openvm-algebra-circuit.workspace = true +openvm-algebra-transpiler.workspace = true +openvm-ecc-transpiler.workspace = true +openvm-ecc-circuit.workspace = true +openvm-ecc-guest.workspace = true +openvm-rv32im-transpiler.workspace = true +openvm-keccak256-transpiler.workspace = true +openvm-toolchain-tests = { path = "../../../crates/toolchain/tests" } +openvm-sdk.workspace = true +eyre.workspace = true + +[features] +default = ["parallel"] +parallel = ["openvm-circuit/parallel"] diff --git a/extensions/ecc/tests/programs/Cargo.toml b/extensions/ecc/tests/programs/Cargo.toml new file mode 100644 index 0000000000..ef91d26479 --- /dev/null +++ b/extensions/ecc/tests/programs/Cargo.toml @@ -0,0 +1,45 @@ +[workspace] +[package] +name = "openvm-ecc-test-programs" +version = "0.0.0" +edition = "2021" + +[dependencies] +openvm = { path = "../../../../crates/toolchain/openvm" } +openvm-platform = { path = "../../../../crates/toolchain/platform" } + +openvm-ecc-guest = { path = "../../guest", default-features = false } +openvm-ecc-sw-setup = { path = "../../../../extensions/ecc/sw-setup", default-features = false } +openvm-algebra-guest = { path = "../../../algebra/guest", default-features = false } +openvm-algebra-moduli-setup = { path = "../../../algebra/moduli-setup", default-features = false } +openvm-keccak256-guest = { path = "../../../keccak256/guest", default-features = false } + +serde = { version = "1.0", default-features = false, features = [ + "alloc", + "derive", +] } +hex = { version = "0.4.3", default-features = false, features = ["alloc"] } +hex-literal = { version = "0.4.1", default-features = false } +k256 = { version = "0.13.3", default-features = false, features = [ + "ecdsa-core", + "ecdsa", +], optional = true } + +[features] +default = [] +std = [ + "serde/std", + "openvm/std", + "openvm-ecc-guest/std", +] + +k256 = ["openvm-ecc-guest/k256", "dep:k256"] + +[profile.release] +panic = "abort" +lto = "thin" # turn on lto = fat to decrease binary size, but this optimizes out some missing extern links so we shouldn't use it for testing +# strip = "symbols" + +[[example]] +name = "ec" +required-features = ["k256"] diff --git a/crates/toolchain/tests/programs/examples/decompress.rs b/extensions/ecc/tests/programs/examples/decompress.rs similarity index 100% rename from crates/toolchain/tests/programs/examples/decompress.rs rename to extensions/ecc/tests/programs/examples/decompress.rs diff --git a/crates/toolchain/tests/programs/examples/ec.rs b/extensions/ecc/tests/programs/examples/ec.rs similarity index 100% rename from crates/toolchain/tests/programs/examples/ec.rs rename to extensions/ecc/tests/programs/examples/ec.rs diff --git a/crates/toolchain/tests/programs/examples/ecdsa.rs b/extensions/ecc/tests/programs/examples/ecdsa.rs similarity index 100% rename from crates/toolchain/tests/programs/examples/ecdsa.rs rename to extensions/ecc/tests/programs/examples/ecdsa.rs diff --git a/extensions/ecc/tests/src/lib.rs b/extensions/ecc/tests/src/lib.rs new file mode 100644 index 0000000000..3afd8e155b --- /dev/null +++ b/extensions/ecc/tests/src/lib.rs @@ -0,0 +1,101 @@ +#[cfg(test)] +mod tests { + use eyre::Result; + use openvm_algebra_circuit::ModularExtension; + use openvm_algebra_transpiler::ModularTranspilerExtension; + use openvm_circuit::{ + arch::{instructions::exe::VmExe, SystemConfig}, + utils::{air_test, air_test_with_min_segments}, + }; + use openvm_ecc_circuit::{Rv32WeierstrassConfig, WeierstrassExtension, SECP256K1_CONFIG}; + use openvm_ecc_transpiler::EccTranspilerExtension; + use openvm_keccak256_transpiler::Keccak256TranspilerExtension; + use openvm_rv32im_transpiler::{ + Rv32ITranspilerExtension, Rv32IoTranspilerExtension, Rv32MTranspilerExtension, + }; + use openvm_sdk::config::SdkVmConfig; + use openvm_stark_backend::p3_field::AbstractField; + use openvm_stark_sdk::{openvm_stark_backend, p3_baby_bear::BabyBear}; + use openvm_toolchain_tests::{build_example_program_at_path_with_features, get_programs_dir}; + use openvm_transpiler::{transpiler::Transpiler, FromElf}; + type F = BabyBear; + + #[test] + fn test_ec() -> Result<()> { + let elf = build_example_program_at_path_with_features(get_programs_dir!(), "ec", ["k256"])?; + let openvm_exe = VmExe::from_elf( + elf, + Transpiler::::default() + .with_extension(Rv32ITranspilerExtension) + .with_extension(Rv32MTranspilerExtension) + .with_extension(Rv32IoTranspilerExtension) + .with_extension(EccTranspilerExtension) + .with_extension(ModularTranspilerExtension), + )?; + let config = Rv32WeierstrassConfig::new(vec![SECP256K1_CONFIG.clone()]); + air_test(config, openvm_exe); + Ok(()) + } + + #[test] + fn test_decompress() -> Result<()> { + use openvm_ecc_guest::halo2curves::{group::Curve, secp256k1::Secp256k1Affine}; + + let elf = build_example_program_at_path_with_features( + get_programs_dir!(), + "decompress", + ["k256"], + )?; + let openvm_exe = VmExe::from_elf( + elf, + Transpiler::::default() + .with_extension(Rv32ITranspilerExtension) + .with_extension(Rv32MTranspilerExtension) + .with_extension(Rv32IoTranspilerExtension) + .with_extension(EccTranspilerExtension) + .with_extension(ModularTranspilerExtension), + )?; + let config = Rv32WeierstrassConfig::new(vec![SECP256K1_CONFIG.clone()]); + + let p = Secp256k1Affine::generator(); + let p = (p + p + p).to_affine(); + println!("decompressed: {:?}", p); + let coords: Vec<_> = [p.x.to_bytes(), p.y.to_bytes()] + .concat() + .into_iter() + .map(AbstractField::from_canonical_u8) + .collect(); + air_test_with_min_segments(config, openvm_exe, vec![coords], 1); + Ok(()) + } + + #[test] + fn test_ecdsa() -> Result<()> { + let elf = + build_example_program_at_path_with_features(get_programs_dir!(), "ecdsa", ["k256"])?; + let config = SdkVmConfig::builder() + .system(SystemConfig::default().with_continuations().into()) + .rv32i(Default::default()) + .rv32m(Default::default()) + .io(Default::default()) + .modular(ModularExtension::new(vec![ + SECP256K1_CONFIG.modulus.clone(), + SECP256K1_CONFIG.scalar.clone(), + ])) + .keccak(Default::default()) + .ecc(WeierstrassExtension::new(vec![SECP256K1_CONFIG.clone()])) + .build(); + let openvm_exe = VmExe::from_elf( + elf, + Transpiler::::default() + .with_extension(Rv32ITranspilerExtension) + .with_extension(Rv32MTranspilerExtension) + .with_extension(Rv32IoTranspilerExtension) + .with_extension(Keccak256TranspilerExtension) + .with_extension(EccTranspilerExtension) + .with_extension(ModularTranspilerExtension), + )?; + air_test(config, openvm_exe); + Ok(()) + } +} diff --git a/extensions/keccak256/tests/Cargo.toml b/extensions/keccak256/tests/Cargo.toml new file mode 100644 index 0000000000..6148313ea3 --- /dev/null +++ b/extensions/keccak256/tests/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "openvm-keccak256-integration-tests" +description = "Integration tests for the OpenVM keccak256 extension" +version.workspace = true +authors.workspace = true +edition.workspace = true +homepage.workspace = true +repository.workspace = true + +[dependencies] +openvm-circuit-primitives-derive.workspace = true +openvm-instructions = { workspace = true } +openvm-stark-sdk.workspace = true +openvm-circuit = { workspace = true, features = ["test-utils"] } +openvm-transpiler.workspace = true +openvm-build.workspace = true +openvm-keccak256-transpiler.workspace = true +openvm-keccak256-circuit.workspace = true +openvm-rv32im-transpiler.workspace = true +openvm-platform = { workspace = true } +openvm = { workspace = true } +openvm-toolchain-tests = { path = "../../../crates/toolchain/tests" } +eyre.workspace = true + +[features] +default = ["parallel"] +parallel = ["openvm-circuit/parallel"] diff --git a/extensions/keccak256/tests/programs/Cargo.toml b/extensions/keccak256/tests/programs/Cargo.toml new file mode 100644 index 0000000000..e5e0adce43 --- /dev/null +++ b/extensions/keccak256/tests/programs/Cargo.toml @@ -0,0 +1,29 @@ +[workspace] +[package] +name = "openvm-keccak256-test-programs" +version = "0.0.0" +edition = "2021" + +[dependencies] +openvm = { path = "../../../../crates/toolchain/openvm" } +openvm-platform = { path = "../../../../crates/toolchain/platform" } +openvm-keccak256-guest = { path = "../../guest" } +hex = { version = "0.4.3", default-features = false, features = ["alloc"] } +serde = { version = "1.0", default-features = false, features = [ + "alloc", + "derive", +] } + + +[features] +default = [] +std = [ + "serde/std", + "openvm/std", + "openvm-keccak256-guest/std", +] + +[profile.release] +panic = "abort" +lto = "thin" # turn on lto = fat to decrease binary size, but this optimizes out some missing extern links so we shouldn't use it for testing +# strip = "symbols" diff --git a/crates/toolchain/tests/programs/examples/keccak.rs b/extensions/keccak256/tests/programs/examples/keccak.rs similarity index 100% rename from crates/toolchain/tests/programs/examples/keccak.rs rename to extensions/keccak256/tests/programs/examples/keccak.rs diff --git a/extensions/keccak256/tests/src/lib.rs b/extensions/keccak256/tests/src/lib.rs new file mode 100644 index 0000000000..ecb2f524ee --- /dev/null +++ b/extensions/keccak256/tests/src/lib.rs @@ -0,0 +1,31 @@ +#[cfg(test)] +mod tests { + use eyre::Result; + use openvm_circuit::utils::air_test; + use openvm_instructions::exe::VmExe; + use openvm_keccak256_circuit::Keccak256Rv32Config; + use openvm_keccak256_transpiler::Keccak256TranspilerExtension; + use openvm_rv32im_transpiler::{ + Rv32ITranspilerExtension, Rv32IoTranspilerExtension, Rv32MTranspilerExtension, + }; + use openvm_stark_sdk::p3_baby_bear::BabyBear; + use openvm_toolchain_tests::{build_example_program_at_path, get_programs_dir}; + use openvm_transpiler::{transpiler::Transpiler, FromElf}; + + type F = BabyBear; + + #[test] + fn test_keccak256() -> Result<()> { + let elf = build_example_program_at_path(get_programs_dir!(), "keccak")?; + let openvm_exe = VmExe::from_elf( + elf, + Transpiler::::default() + .with_extension(Keccak256TranspilerExtension) + .with_extension(Rv32ITranspilerExtension) + .with_extension(Rv32MTranspilerExtension) + .with_extension(Rv32IoTranspilerExtension), + )?; + air_test(Keccak256Rv32Config::default(), openvm_exe); + Ok(()) + } +} diff --git a/extensions/pairing/tests/Cargo.toml b/extensions/pairing/tests/Cargo.toml new file mode 100644 index 0000000000..b536d32ad1 --- /dev/null +++ b/extensions/pairing/tests/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "openvm-pairing-integration-tests" +description = "Integration tests for the OpenVM pairing extension" +version.workspace = true +authors.workspace = true +edition.workspace = true +homepage.workspace = true +repository.workspace = true + +[dependencies] +openvm-circuit-primitives-derive.workspace = true +openvm-instructions = { workspace = true } +openvm-stark-sdk.workspace = true +openvm-circuit = { workspace = true, features = ["test-utils"] } +openvm-transpiler.workspace = true +openvm-build.workspace = true +openvm-algebra-circuit.workspace = true +openvm-algebra-transpiler.workspace = true +openvm-pairing-circuit.workspace = true +openvm-pairing-transpiler.workspace = true +openvm-pairing-guest.workspace = true +openvm-ecc-circuit.workspace = true +openvm-ecc-guest.workspace = true +openvm-rv32im-transpiler.workspace = true +openvm-platform = { workspace = true } +openvm = { workspace = true } +openvm-toolchain-tests = { path = "../../../crates/toolchain/tests" } +eyre.workspace = true +num-bigint-dig.workspace = true +rand.workspace = true + +[features] +default = ["parallel"] +parallel = ["openvm-circuit/parallel"] diff --git a/crates/toolchain/tests/programs/Cargo.toml b/extensions/pairing/tests/programs/Cargo.toml similarity index 72% rename from crates/toolchain/tests/programs/Cargo.toml rename to extensions/pairing/tests/programs/Cargo.toml index bdbce056ef..891e806dcc 100644 --- a/crates/toolchain/tests/programs/Cargo.toml +++ b/extensions/pairing/tests/programs/Cargo.toml @@ -1,21 +1,20 @@ [workspace] [package] -name = "openvm-test-programs" +name = "openvm-pairing-test-programs" version = "0.0.0" edition = "2021" [dependencies] -openvm = { path = "../../openvm" } -openvm-platform = { path = "../../platform" } +openvm = { path = "../../../../crates/toolchain/openvm" } +openvm-platform = { path = "../../../../crates/toolchain/platform" } openvm-algebra-guest = { path = "../../../../extensions/algebra/guest", default-features = false } openvm-algebra-moduli-setup = { path = "../../../../extensions/algebra/moduli-setup", default-features = false } openvm-algebra-complex-macros = { path = "../../../../extensions/algebra/guest/src/field/complex-macros", default-features = false } -openvm-bigint-guest = { path = "../../../../extensions/bigint/guest" } openvm-ecc-guest = { path = "../../../../extensions/ecc/guest", default-features = false } openvm-ecc-sw-setup = { path = "../../../../extensions/ecc/sw-setup", default-features = false } -openvm-keccak256-guest = { path = "../../../../extensions/keccak256/guest" } openvm-pairing-guest = { path = "../../../../extensions/pairing/guest", default-features = false } + serde = { version = "1.0", default-features = false, features = [ "alloc", "derive", @@ -32,30 +31,17 @@ default = [] std = [ "serde/std", "openvm/std", - "openvm-algebra-guest/std", - "openvm-bigint-guest/std", - "openvm-ecc-guest/std", - "openvm-keccak256-guest/std", "openvm-pairing-guest/std", ] + bn254 = ["openvm-pairing-guest/bn254"] bls12_381 = ["openvm-pairing-guest/bls12_381"] -k256 = ["openvm-ecc-guest/k256", "dep:k256"] -heap-embedded-alloc = ["openvm/heap-embedded-alloc"] [profile.release] panic = "abort" lto = "thin" # turn on lto = fat to decrease binary size, but this optimizes out some missing extern links so we shouldn't use it for testing # strip = "symbols" -[[example]] -name = "ec" -required-features = ["k256"] - -[[example]] -name = "ecdsa" -required-features = ["k256"] - [[example]] name = "final_exp_hint" required-features = ["bls12_381"] diff --git a/crates/toolchain/tests/programs/examples/final_exp_hint.rs b/extensions/pairing/tests/programs/examples/final_exp_hint.rs similarity index 100% rename from crates/toolchain/tests/programs/examples/final_exp_hint.rs rename to extensions/pairing/tests/programs/examples/final_exp_hint.rs diff --git a/crates/toolchain/tests/programs/examples/fp12_mul.rs b/extensions/pairing/tests/programs/examples/fp12_mul.rs similarity index 100% rename from crates/toolchain/tests/programs/examples/fp12_mul.rs rename to extensions/pairing/tests/programs/examples/fp12_mul.rs diff --git a/crates/toolchain/tests/programs/examples/pairing_check.rs b/extensions/pairing/tests/programs/examples/pairing_check.rs similarity index 100% rename from crates/toolchain/tests/programs/examples/pairing_check.rs rename to extensions/pairing/tests/programs/examples/pairing_check.rs diff --git a/crates/toolchain/tests/programs/examples/pairing_line.rs b/extensions/pairing/tests/programs/examples/pairing_line.rs similarity index 100% rename from crates/toolchain/tests/programs/examples/pairing_line.rs rename to extensions/pairing/tests/programs/examples/pairing_line.rs diff --git a/crates/toolchain/tests/programs/examples/pairing_miller_loop.rs b/extensions/pairing/tests/programs/examples/pairing_miller_loop.rs similarity index 100% rename from crates/toolchain/tests/programs/examples/pairing_miller_loop.rs rename to extensions/pairing/tests/programs/examples/pairing_miller_loop.rs diff --git a/crates/toolchain/tests/programs/examples/pairing_miller_step.rs b/extensions/pairing/tests/programs/examples/pairing_miller_step.rs similarity index 100% rename from crates/toolchain/tests/programs/examples/pairing_miller_step.rs rename to extensions/pairing/tests/programs/examples/pairing_miller_step.rs diff --git a/crates/toolchain/tests/src/pairing_tests.rs b/extensions/pairing/tests/src/lib.rs similarity index 79% rename from crates/toolchain/tests/src/pairing_tests.rs rename to extensions/pairing/tests/src/lib.rs index 91faf1e2be..9ec3002c6f 100644 --- a/crates/toolchain/tests/src/pairing_tests.rs +++ b/extensions/pairing/tests/src/lib.rs @@ -1,41 +1,40 @@ #![allow(non_snake_case)] -use eyre::Result; -use openvm_algebra_circuit::{Fp2Extension, ModularExtension}; -use openvm_circuit::{ - arch::{instructions::exe::VmExe, SystemConfig}, - utils::new_air_test_with_min_segments, -}; -use openvm_ecc_circuit::WeierstrassExtension; -use openvm_ecc_guest::{algebra::field::FieldExtension, halo2curves::ff::Field, AffinePoint}; -use openvm_pairing_circuit::{PairingCurve, PairingExtension, Rv32PairingConfig}; -use openvm_pairing_guest::pairing::{EvaluatedLine, FinalExp, LineMulDType, MultiMillerLoop}; -use openvm_stark_sdk::{openvm_stark_backend::p3_field::AbstractField, p3_baby_bear::BabyBear}; -use openvm_transpiler::{transpiler::Transpiler, FromElf}; -use rand::SeedableRng; - -type F = BabyBear; - +#[cfg(test)] mod bn254 { use std::iter; + use eyre::Result; + use openvm_algebra_circuit::{Fp2Extension, ModularExtension}; use openvm_algebra_transpiler::{Fp2TranspilerExtension, ModularTranspilerExtension}; - use openvm_ecc_guest::halo2curves::{ - bn256::{Fq12, Fq2, Fr, G1Affine, G2Affine}, - ff::Field, + use openvm_circuit::{arch::SystemConfig, utils::air_test_with_min_segments}; + use openvm_ecc_circuit::WeierstrassExtension; + use openvm_ecc_guest::{ + algebra::field::FieldExtension, + halo2curves::{ + bn256::{Fq12, Fq2, Fr, G1Affine, G2Affine}, + ff::Field, + }, + AffinePoint, }; + use openvm_instructions::exe::VmExe; + use openvm_pairing_circuit::{PairingCurve, PairingExtension, Rv32PairingConfig}; use openvm_pairing_guest::{ - affine_point::AffineCoords, bn254::BN254_MODULUS, halo2curves_shims::bn254::Bn254, - pairing::MillerStep, + affine_point::AffineCoords, + bn254::BN254_MODULUS, + halo2curves_shims::bn254::Bn254, + pairing::{EvaluatedLine, LineMulDType, MillerStep, MultiMillerLoop}, }; use openvm_pairing_transpiler::PairingTranspilerExtension; use openvm_rv32im_transpiler::{ Rv32ITranspilerExtension, Rv32IoTranspilerExtension, Rv32MTranspilerExtension, }; - use openvm_transpiler::transpiler::Transpiler; + use openvm_stark_sdk::{openvm_stark_backend::p3_field::AbstractField, p3_baby_bear::BabyBear}; + use openvm_toolchain_tests::{build_example_program_at_path_with_features, get_programs_dir}; + use openvm_transpiler::{transpiler::Transpiler, FromElf}; + use rand::SeedableRng; - use super::*; - use crate::utils::build_example_program_with_features; + type F = BabyBear; pub fn get_testing_config() -> Rv32PairingConfig { let primes = [BN254_MODULUS.clone()]; @@ -53,7 +52,11 @@ mod bn254 { #[test] fn test_bn254_fp12_mul() -> Result<()> { - let elf = build_example_program_with_features("fp12_mul", ["bn254"])?; + let elf = build_example_program_at_path_with_features( + get_programs_dir!(), + "fp12_mul", + ["bn254"], + )?; let openvm_exe = VmExe::from_elf( elf, Transpiler::::default() @@ -77,13 +80,17 @@ mod bn254 { .map(AbstractField::from_canonical_u8) .collect::>(); - new_air_test_with_min_segments(get_testing_config(), openvm_exe, vec![io], 1, false); + air_test_with_min_segments(get_testing_config(), openvm_exe, vec![io], 1); Ok(()) } #[test] fn test_bn254_line_functions() -> Result<()> { - let elf = build_example_program_with_features("pairing_line", ["bn254"])?; + let elf = build_example_program_at_path_with_features( + get_programs_dir!(), + "pairing_line", + ["bn254"], + )?; let openvm_exe = VmExe::from_elf( elf, Transpiler::::default() @@ -129,13 +136,17 @@ mod bn254 { let io_all = io0.into_iter().chain(io1).collect::>(); - new_air_test_with_min_segments(get_testing_config(), openvm_exe, vec![io_all], 1, false); + air_test_with_min_segments(get_testing_config(), openvm_exe, vec![io_all], 1); Ok(()) } #[test] fn test_bn254_miller_step() -> Result<()> { - let elf = build_example_program_with_features("pairing_miller_step", ["bn254"])?; + let elf = build_example_program_at_path_with_features( + get_programs_dir!(), + "pairing_miller_step", + ["bn254"], + )?; let openvm_exe = VmExe::from_elf( elf, Transpiler::::default() @@ -172,13 +183,17 @@ mod bn254 { let io_all = io0.into_iter().chain(io1).collect::>(); - new_air_test_with_min_segments(get_testing_config(), openvm_exe, vec![io_all], 1, false); + air_test_with_min_segments(get_testing_config(), openvm_exe, vec![io_all], 1); Ok(()) } #[test] fn test_bn254_miller_loop() -> Result<()> { - let elf = build_example_program_with_features("pairing_miller_loop", ["bn254"])?; + let elf = build_example_program_at_path_with_features( + get_programs_dir!(), + "pairing_miller_loop", + ["bn254"], + )?; let openvm_exe = VmExe::from_elf( elf, Transpiler::::default() @@ -219,13 +234,17 @@ mod bn254 { let io_all = io0.into_iter().chain(io1).collect::>(); - new_air_test_with_min_segments(get_testing_config(), openvm_exe, vec![io_all], 1, true); + air_test_with_min_segments(get_testing_config(), openvm_exe, vec![io_all], 1); Ok(()) } #[test] fn test_bn254_pairing_check() -> Result<()> { - let elf = build_example_program_with_features("pairing_check", ["bn254"])?; + let elf = build_example_program_at_path_with_features( + get_programs_dir!(), + "pairing_check", + ["bn254"], + )?; let openvm_exe = VmExe::from_elf( elf, Transpiler::::default() @@ -270,31 +289,45 @@ mod bn254 { let io_all = io0.into_iter().chain(io1).collect::>(); - // Always run proving for just pairing check - new_air_test_with_min_segments(get_testing_config(), openvm_exe, vec![io_all], 1, true); + air_test_with_min_segments(get_testing_config(), openvm_exe, vec![io_all], 1); Ok(()) } } +#[cfg(test)] mod bls12_381 { + use eyre::Result; + use openvm_algebra_circuit::{Fp2Extension, ModularExtension}; use openvm_algebra_transpiler::{Fp2TranspilerExtension, ModularTranspilerExtension}; + use openvm_circuit::{ + arch::{instructions::exe::VmExe, SystemConfig}, + utils::air_test_with_min_segments, + }; + use openvm_ecc_circuit::WeierstrassExtension; use openvm_ecc_guest::{ - algebra::IntMod, - halo2curves::bls12_381::{Fq12, Fq2, Fr, G1Affine, G2Affine}, + algebra::{field::FieldExtension, IntMod}, + halo2curves::{ + bls12_381::{Fq12, Fq2, Fr, G1Affine, G2Affine}, + ff::Field, + }, AffinePoint, }; + use openvm_pairing_circuit::{PairingCurve, PairingExtension, Rv32PairingConfig}; use openvm_pairing_guest::{ bls12_381::BLS12_381_MODULUS, halo2curves_shims::bls12_381::Bls12_381, - pairing::{LineMulMType, MillerStep}, + pairing::{EvaluatedLine, FinalExp, LineMulMType, MillerStep, MultiMillerLoop}, }; use openvm_pairing_transpiler::PairingTranspilerExtension; use openvm_rv32im_transpiler::{ Rv32ITranspilerExtension, Rv32IoTranspilerExtension, Rv32MTranspilerExtension, }; + use openvm_stark_sdk::{openvm_stark_backend::p3_field::AbstractField, p3_baby_bear::BabyBear}; + use openvm_toolchain_tests::{build_example_program_at_path_with_features, get_programs_dir}; + use openvm_transpiler::{transpiler::Transpiler, FromElf}; + use rand::SeedableRng; - use super::*; - use crate::utils::build_example_program_with_features; + type F = BabyBear; pub fn get_testing_config() -> Rv32PairingConfig { let primes = [BLS12_381_MODULUS.clone()]; @@ -312,7 +345,11 @@ mod bls12_381 { #[test] fn test_bls12_381_fp12_mul() -> Result<()> { - let elf = build_example_program_with_features("fp12_mul", ["bls12_381"])?; + let elf = build_example_program_at_path_with_features( + get_programs_dir!(), + "fp12_mul", + ["bls12_381"], + )?; let openvm_exe = VmExe::from_elf( elf, Transpiler::::default() @@ -336,13 +373,17 @@ mod bls12_381 { .map(AbstractField::from_canonical_u8) .collect::>(); - new_air_test_with_min_segments(get_testing_config(), openvm_exe, vec![io], 1, false); + air_test_with_min_segments(get_testing_config(), openvm_exe, vec![io], 1); Ok(()) } #[test] fn test_bls12_381_line_functions() -> Result<()> { - let elf = build_example_program_with_features("pairing_line", ["bls12_381"])?; + let elf = build_example_program_at_path_with_features( + get_programs_dir!(), + "pairing_line", + ["bls12_381"], + )?; let openvm_exe = VmExe::from_elf( elf, Transpiler::::default() @@ -389,13 +430,17 @@ mod bls12_381 { let io_all = io0.into_iter().chain(io1).collect::>(); - new_air_test_with_min_segments(get_testing_config(), openvm_exe, vec![io_all], 1, false); + air_test_with_min_segments(get_testing_config(), openvm_exe, vec![io_all], 1); Ok(()) } #[test] fn test_bls12_381_miller_step() -> Result<()> { - let elf = build_example_program_with_features("pairing_miller_step", ["bls12_381"])?; + let elf = build_example_program_at_path_with_features( + get_programs_dir!(), + "pairing_miller_step", + ["bls12_381"], + )?; let openvm_exe = VmExe::from_elf( elf, Transpiler::::default() @@ -432,13 +477,17 @@ mod bls12_381 { let io_all = io0.into_iter().chain(io1).collect::>(); - new_air_test_with_min_segments(get_testing_config(), openvm_exe, vec![io_all], 1, false); + air_test_with_min_segments(get_testing_config(), openvm_exe, vec![io_all], 1); Ok(()) } #[test] fn test_bls12_381_miller_loop() -> Result<()> { - let elf = build_example_program_with_features("pairing_miller_loop", ["bls12_381"])?; + let elf = build_example_program_at_path_with_features( + get_programs_dir!(), + "pairing_miller_loop", + ["bls12_381"], + )?; let openvm_exe = VmExe::from_elf( elf, Transpiler::::default() @@ -485,13 +534,17 @@ mod bls12_381 { let io_all = io0.into_iter().chain(io1).collect::>(); - new_air_test_with_min_segments(get_testing_config(), openvm_exe, vec![io_all], 1, false); + air_test_with_min_segments(get_testing_config(), openvm_exe, vec![io_all], 1); Ok(()) } #[test] fn test_bls12_381_pairing_check() -> Result<()> { - let elf = build_example_program_with_features("pairing_check", ["bls12_381"])?; + let elf = build_example_program_at_path_with_features( + get_programs_dir!(), + "pairing_check", + ["bls12_381"], + )?; let openvm_exe = VmExe::from_elf( elf, Transpiler::::default() @@ -535,14 +588,17 @@ mod bls12_381 { let io_all = io0.into_iter().chain(io1).collect::>(); - // Always run proving for just pairing check - new_air_test_with_min_segments(get_testing_config(), openvm_exe, vec![io_all], 1, true); + air_test_with_min_segments(get_testing_config(), openvm_exe, vec![io_all], 1); Ok(()) } #[test] fn test_bls12_381_final_exp_hint() -> Result<()> { - let elf = build_example_program_with_features("final_exp_hint", ["bls12_381"])?; + let elf = build_example_program_at_path_with_features( + get_programs_dir!(), + "final_exp_hint", + ["bls12_381"], + )?; let openvm_exe = VmExe::from_elf( elf, Transpiler::::default() @@ -585,7 +641,7 @@ mod bls12_381 { .flat_map(|w| w.to_le_bytes()) .map(F::from_canonical_u8) .collect(); - new_air_test_with_min_segments(get_testing_config(), openvm_exe, vec![io], 1, false); + air_test_with_min_segments(get_testing_config(), openvm_exe, vec![io], 1); Ok(()) } } diff --git a/extensions/rv32im/tests/Cargo.toml b/extensions/rv32im/tests/Cargo.toml new file mode 100644 index 0000000000..731ebb0729 --- /dev/null +++ b/extensions/rv32im/tests/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "openvm-rv32im-integration-tests" +description = "Integration tests for the OpenVM rv32im extension" +version.workspace = true +authors.workspace = true +edition.workspace = true +homepage.workspace = true +repository.workspace = true + +[dependencies] +openvm-circuit-primitives-derive.workspace = true +openvm-instructions = { workspace = true } +openvm-stark-sdk.workspace = true +openvm-circuit = { workspace = true, features = ["test-utils"] } +openvm-transpiler.workspace = true +openvm-build.workspace = true +openvm-rv32im-circuit.workspace = true +openvm-rv32im-transpiler.workspace = true +openvm-platform = { workspace = true } +openvm = { workspace = true } +openvm-toolchain-tests = { path = "../../../crates/toolchain/tests" } +eyre.workspace = true +test-case.workspace = true +serde = { workspace = true, features = ["alloc"] } + +[features] +default = ["parallel"] +parallel = ["openvm-circuit/parallel"] diff --git a/extensions/rv32im/tests/programs/Cargo.toml b/extensions/rv32im/tests/programs/Cargo.toml new file mode 100644 index 0000000000..3239eed123 --- /dev/null +++ b/extensions/rv32im/tests/programs/Cargo.toml @@ -0,0 +1,29 @@ +[workspace] +[package] +name = "openvm-rv32im-test-programs" +version = "0.0.0" +edition = "2021" + +[dependencies] +openvm = { path = "../../../../crates/toolchain/openvm", default-features = false } +openvm-platform = { path = "../../../../crates/toolchain/platform", default-features = false } +openvm-rv32im-guest = { path = "../../guest", default-features = false } +serde = { version = "1.0", default-features = false, features = [ + "alloc", + "derive", +] } + + +[features] +default = [] +std = [ + "serde/std", + "openvm/std", +] + +heap-embedded-alloc = ["openvm/heap-embedded-alloc"] + +[profile.release] +panic = "abort" +lto = "thin" # turn on lto = fat to decrease binary size, but this optimizes out some missing extern links so we shouldn't use it for testing +# strip = "symbols" diff --git a/crates/toolchain/tests/programs/examples/collatz.rs b/extensions/rv32im/tests/programs/examples/collatz.rs similarity index 100% rename from crates/toolchain/tests/programs/examples/collatz.rs rename to extensions/rv32im/tests/programs/examples/collatz.rs diff --git a/crates/toolchain/tests/programs/examples/fibonacci.rs b/extensions/rv32im/tests/programs/examples/fibonacci.rs similarity index 100% rename from crates/toolchain/tests/programs/examples/fibonacci.rs rename to extensions/rv32im/tests/programs/examples/fibonacci.rs diff --git a/crates/toolchain/tests/programs/examples/hint.rs b/extensions/rv32im/tests/programs/examples/hint.rs similarity index 100% rename from crates/toolchain/tests/programs/examples/hint.rs rename to extensions/rv32im/tests/programs/examples/hint.rs diff --git a/crates/toolchain/tests/programs/examples/print.rs b/extensions/rv32im/tests/programs/examples/print.rs similarity index 100% rename from crates/toolchain/tests/programs/examples/print.rs rename to extensions/rv32im/tests/programs/examples/print.rs diff --git a/crates/toolchain/tests/programs/examples/read.rs b/extensions/rv32im/tests/programs/examples/read.rs similarity index 100% rename from crates/toolchain/tests/programs/examples/read.rs rename to extensions/rv32im/tests/programs/examples/read.rs diff --git a/crates/toolchain/tests/programs/examples/reveal.rs b/extensions/rv32im/tests/programs/examples/reveal.rs similarity index 100% rename from crates/toolchain/tests/programs/examples/reveal.rs rename to extensions/rv32im/tests/programs/examples/reveal.rs diff --git a/crates/toolchain/tests/programs/examples/tiny-mem-test.rs b/extensions/rv32im/tests/programs/examples/tiny-mem-test.rs similarity index 100% rename from crates/toolchain/tests/programs/examples/tiny-mem-test.rs rename to extensions/rv32im/tests/programs/examples/tiny-mem-test.rs diff --git a/extensions/rv32im/tests/src/lib.rs b/extensions/rv32im/tests/src/lib.rs new file mode 100644 index 0000000000..d44ee4c7eb --- /dev/null +++ b/extensions/rv32im/tests/src/lib.rs @@ -0,0 +1,187 @@ +#[cfg(test)] +mod tests { + use eyre::Result; + use openvm_circuit::{ + arch::{hasher::poseidon2::vm_poseidon2_hasher, VmExecutor}, + system::memory::tree::public_values::UserPublicValuesProof, + utils::{air_test, air_test_with_min_segments}, + }; + use openvm_instructions::exe::VmExe; + use openvm_rv32im_circuit::{Rv32IConfig, Rv32ImConfig}; + use openvm_rv32im_transpiler::{ + Rv32ITranspilerExtension, Rv32IoTranspilerExtension, Rv32MTranspilerExtension, + }; + use openvm_stark_sdk::{openvm_stark_backend::p3_field::AbstractField, p3_baby_bear::BabyBear}; + use openvm_toolchain_tests::{ + build_example_program_at_path, build_example_program_at_path_with_features, + get_programs_dir, + }; + use openvm_transpiler::{ + elf::ELF_DEFAULT_MAX_NUM_PUBLIC_VALUES, transpiler::Transpiler, FromElf, + }; + use test_case::test_case; + + type F = BabyBear; + + #[test_case("fibonacci", 1)] + fn test_rv32i(example_name: &str, min_segments: usize) -> Result<()> { + let elf = build_example_program_at_path(get_programs_dir!(), example_name)?; + let exe = VmExe::from_elf( + elf, + Transpiler::::default() + .with_extension(Rv32ITranspilerExtension) + .with_extension(Rv32MTranspilerExtension) + .with_extension(Rv32IoTranspilerExtension), + )?; + let config = Rv32IConfig::default(); + air_test_with_min_segments(config, exe, vec![], min_segments); + Ok(()) + } + + #[test_case("collatz", 1)] + fn test_rv32im(example_name: &str, min_segments: usize) -> Result<()> { + let elf = build_example_program_at_path(get_programs_dir!(), example_name)?; + let exe = VmExe::from_elf( + elf, + Transpiler::::default() + .with_extension(Rv32ITranspilerExtension) + .with_extension(Rv32IoTranspilerExtension) + .with_extension(Rv32MTranspilerExtension), + )?; + let config = Rv32ImConfig::default(); + air_test_with_min_segments(config, exe, vec![], min_segments); + Ok(()) + } + + // #[test_case("fibonacci", 1)] + #[test_case("collatz", 1)] + fn test_rv32im_std(example_name: &str, min_segments: usize) -> Result<()> { + let elf = build_example_program_at_path_with_features( + get_programs_dir!(), + example_name, + ["std"], + )?; + let exe = VmExe::from_elf( + elf, + Transpiler::::default() + .with_extension(Rv32ITranspilerExtension) + .with_extension(Rv32IoTranspilerExtension) + .with_extension(Rv32MTranspilerExtension), + )?; + let config = Rv32ImConfig::default(); + air_test_with_min_segments(config, exe, vec![], min_segments); + Ok(()) + } + + #[test] + fn test_read_vec() -> Result<()> { + let elf = build_example_program_at_path(get_programs_dir!(), "hint")?; + let exe = VmExe::from_elf( + elf, + Transpiler::::default() + .with_extension(Rv32ITranspilerExtension) + .with_extension(Rv32MTranspilerExtension) + .with_extension(Rv32IoTranspilerExtension), + )?; + let config = Rv32IConfig::default(); + let input = vec![[0, 1, 2, 3].map(F::from_canonical_u8).to_vec()]; + air_test_with_min_segments(config, exe, input, 1); + Ok(()) + } + + #[test] + fn test_read() -> Result<()> { + let elf = build_example_program_at_path(get_programs_dir!(), "read")?; + let exe = VmExe::from_elf( + elf, + Transpiler::::default() + .with_extension(Rv32ITranspilerExtension) + .with_extension(Rv32MTranspilerExtension) + .with_extension(Rv32IoTranspilerExtension), + )?; + let config = Rv32IConfig::default(); + + #[derive(serde::Serialize)] + struct Foo { + bar: u32, + baz: Vec, + } + let foo = Foo { + bar: 42, + baz: vec![0, 1, 2, 3], + }; + let serialized_foo = openvm::serde::to_vec(&foo).unwrap(); + let input = serialized_foo + .into_iter() + .flat_map(|w| w.to_le_bytes()) + .map(F::from_canonical_u8) + .collect(); + air_test_with_min_segments(config, exe, vec![input], 1); + Ok(()) + } + + #[test] + fn test_reveal() -> Result<()> { + let elf = build_example_program_at_path(get_programs_dir!(), "reveal")?; + let exe = VmExe::from_elf( + elf, + Transpiler::::default() + .with_extension(Rv32ITranspilerExtension) + .with_extension(Rv32MTranspilerExtension) + .with_extension(Rv32IoTranspilerExtension), + )?; + let config = Rv32IConfig::default(); + let executor = VmExecutor::::new(config.clone()); + let final_memory = executor.execute(exe, vec![])?.unwrap(); + let hasher = vm_poseidon2_hasher(); + let pv_proof = UserPublicValuesProof::compute( + config.system.memory_config.memory_dimensions(), + ELF_DEFAULT_MAX_NUM_PUBLIC_VALUES, + &hasher, + &final_memory, + ); + assert_eq!( + pv_proof.public_values, + [123, 0, 456, 0u32, 0u32, 0u32, 0u32, 0u32] + .into_iter() + .flat_map(|x| x.to_le_bytes()) + .map(F::from_canonical_u8) + .collect::>() + ); + Ok(()) + } + + #[test] + fn test_print() -> Result<()> { + let elf = build_example_program_at_path(get_programs_dir!(), "print")?; + let exe = VmExe::from_elf( + elf, + Transpiler::::default() + .with_extension(Rv32ITranspilerExtension) + .with_extension(Rv32MTranspilerExtension) + .with_extension(Rv32IoTranspilerExtension), + )?; + let config = Rv32IConfig::default(); + air_test(config, exe); + Ok(()) + } + + #[test] + fn test_tiny_mem_test() -> Result<()> { + let elf = build_example_program_at_path_with_features( + get_programs_dir!(), + "tiny-mem-test", + ["heap-embedded-alloc"], + )?; + let exe = VmExe::from_elf( + elf, + Transpiler::::default() + .with_extension(Rv32ITranspilerExtension) + .with_extension(Rv32MTranspilerExtension) + .with_extension(Rv32IoTranspilerExtension), + )?; + let config = Rv32ImConfig::default(); + air_test(config, exe); + Ok(()) + } +}