Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 1 addition & 12 deletions executor/src/flamegraph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
}
1 change: 1 addition & 0 deletions executor/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod elf;
pub mod flamegraph;
pub mod tests;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug (Medium): Missing #[cfg(test)] gate — this is the exact convention the PR claims to match.

Both prover/src/lib.rs and crypto/math/src/lib.rs declare this as:

#[cfg(test)]
pub mod tests;

Without the gate, the tests module is compiled into every non-test build and becomes part of the crate's public API (visible as executor::tests). It will be an empty module in non-test builds (since all sub-modules in mod.rs are individually gated), but it still pollutes the public surface.

Suggested change
pub mod tests;
#[cfg(test)]
pub mod tests;

pub mod vm;
9 changes: 9 additions & 0 deletions executor/src/tests/flamegraph_tests.rs
Original file line number Diff line number Diff line change
@@ -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");
}
88 changes: 88 additions & 0 deletions executor/src/tests/keccak_tests.rs
Original file line number Diff line number Diff line change
@@ -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
));
}
111 changes: 111 additions & 0 deletions executor/src/tests/memory_tests.rs
Original file line number Diff line number Diff line change
@@ -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));
}
6 changes: 6 additions & 0 deletions executor/src/tests/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#[cfg(test)]
pub mod flamegraph_tests;
#[cfg(test)]
pub mod keccak_tests;
#[cfg(test)]
pub mod memory_tests;
87 changes: 0 additions & 87 deletions executor/src/vm/instruction/execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
));
}
}
Loading
Loading