From f888c8d32a52f2aadc8762a44ac71ee705c1c9b4 Mon Sep 17 00:00:00 2001 From: diegokingston Date: Fri, 22 May 2026 18:42:01 -0300 Subject: [PATCH] refactor(executor): move inline unit tests to src/tests/ module Relocates the three inline `#[cfg(test)] mod tests` blocks in executor/src into a dedicated `executor/src/tests/` module, matching the convention already used by the prover and math crates. - flamegraph.rs -> tests/flamegraph_tests.rs (demangle) - vm/instruction/execution.rs -> tests/keccak_tests.rs (keccak-f[1600] + syscall) - vm/memory.rs -> tests/memory_tests.rs (public output, load bounds) `demangle` and `Memory::set_bytes_aligned` were module-private; bumped to pub(crate) so the relocated tests can reach them. No test logic changed. --- executor/src/flamegraph.rs | 13 +-- executor/src/lib.rs | 1 + executor/src/tests/flamegraph_tests.rs | 9 ++ executor/src/tests/keccak_tests.rs | 88 +++++++++++++++++ executor/src/tests/memory_tests.rs | 111 +++++++++++++++++++++ executor/src/tests/mod.rs | 6 ++ executor/src/vm/instruction/execution.rs | 87 ----------------- executor/src/vm/memory.rs | 119 +---------------------- 8 files changed, 221 insertions(+), 213 deletions(-) create mode 100644 executor/src/tests/flamegraph_tests.rs create mode 100644 executor/src/tests/keccak_tests.rs create mode 100644 executor/src/tests/memory_tests.rs create mode 100644 executor/src/tests/mod.rs diff --git a/executor/src/flamegraph.rs b/executor/src/flamegraph.rs index 4fdb3e563..f9b447d19 100644 --- a/executor/src/flamegraph.rs +++ b/executor/src/flamegraph.rs @@ -154,18 +154,7 @@ impl FlamegraphGenerator { /// Demangle a Rust symbol name using the official rustc-demangle crate. /// /// Uses the alternate format (`{:#}`) to omit the hash suffix for cleaner output. -fn demangle(name: &str) -> String { +pub(crate) fn demangle(name: &str) -> String { // Use rustc-demangle with alternate format to omit hash format!("{:#}", rustc_demangle(name)) } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_demangle_simple() { - assert_eq!(demangle("main"), "main"); - assert_eq!(demangle("_start"), "_start"); - } -} diff --git a/executor/src/lib.rs b/executor/src/lib.rs index b17065ec8..fa8f22875 100644 --- a/executor/src/lib.rs +++ b/executor/src/lib.rs @@ -1,3 +1,4 @@ pub mod elf; pub mod flamegraph; +pub mod tests; pub mod vm; diff --git a/executor/src/tests/flamegraph_tests.rs b/executor/src/tests/flamegraph_tests.rs new file mode 100644 index 000000000..7d49c16c9 --- /dev/null +++ b/executor/src/tests/flamegraph_tests.rs @@ -0,0 +1,9 @@ +//! Tests for symbol demangling in flamegraph generation. + +use crate::flamegraph::demangle; + +#[test] +fn test_demangle_simple() { + assert_eq!(demangle("main"), "main"); + assert_eq!(demangle("_start"), "_start"); +} diff --git a/executor/src/tests/keccak_tests.rs b/executor/src/tests/keccak_tests.rs new file mode 100644 index 000000000..1c592f080 --- /dev/null +++ b/executor/src/tests/keccak_tests.rs @@ -0,0 +1,88 @@ +//! Tests for the Keccak-f[1600] permutation and the Keccak syscall. + +use crate::vm::instruction::decoding::Instruction; +use crate::vm::instruction::execution::{ExecutionError, KECCAK_SYSCALL_NUMBER, keccak_f1600}; +use crate::vm::memory::Memory; +use crate::vm::registers::Registers; + +#[test] +fn test_keccak_f1600_zero_input() { + let mut state = [0u64; 25]; + keccak_f1600(&mut state); + + let expected: [u64; 25] = [ + 0xF1258F7940E1DDE7, + 0x84D5CCF933C0478A, + 0xD598261EA65AA9EE, + 0xBD1547306F80494D, + 0x8B284E056253D057, + 0xFF97A42D7F8E6FD4, + 0x90FEE5A0A44647C4, + 0x8C5BDA0CD6192E76, + 0xAD30A6F71B19059C, + 0x30935AB7D08FFC64, + 0xEB5AA93F2317D635, + 0xA9A6E6260D712103, + 0x81A57C16DBCF555F, + 0x43B831CD0347C826, + 0x01F22F1A11A5569F, + 0x05E5635A21D9AE61, + 0x64BEFEF28CC970F2, + 0x613670957BC46611, + 0xB87C5A554FD00ECB, + 0x8C3EE88A1CCF32C8, + 0x940C7922AE3A2614, + 0x1841F924A2C509E4, + 0x16F53526E70465C2, + 0x75F644E97F30A13B, + 0xEAF1FF7B5CECA249, + ]; + + assert_eq!(state, expected, "keccak-f[1600] on zero input mismatch"); +} + +#[test] +fn test_keccak_f1600_nonzero_input() { + let mut state = [0u64; 25]; + state[0] = 1; + let original = state; + keccak_f1600(&mut state); + assert_ne!(state, original); + assert!(state.iter().any(|&x| x != 0)); +} + +#[test] +fn test_keccak_syscall_rejects_unaligned_state_addr() { + let mut pc = 0; + let mut registers = Registers::default(); + let mut memory = Memory::default(); + + registers.write(17, KECCAK_SYSCALL_NUMBER).unwrap(); + registers.write(10, 0x1001).unwrap(); + + let err = Instruction::EcallEbreak + .run(&mut pc, &mut registers, &mut memory) + .unwrap_err(); + assert!(matches!( + err, + ExecutionError::UnalignedKeccakStateAddress(0x1001) + )); +} + +#[test] +fn test_keccak_syscall_rejects_overflowing_state_range() { + let mut pc = 0; + let mut registers = Registers::default(); + let mut memory = Memory::default(); + + registers.write(17, KECCAK_SYSCALL_NUMBER).unwrap(); + registers.write(10, u64::MAX - 191).unwrap(); + + let err = Instruction::EcallEbreak + .run(&mut pc, &mut registers, &mut memory) + .unwrap_err(); + assert!(matches!( + err, + ExecutionError::KeccakStateAddressOverflow(addr) if addr == u64::MAX - 191 + )); +} diff --git a/executor/src/tests/memory_tests.rs b/executor/src/tests/memory_tests.rs new file mode 100644 index 000000000..fcc0e07b9 --- /dev/null +++ b/executor/src/tests/memory_tests.rs @@ -0,0 +1,111 @@ +//! Tests for guest memory: public-output commits and bounds-checked loads. + +use crate::vm::memory::{Memory, MemoryError}; + +#[test] +fn test_commit_public_output_single() { + let mut memory = Memory::default(); + memory.store_byte(0x100, b'a'); + memory.store_byte(0x101, b'b'); + + memory + .commit_public_output(0x100, 2) + .expect("commit should succeed"); + + assert_eq!( + memory + .read_return_value() + .expect("public output should be readable"), + b"ab".to_vec() + ); +} + +#[test] +fn test_commit_public_output_appends() { + let mut memory = Memory::default(); + memory.store_byte(0x100, b'a'); + memory.store_byte(0x101, b'b'); + memory.store_byte(0x104, b'c'); + memory.store_byte(0x105, b'd'); + + memory + .commit_public_output(0x100, 2) + .expect("first commit should succeed"); + memory + .commit_public_output(0x104, 2) + .expect("second commit should succeed"); + + // Append semantics: calls concatenate (EF zkVM IO interface). + assert_eq!( + memory + .read_return_value() + .expect("public output should be readable"), + b"abcd".to_vec() + ); +} + +#[test] +fn test_commit_public_output_empty_is_ok() { + let mut memory = Memory::default(); + memory + .commit_public_output(0, 0) + .expect("zero-length commit should succeed"); + assert!( + memory + .read_return_value() + .expect("public output should be readable") + .is_empty() + ); +} + +#[test] +fn test_commit_public_output_address_overflow() { + let mut memory = Memory::default(); + let err = memory + .commit_public_output(u64::MAX, 2) + .expect_err("address overflow must error, not panic"); + assert!(matches!(err, MemoryError::AddressOverflow)); +} + +#[test] +fn test_load_bytes_huge_len_returns_alloc_error() { + let memory = Memory::default(); + // A multi-petabyte allocation request from a guest must fail cleanly, + // not abort the host process via OOM. `addr=0` and `len=1<<50` keep + // `checked_add` happy so the path reaches the allocation. + let huge = 1u64 << 50; + let err = memory + .load_bytes(0, huge) + .expect_err("huge alloc must error, not abort"); + assert!(matches!(err, MemoryError::AllocationFailed)); +} + +#[test] +fn test_load_bytes_overflow_errors() { + let memory = Memory::default(); + let err = memory + .load_bytes(u64::MAX, 2) + .expect_err("address overflow must error, not panic"); + assert!(matches!(err, MemoryError::AddressOverflow)); +} + +#[test] +fn test_commit_public_output_total_cap() { + let mut memory = Memory::default(); + // Seed enough source bytes for two 512 KB writes. + let chunk = vec![0xAB; 512 * 1024]; + memory + .set_bytes_aligned(0x1_0000, &chunk) + .expect("seed should succeed"); + + memory + .commit_public_output(0x1_0000, 512 * 1024) + .expect("first 512 KB commit should succeed"); + memory + .commit_public_output(0x1_0000, 512 * 1024) + .expect("second 512 KB commit should succeed (total = 1 MB)"); + + // One more byte exceeds the 1 MB total cap. + let err = memory.commit_public_output(0x1_0000, 1).unwrap_err(); + assert!(matches!(err, MemoryError::CommitSizeExceeded)); +} diff --git a/executor/src/tests/mod.rs b/executor/src/tests/mod.rs new file mode 100644 index 000000000..575b5092a --- /dev/null +++ b/executor/src/tests/mod.rs @@ -0,0 +1,6 @@ +#[cfg(test)] +pub mod flamegraph_tests; +#[cfg(test)] +pub mod keccak_tests; +#[cfg(test)] +pub mod memory_tests; diff --git a/executor/src/vm/instruction/execution.rs b/executor/src/vm/instruction/execution.rs index 219414745..d9b0e1c8d 100644 --- a/executor/src/vm/instruction/execution.rs +++ b/executor/src/vm/instruction/execution.rs @@ -618,90 +618,3 @@ pub fn keccak_f1600(state: &mut [u64; 25]) { state[0] ^= rc; } } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_keccak_f1600_zero_input() { - let mut state = [0u64; 25]; - keccak_f1600(&mut state); - - let expected: [u64; 25] = [ - 0xF1258F7940E1DDE7, - 0x84D5CCF933C0478A, - 0xD598261EA65AA9EE, - 0xBD1547306F80494D, - 0x8B284E056253D057, - 0xFF97A42D7F8E6FD4, - 0x90FEE5A0A44647C4, - 0x8C5BDA0CD6192E76, - 0xAD30A6F71B19059C, - 0x30935AB7D08FFC64, - 0xEB5AA93F2317D635, - 0xA9A6E6260D712103, - 0x81A57C16DBCF555F, - 0x43B831CD0347C826, - 0x01F22F1A11A5569F, - 0x05E5635A21D9AE61, - 0x64BEFEF28CC970F2, - 0x613670957BC46611, - 0xB87C5A554FD00ECB, - 0x8C3EE88A1CCF32C8, - 0x940C7922AE3A2614, - 0x1841F924A2C509E4, - 0x16F53526E70465C2, - 0x75F644E97F30A13B, - 0xEAF1FF7B5CECA249, - ]; - - assert_eq!(state, expected, "keccak-f[1600] on zero input mismatch"); - } - - #[test] - fn test_keccak_f1600_nonzero_input() { - let mut state = [0u64; 25]; - state[0] = 1; - let original = state; - keccak_f1600(&mut state); - assert_ne!(state, original); - assert!(state.iter().any(|&x| x != 0)); - } - - #[test] - fn test_keccak_syscall_rejects_unaligned_state_addr() { - let mut pc = 0; - let mut registers = Registers::default(); - let mut memory = Memory::default(); - - registers.write(17, KECCAK_SYSCALL_NUMBER).unwrap(); - registers.write(10, 0x1001).unwrap(); - - let err = Instruction::EcallEbreak - .run(&mut pc, &mut registers, &mut memory) - .unwrap_err(); - assert!(matches!( - err, - ExecutionError::UnalignedKeccakStateAddress(0x1001) - )); - } - - #[test] - fn test_keccak_syscall_rejects_overflowing_state_range() { - let mut pc = 0; - let mut registers = Registers::default(); - let mut memory = Memory::default(); - - registers.write(17, KECCAK_SYSCALL_NUMBER).unwrap(); - registers.write(10, u64::MAX - 191).unwrap(); - - let err = Instruction::EcallEbreak - .run(&mut pc, &mut registers, &mut memory) - .unwrap_err(); - assert!(matches!( - err, - ExecutionError::KeccakStateAddressOverflow(addr) if addr == u64::MAX - 191 - )); - } -} diff --git a/executor/src/vm/memory.rs b/executor/src/vm/memory.rs index b78c98d44..c94376e76 100644 --- a/executor/src/vm/memory.rs +++ b/executor/src/vm/memory.rs @@ -213,7 +213,11 @@ impl Memory { /// Helper method to store a given input at an aligned address. It may also overwrite existing bytes with zero if inputs is not divisible by 4 /// Should only be used to write to public output and private input where these limitations are not a problem - fn set_bytes_aligned(&mut self, mut addr: u64, inputs: &[u8]) -> Result<(), MemoryError> { + pub(crate) fn set_bytes_aligned( + &mut self, + mut addr: u64, + inputs: &[u8], + ) -> Result<(), MemoryError> { if !addr.is_multiple_of(4) { return Err(MemoryError::UnalignedAccess); } @@ -242,116 +246,3 @@ pub enum MemoryError { #[error("Failed to allocate memory for load_bytes")] AllocationFailed, } - -#[cfg(test)] -mod tests { - use super::Memory; - - #[test] - fn test_commit_public_output_single() { - let mut memory = Memory::default(); - memory.store_byte(0x100, b'a'); - memory.store_byte(0x101, b'b'); - - memory - .commit_public_output(0x100, 2) - .expect("commit should succeed"); - - assert_eq!( - memory - .read_return_value() - .expect("public output should be readable"), - b"ab".to_vec() - ); - } - - #[test] - fn test_commit_public_output_appends() { - let mut memory = Memory::default(); - memory.store_byte(0x100, b'a'); - memory.store_byte(0x101, b'b'); - memory.store_byte(0x104, b'c'); - memory.store_byte(0x105, b'd'); - - memory - .commit_public_output(0x100, 2) - .expect("first commit should succeed"); - memory - .commit_public_output(0x104, 2) - .expect("second commit should succeed"); - - // Append semantics: calls concatenate (EF zkVM IO interface). - assert_eq!( - memory - .read_return_value() - .expect("public output should be readable"), - b"abcd".to_vec() - ); - } - - #[test] - fn test_commit_public_output_empty_is_ok() { - let mut memory = Memory::default(); - memory - .commit_public_output(0, 0) - .expect("zero-length commit should succeed"); - assert!( - memory - .read_return_value() - .expect("public output should be readable") - .is_empty() - ); - } - - #[test] - fn test_commit_public_output_address_overflow() { - let mut memory = Memory::default(); - let err = memory - .commit_public_output(u64::MAX, 2) - .expect_err("address overflow must error, not panic"); - assert!(matches!(err, super::MemoryError::AddressOverflow)); - } - - #[test] - fn test_load_bytes_huge_len_returns_alloc_error() { - let memory = Memory::default(); - // A multi-petabyte allocation request from a guest must fail cleanly, - // not abort the host process via OOM. `addr=0` and `len=1<<50` keep - // `checked_add` happy so the path reaches the allocation. - let huge = 1u64 << 50; - let err = memory - .load_bytes(0, huge) - .expect_err("huge alloc must error, not abort"); - assert!(matches!(err, super::MemoryError::AllocationFailed)); - } - - #[test] - fn test_load_bytes_overflow_errors() { - let memory = Memory::default(); - let err = memory - .load_bytes(u64::MAX, 2) - .expect_err("address overflow must error, not panic"); - assert!(matches!(err, super::MemoryError::AddressOverflow)); - } - - #[test] - fn test_commit_public_output_total_cap() { - let mut memory = Memory::default(); - // Seed enough source bytes for two 512 KB writes. - let chunk = vec![0xAB; 512 * 1024]; - memory - .set_bytes_aligned(0x1_0000, &chunk) - .expect("seed should succeed"); - - memory - .commit_public_output(0x1_0000, 512 * 1024) - .expect("first 512 KB commit should succeed"); - memory - .commit_public_output(0x1_0000, 512 * 1024) - .expect("second 512 KB commit should succeed (total = 1 MB)"); - - // One more byte exceeds the 1 MB total cap. - let err = memory.commit_public_output(0x1_0000, 1).unwrap_err(); - assert!(matches!(err, super::MemoryError::CommitSizeExceeded)); - } -}