Skip to main content

javm_guest_tests/
lib.rs

1//! JAVM guest test vectors — three-way conformance corpus.
2//!
3//! A library of pure, deterministic, `no_std`-friendly test
4//! functions that compile to both host (native Rust) and JAVM (PVM
5//! bytecode). Each operation has a `<name>_suite() -> u64`
6//! companion that runs the underlying byte-level helper over a
7//! baked corpus of inputs and XOR-folds the results into a single
8//! u64 fingerprint.
9//!
10//! The conformance harness (`tests/conformance.rs`) calls every
11//! suite three ways — host native, byte-PVM interpreter (via
12//! `jar-kernel`), JIT recompiler (via `javm-exec`) — and asserts
13//! the fingerprints agree, plus that the two PVM backends consume
14//! identical gas.
15//!
16//! Baking the corpus into the guest sidesteps the args-delivery
17//! problem: the kernel can pass `event.payload` into the
18//! interpreter, but the standalone recompiler has no equivalent.
19//! Returning one u64 also avoids reading guest memory post-halt.
20
21#![cfg_attr(target_os = "none", no_std)]
22
23use subsoil as _;
24
25pub mod tests;
26
27/// One row of [`SUITE_TABLE`]: (endpoint index, suite name, host fn).
28#[cfg(not(target_os = "none"))]
29pub type SuiteEntry = (u8, &'static str, fn() -> u64);
30
31/// Endpoint index → suite directory.
32///
33/// The host-side mirror of `src/main.rs`'s `#[subsoil::endpoint(N)]`
34/// table. Both lists must stay in sync; the conformance harness
35/// iterates this one to drive every endpoint without duplicating
36/// the indices in the test code.
37///
38/// The `#[subsoil::endpoint(N)]` annotations live in `main.rs`
39/// (the binary crate) because `#[used] static` in an rlib doesn't
40/// propagate into a bin's final ELF if nothing in the bin
41/// references it — the linker drops the whole rlib object file.
42#[cfg(not(target_os = "none"))]
43pub const SUITE_TABLE: &[SuiteEntry] = &[
44    (0, "add_u64_suite", tests::arithmetic::add_u64_suite),
45    (1, "sub_u64_suite", tests::arithmetic::sub_u64_suite),
46    (2, "mul_u64_suite", tests::arithmetic::mul_u64_suite),
47    (
48        3,
49        "mul_upper_uu_suite",
50        tests::arithmetic::mul_upper_uu_suite,
51    ),
52    (
53        4,
54        "mul_upper_ss_suite",
55        tests::arithmetic::mul_upper_ss_suite,
56    ),
57    (5, "div_u64_suite", tests::arithmetic::div_u64_suite),
58    (6, "rem_u64_suite", tests::arithmetic::rem_u64_suite),
59    (7, "div_s64_suite", tests::arithmetic::div_s64_suite),
60    (8, "rem_s64_suite", tests::arithmetic::rem_s64_suite),
61    (10, "shift_left_suite", tests::bitwise::shift_left_suite),
62    (
63        11,
64        "shift_right_logical_suite",
65        tests::bitwise::shift_right_logical_suite,
66    ),
67    (
68        12,
69        "shift_right_arithmetic_suite",
70        tests::bitwise::shift_right_arithmetic_suite,
71    ),
72    (13, "rotate_right_suite", tests::bitwise::rotate_right_suite),
73    (14, "and_suite", tests::bitwise::and_suite),
74    (15, "or_suite", tests::bitwise::or_suite),
75    (16, "xor_suite", tests::bitwise::xor_suite),
76    (17, "clz_suite", tests::bitwise::clz_suite),
77    (18, "ctz_suite", tests::bitwise::ctz_suite),
78    (19, "set_lt_u_suite", tests::bitwise::set_lt_u_suite),
79    (20, "set_lt_s_suite", tests::bitwise::set_lt_s_suite),
80    (30, "memcpy_test_suite", tests::memory::memcpy_test_suite),
81    (31, "sort_u32_suite", tests::memory::sort_u32_suite),
82    (32, "fib_suite", tests::memory::fib_suite),
83    (40, "blake2b_256_suite", tests::crypto::blake2b_256_suite),
84    (41, "keccak_256_suite", tests::crypto::keccak_256_suite),
85];
86
87// -- Helpers for test functions -----------------------------------------------
88
89/// Read a u64 from LE bytes at offset, advancing the offset.
90pub(crate) fn read_u64(input: &[u8], off: &mut usize) -> u64 {
91    let v = u64::from_le_bytes(input[*off..*off + 8].try_into().unwrap());
92    *off += 8;
93    v
94}
95
96/// Read a u32 from LE bytes at offset, advancing the offset.
97pub(crate) fn read_u32(input: &[u8], off: &mut usize) -> u32 {
98    let v = u32::from_le_bytes(input[*off..*off + 4].try_into().unwrap());
99    *off += 4;
100    v
101}
102
103/// Write a u64 as LE bytes to output at offset, advancing the offset.
104pub(crate) fn write_u64(output: &mut [u8], off: &mut usize, v: u64) {
105    output[*off..*off + 8].copy_from_slice(&v.to_le_bytes());
106    *off += 8;
107}
108
109/// Fold an arbitrary byte slice into a single u64 fingerprint.
110///
111/// Processes bytes in 8-byte LE chunks, XORing each into the
112/// accumulator. The byte length is mixed in to distinguish e.g.
113/// `[]` from `[0]`.
114pub(crate) fn fold_bytes_to_u64(bytes: &[u8]) -> u64 {
115    let mut acc = bytes.len() as u64;
116    let mut chunk = [0u8; 8];
117    let mut i = 0;
118    while i < bytes.len() {
119        let take = core::cmp::min(8, bytes.len() - i);
120        chunk.fill(0);
121        chunk[..take].copy_from_slice(&bytes[i..i + take]);
122        acc ^= u64::from_le_bytes(chunk);
123        i += 8;
124    }
125    acc
126}