# Component (d) FRI verifier for Aere STARK-verify port (0x0AE8): summary > **Scope caveat (2026-07-19 finding).** This FRI verifier is conformance-confirmed against Plonky3 > `p3-fri` `0.4.3-succinct` (Aere's OWN BabyBear + FRI STARK stack), NOT against SP1 6.1.0. SP1 6.1.0 > is a **Hypercube** release (KoalaBear multilinear), and its inner recursion proof uses **BaseFold, > not FRI**, so this FRI low-degree test (one of the two largest confirmed pieces) does NOT apply to > SP1 6.1.0 at all and this work does not verify SP1 6.1.0 proofs. Replacing the SP1 BN254 Groth16 > wrap is a separate ~22 to 32 person-week retarget. See `AERE-STARK-SP1-RECURSION-AIR-PORT-SPEC-SUMMARY.md`. Date: 2026-07-19. Outcome: the FRI FOLD + OPENING relation (verify_query, the low-degree test) is IMPLEMENTED and CONFORMANCE-CONFIRMED against the pinned Plonky3 p3-fri 0.4.3-succinct via a real KAT (accept genuine + reject tampered). The top-level precompile 0x0AE8 STAYS FAIL-CLOSED. ## 1. Confirmed FRI config (traced from pinned p3-fri 0.4.3-succinct source) Read from the cargo cache of `p3-fri` (checksum 5cbc4965..., matches repo Cargo.lock): - Folding arity 2 (hard-coded): sibling = index ^ 1, index_pair = index >> 1 (verifier.rs verify_query). - FriConfig fields: { log_blowup, num_queries, proof_of_work_bits, mmcs } (config.rs). - log_max_height = commit_phase_commits.len() + log_blowup; num_fold_rounds = log_max_height - log_blowup. - Final polynomial is a SINGLE F_{p^4} CONSTANT (final_poly: F); verify_challenges checks folded == final for every query. (log_final_poly_len = 0 in this pinned version.) - Commit-phase MMCS = ExtensionMmcs, ValMmcs> over the CONFIRMED FieldMerkleTreeMmcs (component (c)). A leaf is a PAIR of F_{p^4} evals flattened to 8 BabyBear coords (ExtensionMmcs::verify_batch, as_base_slice per element), a width-8 row of height 2^log_folded_height. - Grinding (proof_of_work_bits / check_witness) is in the transcript phase (verify_shape_and_sample_challenges), i.e. component (f), NOT part of the per-query fold+opening relation. - Fold: x = two_adic_generator(log_max_height)^reverse_bits_len(index, log_max_height) in F_{p^4}; sibling point = x * two_adic_generator(1) = -x; folded = e0 + (beta - x0)*(e1 - e0)/(x1 - x0); plus reduced_openings[log_folded_height+1] injected before each fold. ## 2. What I implemented - Ground-truth extractor `pqc-fork/pq-stark/fri-extractor/` (Rust, depends on pinned p3-fri/p3-challenger/ p3-dft/p3-commit/p3-merkle-tree/p3-poseidon2/... =0.4.3-succinct; lockfile checksums matched). Builds the exact SP1 inner FRI config with the seed_from_u64(1) Poseidon2, constructs genuinely low-degree codewords, runs p3_fri::prover::prove to emit real FriProofs, and runs p3_fri::verifier::{verify_shape_and_sample_ challenges, verify_challenges} to confirm the LIBRARY accepts. Emits fri_ground_truth.json (roots, betas, query indices, reduced openings, per-query per-layer sibling values + MMCS proofs, final poly, all canonical u32) plus perm_zeros + two_adic_generators sanity anchors. 3 cases (blowup 1/2, height 6/7/8, 4/5/6 queries). - FRI verifier reference (verify_query / verify_challenges) in 3 languages, reusing the CONFIRMED sub-components (F_{p^4} = component (a), MMCS verifyBatch = component (c), Poseidon2 = component (b)): `fri_verify_reference.py`, `fri_verify_reference.mjs`, standalone `FriVerifySelfTest.java`. - Wired into the fail-closed Java precompile: `Fri.verifyQuery` (+ `Fri.reverseBitsLen`, `Fri.foldRelationConfirmed = true`) in Sp1StarkVerifierPrecompiledContract.java, using the existing BabyBearExt4 / Mmcs.verifyBatch / BabyBear.twoAdicGenerator. `Fri.checkQuery` kept GATED (returns UNAVAILABLE). - Harness `test_fri_verify.py` -> writes `../results/kat-results-fri-verify.json`. Spec `spec-fri-babybear.md`. Updated port spec + pq-stark README. ## 3. Conformance KAT result: PASSED (accept + reject) - `python test_fri_verify.py` -> PASS=55 FAIL=0: sanity (perm_zeros == confirmed Poseidon2 zeros; 28 two-adic generators == component (a)); 3 cases each ACCEPT the genuine p3-fri proof and REJECT four tampers (corrupted opened value, corrupted MMCS sibling digest, wrong beta/fold, non-matching final poly); Python + Node + Java produce BYTE-IDENTICAL per-query folded values. - `java FriVerifySelfTest fri_ground_truth.json` -> PASS=44 FAIL=0. - The PRECOMPILE's own `Sp1StarkVerifierPrecompiledContract.Fri.verifyQuery`, driven directly against the ground truth, reproduces accept + reject for all 3 cases (15/15). So the wired code, not just parallel references, is conformant (no "two wrong copies agree"). Closed by this KAT (were [VERIFY]): arity-2 fold; num_fold_rounds = log_max_height - log_blowup + constant final poly; reverse_bits_len coset mapping; two_adic_generator(bits) == Plonky3's; F_{p^4} non-residue W = 11 == Plonky3's. ## 4. Top level stays FAIL-CLOSED (verified) - Challenger.spongePorted = false (component (f) un-ported) and StarkConstraints.evaluateAtZeta returns UNAVAILABLE (component (e) un-ported): verify() returns UNAVAILABLE at stage 4, never reaches ACCEPT. - computePrecompile returns VERIFIED only on ACCEPT, else Bytes.EMPTY -> EMPTY for every input. - The FRI fold+opening takes betas/indices/reduced-openings as INPUTS; deriving them trustlessly is component (f) (the duplex-sponge challenger), so Fri.checkQuery stays gated (UNAVAILABLE). WireReader also does not parse the commit-phase roots/openings. DO NOT ACTIVATE banner intact. Precompile compiles clean (Besu stubs, javac exit 0). ## 5. Scope boundary (unchanged) On-chain ZK verifiers on Aere remain classical BN254 Groth16 until the whole verifier is real, audited, and founder-activated. This is one more component of a multi-week port. Rust compiled only in a scratch/temp dir; no live infra touched.