Skip to main content

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}