bench_mini_verifier/lib.rs
1//! Plonky3 STARK-verifier-shaped workload — Fiat-Shamir transcript +
2//! FRI-fold linear combinations + AIR constraint evaluation, all in
3//! Goldilocks field with WIDTH=8 Poseidon2 hash. Mirrors what
4//! `p3_uni_stark::verify` does in its hot loop without dragging in
5//! the full uni-stark machinery.
6//!
7//! The Goldilocks + Poseidon2 implementation lives in
8//! `bench-goldilocks-poseidon2` and is hand-written rather than
9//! pulled from `p3-goldilocks` / `p3-poseidon2` because Plonky3's
10//! crates transitively depend on `tracing`, which requires
11//! atomic-pointer-width support — incompatible with javm's
12//! `max-atomic-width: 0` target. Bit-exact with
13//! `default_goldilocks_poseidon2_8` (same constants, same MDS, same
14//! S-box).
15//!
16//! ## Workload proportions per `mini_verifier_bench()` call
17//!
18//! - 16 transcript Poseidon2 permutations (Fiat-Shamir derives)
19//! - 32 FRI queries × 12 fold steps = 384 permutations + 384 linear combs
20//! - 32 constraint-eval chains × 50 mul-add ops = 1600 Goldilocks ops
21//!
22//! Total ≈ 400 permutations + ~2400 Goldilocks field ops per call —
23//! representative of one moderate STARK verify.
24
25#![cfg_attr(target_os = "none", no_std)]
26
27use subsoil as _;
28
29use gp::{add, canonical, mul, permute, sub, ONE, WIDTH, ZERO};
30
31const TRANSCRIPT_PERMS: usize = 16;
32const FRI_QUERIES: usize = 32;
33const FRI_FOLDS_PER_QUERY: usize = 12;
34const CONSTRAINT_EVALS: usize = 32;
35const CONSTRAINT_OPS_PER_EVAL: usize = 50;
36
37/// One STARK-verifier-shaped pass. Returns low 32 bits of the
38/// accumulator for cross-VM correctness checking.
39pub fn mini_verifier_bench() -> u32 {
40 // Deterministic seed — every cell distinct so all-zero collisions
41 // don't hide bugs.
42 let mut state: [u64; WIDTH] = [
43 0xdeadbeef_00000000,
44 0xdeadbeef_00000001,
45 0xdeadbeef_00000002,
46 0xdeadbeef_00000003,
47 0xdeadbeef_00000004,
48 0xdeadbeef_00000005,
49 0xdeadbeef_00000006,
50 0xdeadbeef_00000007,
51 ];
52
53 let mut i = 0u64;
54 while i < TRANSCRIPT_PERMS as u64 {
55 let slot = (i as usize) % WIDTH;
56 state[slot] = add(state[slot], i.wrapping_mul(0x9E3779B97F4A7C15));
57 permute(&mut state);
58 i += 1;
59 }
60
61 let mut accum = ZERO;
62 let mut q = 0;
63 while q < FRI_QUERIES {
64 let mut left = state[0];
65 let mut right = state[1];
66 let mut sibling = state[2];
67 let mut fold = 0;
68 while fold < FRI_FOLDS_PER_QUERY {
69 permute(&mut state);
70 let challenge = state[(q + fold) % WIDTH];
71 let one_minus_c = sub(ONE, challenge);
72 left = add(mul(one_minus_c, left), mul(challenge, right));
73 right = sibling;
74 sibling = state[3];
75 fold += 1;
76 }
77 accum = add(accum, left);
78 q += 1;
79 }
80
81 let coeff_a = state[3];
82 let coeff_b = state[5];
83 let mut k = 0;
84 while k < CONSTRAINT_EVALS {
85 let mut x = state[k % WIDTH];
86 let mut j = 0;
87 while j < CONSTRAINT_OPS_PER_EVAL {
88 x = add(mul(x, coeff_a), coeff_b);
89 j += 1;
90 }
91 accum = add(accum, x);
92 k += 1;
93 }
94
95 (canonical(accum) & 0xFFFF_FFFF) as u32
96}