aere-research/pq-stark/export-inner-stark-vector.md
Aere Network 6cb0140fae Republished from a clean root: the compiled artifact is gone from history, and the local line of work joins the sanitized public line
The public history carried kat/__pycache__/mlkem768_reference.cpython-314.pyc,
a compiled Python artifact embedding the operator's absolute local path. Text
secret scanners do not read compiled binaries, which is exactly how it slipped
through, and removing it from the tip would have left it reachable through the
old root commits. So this repository is republished from a single clean root.

This root also carries, from the previously unpublished line of work:
- corrected LICENSE year, LICENSING.md, VERIFY-POLICY.md, and
  CITATIONS-UNRESOLVED.md remeasured 2026-08-11 (101 paths, README aligned)
- O-018: run_consensus_verification.py ran 19 of 29 models and reported PASS;
  it now runs all 29, and computemarket_smt.py gains resolveByTimeout /
  reclaimUnsettled cases plus a negative control
- O-006: the word 'audited' removed from next to Bouncy Castle, twice, after a
  concurrent edit resurrected it
- O-014: prior art named and dated - Algorand's native falcon_verify shipped
  about ten months before AERE's precompiles; the primacy claim is withdrawn
  where it was implied
- bench/ scripts parametrized so they actually run for an outsider (the
  earlier textual sanitization left $STAGING unexpanded inside Python strings)
- AIP-2/AIP-3 errata with measured figures, spec remeasurements at 2026-08-01,
  and the spec-zk-stack retractions (owner is an operational key, not the
  Foundation; 'maximally sound' withdrawn; aggregator V1 deprecated)
The redacted bench-host environment files from the sanitized line are kept
exactly as published; the unredacted local variants are not carried.
2026-08-15 13:52:14 +03:00

105 lines
5.9 KiB
Markdown

# 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<BabyBearPoseidon2>` 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!("{:?}", <the shrink FriConfig>);
}
fn pack_babybear8(_x: &[u32; 8]) -> Vec<u8> { /* 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.
```