# Exporting a real SP1 v6.1.0 INNER STARK vector (for the PQ verifier KAT corpus) > **SCOPE CORRECTION (2026-07-19 research finding; the premise below is wrong for the pinned SP1).** > This document assumes SP1 6.1.0's inner proof is a BabyBear + Poseidon2 + FRI Plonky3 `ShardProof`. > It is NOT. The pinned SP1 (facade `= "=6.1.0"`, internals `sp1-hypercube 6.3.1`) is a **Hypercube** > release: its inner recursion proof is a **KoalaBear multilinear** proof (BaseFold + Jagged/Stacked > PCS + sumcheck-zerocheck + LogUp-GKR with a septic-curve digest), with no BabyBear FRI `ShardProof` > anywhere. So exporting a "shrink/compress Plonky3 shard proof over BabyBear + FRI" from SP1 6.1.0 is > not possible: what SP1 6.1.0 exports is a Hypercube ShardProof (KoalaBear), which the BabyBear + FRI > verifier skeleton does NOT verify. A real KAT corpus for the current skeleton must come from Aere's > OWN Plonky3 (`zk-circuits/*`) BabyBear + FRI provers instead. Building a real SP1 6.1.0 corpus > requires first retargeting 0x0AE8 to the SP1 Hypercube stack (a separate ~22 to 32 person-week > effort). See `../../docs/AERE-STARK-SP1-RECURSION-AIR-PORT-SPEC-SUMMARY.md` and > `../../docs/AERE-STARK-SP1-RECURSION-AIR-PORT-SPEC.md`. Read the instructions below with that in mind. The PQ STARK-verify precompile (0x0AE8) verifies a BabyBear/Plonky3 **inner** hash-based FRI/STARK proof directly, not a BN254 Groth16 wrap. Per the scope correction above, that BabyBear + FRI target is Aere's own Plonky3 circuits, NOT the pinned SP1 6.1.0 (Hypercube). The steps below were written to export a BabyBear + Poseidon2 + FRI "shrink"/compress shard proof; they apply to a BabyBear + FRI Plonky3 prover, not to the SP1 6.1.0 Hypercube toolchain. This file is instructions + a Rust snippet, NOT a bundled vector. As of 2026-07-18 **no real inner vector is committed** here, because generating one requires running the SP1 prover (heavy; MUST be on a throwaway non-infra box, never on the live infra host or a validator). Treat every "expect ACCEPT" KAT as PENDING until a vector produced by this procedure is checked in. ## Provenance to pin - SP1 `=6.1.0` (matches the repo's existing pins: `rollup-evm-validity/host/Cargo.toml`, `batch-prover-recovered/bin/aere-prover/Cargo.toml`, `sp1-verifier = "=6.1.0"`). - The proof object we want is the one BEFORE the Groth16/PLONK wrap: - `SP1ReduceProof` after `prover.shrink(...)` (config 1, "inner/shrink"), and/or - `SP1ReduceProof` after `prover.wrap_bn254(...)`'s *input* (config 2, "wrap"), i.e. the STARK that Groth16 is about to attest, exported before the wrap. - These are Plonky3 `ShardProof` values. Their `serde`/`bincode` bytes are the `friProof` body the precompile parses (spec doc section 3.2 pins the sub-layout to this exact serialization + version). ## Rust snippet (run on a throwaway prover box) ```rust // Cargo.toml: sp1-sdk = "=6.1.0", sp1-prover = "=6.1.0", bincode = "1.3", serde_json = "1" use sp1_sdk::{ProverClient, SP1Stdin}; fn main() { // A tiny guest ELF is enough for a KAT (e.g. the fibonacci example, or the aere-client guest). let elf = std::fs::read("guest.elf").unwrap(); let client = ProverClient::from_env(); // CPU prover; native-gnark not needed for shrink let (pk, vk) = client.setup(&elf); let mut stdin = SP1Stdin::new(); stdin.write(&/* public input */ 20u32); // 1) core proof -> 2) compress -> 3) shrink. Stop BEFORE groth16/plonk wrap. let core = client.prove(&pk, &stdin).core().run().unwrap(); let compressed = client.prove(&pk, &stdin).compressed().run().unwrap(); // The inner API name varies by SP1 minor; in 6.1.0 the compressed proof carries the // SP1ReduceProof. Serialize the reduce/shrink proof + vk + public values: // vkey digest domain the precompile expects (Poseidon2 digest of the recursion vk): let vkey_digest = vk.hash_babybear(); // [BabyBear; 8] -> 32 bytes big-endian per element pack std::fs::write("sp1_shrink_vkeydigest.bin", pack_babybear8(&vkey_digest)).unwrap(); let public_values = compressed.public_values.to_vec(); std::fs::write("sp1_shrink_publicvalues.bin", &public_values).unwrap(); // The shard/reduce proof, bincode-serialized == the friProof body: let proof_bytes = bincode::serialize(&compressed.proof).unwrap(); std::fs::write("sp1_shrink_proof.bin", &proof_bytes).unwrap(); // Also dump the FriConfig actually used (num_queries, log_blowup, pow_bits) so section 4 of the // spec can pin configId 1 exactly instead of the placeholder values: // println!("{:?}", ); } fn pack_babybear8(_x: &[u32; 8]) -> Vec { /* 4 BE bytes per limb -> 32 bytes */ vec![] } ``` ## Assemble the precompile envelope Concatenate into the v1 wire format the adapter/precompile expect (spec doc section 3): ``` magic("AS1\0") || version(1)=1 || configId(1)=1 || reserved(2)=0x0000 || vkeyDigest(32) = sp1_shrink_vkeydigest.bin || publicValuesLen(4 BE) || publicValues = sp1_shrink_publicvalues.bin || proofLen(4 BE) || friProof = sp1_shrink_proof.bin ``` Name the file `vectors/sp1_shrink_valid_01.bin`. Produce the negatives by: - `sp1_shrink_tampered_01.bin`: flip one byte inside a query-opening leaf of the proof (must REJECT). - `sp1_shrink_wrongpub_01.bin`: mutate one byte of `publicValues` (must REJECT - the transcript binds public values, so the sampled challenges diverge). ## What these vectors gate Once the crypto core is ported (Poseidon2-BabyBear + FRI folding + SP1 recursion AIR), the KAT corpus is the section-4 conformance gate in `docs/PQ-STARK-VERIFIER-ACTIVATION-2026-07-18.md`: the precompile must ACCEPT every `*_valid_*` vector and REJECT every `*_tampered_*` / `*_wrongpub_*` vector, plus a cross-check that the SAME public values verified through the live BN254 gateway (0x9ca479...) and through 0x0AE8 agree on accept/reject. Until a real vector exists and the core is ported, DO NOT claim the precompile verifies anything. ```