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