Skip to content
Merged
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//! End-to-end prove+verify for a compiled RV32 Fibonacci guest program under the trace-mode runner.
//!
//! The guest is authored in Rust under `riscv-tests/guests/rv32-fibonacci/` and its ROM bytes are committed in
//! `riscv-tests/binaries/rv32_fibonacci_rom.rs` so this test doesn't need to cross-compile at runtime.

#[path = "binaries/rv32_fibonacci_rom.rs"]
mod rv32_fibonacci_rom;

use neo_fold::riscv_trace_shard::Rv32TraceWiring;
use neo_math::F;
use p3_field::PrimeCharacteristicRing;

#[test]
fn test_riscv_fibonacci_compiled_trace_prove_verify() {
// The guest reads n from RAM[0x104], computes fib(n), and writes the result to RAM[0x100].
let n = 10u32;
let expected = F::from_u64(55);

let program_base = rv32_fibonacci_rom::RV32_FIBONACCI_ROM_BASE;
let program_bytes: &[u8] = &rv32_fibonacci_rom::RV32_FIBONACCI_ROM;

let mut run = Rv32TraceWiring::from_rom(program_base, program_bytes)
.xlen(32)
.max_steps(64)
.ram_init_u32(/*addr=*/ 0x104, n)
.shout_auto_minimal()
.output(/*output_addr=*/ 0x100, /*expected_output=*/ expected)
.prove()
.expect("trace-mode prove fibonacci");

run.verify().expect("trace-mode verify fibonacci");

// Wrong output must fail: prove with wrong expected value should fail at verify.
let wrong_run = Rv32TraceWiring::from_rom(program_base, program_bytes)
.xlen(32)
.max_steps(64)
.ram_init_u32(/*addr=*/ 0x104, n)
.shout_auto_minimal()
.output(/*output_addr=*/ 0x100, /*expected_output=*/ F::from_u64(56))
.prove();

match wrong_run {
Ok(mut run_bad) => {
assert!(
run_bad.verify().is_err(),
"wrong output claim must not verify"
);
}
Err(_) => {
// Prove itself failed, which is also acceptable.
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
//! End-to-end prove+verify for a compiled RV32 smoke-test guest program under the trace-mode runner.
//!
//! The guest is authored in Rust under `riscv-tests/guests/rv32-smoke/` and its ROM bytes are committed in
//! `riscv-tests/binaries/rv32_smoke_rom.rs` so this test doesn't need to cross-compile at runtime.

#[path = "binaries/rv32_smoke_rom.rs"]
mod rv32_smoke_rom;

use neo_fold::riscv_trace_shard::Rv32TraceWiring;
use neo_math::F;
use p3_field::PrimeCharacteristicRing;

#[test]
fn test_riscv_program_compiled_trace_prove_verify() {
let program_base = rv32_smoke_rom::RV32_SMOKE_ROM_BASE;
let program_bytes: &[u8] = &rv32_smoke_rom::RV32_SMOKE_ROM;

let mut run = Rv32TraceWiring::from_rom(program_base, program_bytes)
.xlen(32)
.shout_auto_minimal()
.output(
/*output_addr=*/ 0x100,
/*expected_output=*/ F::from_u64(0x100c),
)
.prove()
.expect("trace-mode prove compiled smoke");

run.verify().expect("trace-mode verify compiled smoke");

// Wrong output must fail.
let wrong_run = Rv32TraceWiring::from_rom(program_base, program_bytes)
.xlen(32)
.shout_auto_minimal()
.output(
/*output_addr=*/ 0x100,
/*expected_output=*/ F::from_u64(0x100d),
)
.prove();

match wrong_run {
Ok(mut run_bad) => {
assert!(
run_bad.verify().is_err(),
"wrong output claim must not verify"
);
}
Err(_) => {}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
//! End-to-end prove+verify for RV32M (M-extension) operations under the trace-mode runner.
//!
//! This validates that M-extension ops (MUL, DIV, etc.) are correctly handled
//! via Shout lookups in trace mode (table IDs 12-19).

use neo_fold::riscv_trace_shard::Rv32TraceWiring;
use neo_memory::riscv::lookups::{encode_program, RiscvInstruction, RiscvOpcode};

#[test]
#[ignore = "M-ext Shout tables need closed-form MLE or a packed-key proof path for xlen=32"]
fn test_riscv_program_rv32m_trace_prove_verify() {
let program = vec![
RiscvInstruction::IAlu {
op: RiscvOpcode::Add,
rd: 1,
rs1: 0,
imm: -6,
}, // x1 = -6 (0xFFFFFFFA)
RiscvInstruction::IAlu {
op: RiscvOpcode::Add,
rd: 2,
rs1: 0,
imm: 3,
}, // x2 = 3
RiscvInstruction::RAlu {
op: RiscvOpcode::Mul,
rd: 3,
rs1: 1,
rs2: 2,
}, // x3 = (-6)*3 = -18
RiscvInstruction::RAlu {
op: RiscvOpcode::Div,
rd: 4,
rs1: 1,
rs2: 2,
}, // x4 = (-6)/3 = -2
RiscvInstruction::Halt,
];
let program_bytes = encode_program(&program);

let mut run = Rv32TraceWiring::from_rom(/*program_base=*/ 0, &program_bytes)
.xlen(32)
.min_trace_len(1)
.prove()
.expect("trace-mode prove with RV32M ops");

run.verify().expect("trace-mode verify with RV32M ops");
}

#[test]
#[ignore = "M-ext Shout tables need closed-form MLE or a packed-key proof path for xlen=32"]
fn test_riscv_program_rv32m_all_ops_trace_prove_verify() {
let program = vec![
RiscvInstruction::IAlu {
op: RiscvOpcode::Add,
rd: 1,
rs1: 0,
imm: 7,
}, // x1 = 7
RiscvInstruction::IAlu {
op: RiscvOpcode::Add,
rd: 2,
rs1: 0,
imm: 3,
}, // x2 = 3
// MUL: x3 = 7*3 = 21
RiscvInstruction::RAlu {
op: RiscvOpcode::Mul,
rd: 3,
rs1: 1,
rs2: 2,
},
// MULH: x4 = high bits of signed(7)*signed(3) = 0
RiscvInstruction::RAlu {
op: RiscvOpcode::Mulh,
rd: 4,
rs1: 1,
rs2: 2,
},
// MULHSU: x5 = high bits of signed(7)*unsigned(3) = 0
RiscvInstruction::RAlu {
op: RiscvOpcode::Mulhsu,
rd: 5,
rs1: 1,
rs2: 2,
},
// MULHU: x6 = high bits of unsigned(7)*unsigned(3) = 0
RiscvInstruction::RAlu {
op: RiscvOpcode::Mulhu,
rd: 6,
rs1: 1,
rs2: 2,
},
// DIV: x7 = 7/3 = 2
RiscvInstruction::RAlu {
op: RiscvOpcode::Div,
rd: 7,
rs1: 1,
rs2: 2,
},
// DIVU: x8 = 7/3 = 2 (unsigned)
RiscvInstruction::RAlu {
op: RiscvOpcode::Divu,
rd: 8,
rs1: 1,
rs2: 2,
},
// REM: x9 = 7%3 = 1
RiscvInstruction::RAlu {
op: RiscvOpcode::Rem,
rd: 9,
rs1: 1,
rs2: 2,
},
// REMU: x10 = 7%3 = 1 (unsigned)
RiscvInstruction::RAlu {
op: RiscvOpcode::Remu,
rd: 10,
rs1: 1,
rs2: 2,
},
RiscvInstruction::Halt,
];
let program_bytes = encode_program(&program);

let mut run = Rv32TraceWiring::from_rom(/*program_base=*/ 0, &program_bytes)
.xlen(32)
.min_trace_len(1)
.prove()
.expect("trace-mode prove with all RV32M ops");

run.verify()
.expect("trace-mode verify with all RV32M ops");
}

#[test]
#[ignore = "M-ext Shout tables need closed-form MLE or a packed-key proof path for xlen=32"]
fn test_riscv_program_rv32m_signed_edge_cases_trace_prove_verify() {
let program = vec![
// x1 = -1 (0xFFFFFFFF)
RiscvInstruction::IAlu {
op: RiscvOpcode::Add,
rd: 1,
rs1: 0,
imm: -1,
},
// x2 = -1
RiscvInstruction::IAlu {
op: RiscvOpcode::Add,
rd: 2,
rs1: 0,
imm: -1,
},
// MULH(-1, -1): high bits of 1 = 0
RiscvInstruction::RAlu {
op: RiscvOpcode::Mulh,
rd: 3,
rs1: 1,
rs2: 2,
},
// MULHSU(-1, 0xFFFFFFFF): high bits of signed(-1)*unsigned(0xFFFFFFFF) = -1
RiscvInstruction::RAlu {
op: RiscvOpcode::Mulhsu,
rd: 4,
rs1: 1,
rs2: 2,
},
// DIV(-1, -1) = 1
RiscvInstruction::RAlu {
op: RiscvOpcode::Div,
rd: 5,
rs1: 1,
rs2: 2,
},
// x6 = 0 (divisor = 0 case)
RiscvInstruction::IAlu {
op: RiscvOpcode::Add,
rd: 6,
rs1: 0,
imm: 0,
},
// DIV(-1, 0) = -1 (RISC-V spec: division by zero returns -1)
RiscvInstruction::RAlu {
op: RiscvOpcode::Div,
rd: 7,
rs1: 1,
rs2: 6,
},
// DIVU(-1, 0) = 0xFFFFFFFF (RISC-V spec)
RiscvInstruction::RAlu {
op: RiscvOpcode::Divu,
rd: 8,
rs1: 1,
rs2: 6,
},
// REM(-1, 0) = -1 (RISC-V spec: remainder with divisor 0 = dividend)
RiscvInstruction::RAlu {
op: RiscvOpcode::Rem,
rd: 9,
rs1: 1,
rs2: 6,
},
RiscvInstruction::Halt,
];
let program_bytes = encode_program(&program);

let mut run = Rv32TraceWiring::from_rom(/*program_base=*/ 0, &program_bytes)
.xlen(32)
.min_trace_len(1)
.prove()
.expect("trace-mode prove with signed M-ext edge cases");

run.verify()
.expect("trace-mode verify with signed M-ext edge cases");
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
//! End-to-end prove+verify for a compiled RV32 guest with u64 output under the trace-mode runner.
//!
//! The guest is authored in Rust under `riscv-tests/guests/rv32-u64-output/` and its ROM bytes are committed in
//! `riscv-tests/binaries/rv32_u64_output_rom.rs` so this test doesn't need to cross-compile at runtime.

#[path = "binaries/rv32_u64_output_rom.rs"]
mod rv32_u64_output_rom;

use neo_fold::riscv_trace_shard::Rv32TraceWiring;
use neo_math::F;
use p3_field::PrimeCharacteristicRing;

#[test]
fn test_riscv_u64_output_compiled_trace_prove_verify() {
let output = 0x1122_3344_5566_7788u64;
let out_lo = F::from_u64(output as u32 as u64);
let out_hi = F::from_u64((output >> 32) as u32 as u64);

let program_base = rv32_u64_output_rom::RV32_U64_OUTPUT_ROM_BASE;
let program_bytes: &[u8] = &rv32_u64_output_rom::RV32_U64_OUTPUT_ROM;

let mut run = Rv32TraceWiring::from_rom(program_base, program_bytes)
.xlen(32)
.shout_auto_minimal()
.output_claim(/*addr=*/ 0x100, /*value=*/ out_lo)
.output_claim(/*addr=*/ 0x104, /*value=*/ out_hi)
.prove()
.expect("trace-mode prove u64 output");

run.verify().expect("trace-mode verify u64 output");

// Wrong output must fail.
let wrong_run = Rv32TraceWiring::from_rom(program_base, program_bytes)
.xlen(32)
.shout_auto_minimal()
.output_claim(/*addr=*/ 0x100, /*value=*/ out_lo)
.output_claim(/*addr=*/ 0x104, /*value=*/ F::from_u64(0))
.prove();

match wrong_run {
Ok(mut run_bad) => {
assert!(
run_bad.verify().is_err(),
"wrong output claim must not verify"
);
}
Err(_) => {}
}
}
Loading
Loading