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.
105 lines
5.9 KiB
Markdown
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.
|
|
```
|