Aere Network public source. Everything here can be checked against the live chain (chain id 2800, https://rpc.aere.network). Scope note, stated up front rather than buried: consensus on chain 2800 is classical secp256k1 ECDSA QBFT. The post-quantum work in this repository is at the signature, precompile, account and transport layers. Nothing here makes the consensus post-quantum, and no document in it should be read as claiming so.
97 lines
6.3 KiB
Markdown
97 lines
6.3 KiB
Markdown
# Component (f): Fiat-Shamir transcript / Poseidon2 duplex challenger (0x0AE8) - CONFORMANCE-CONFIRMED
|
|
|
|
> **Scope caveat (2026-07-19 finding).** This transcript is conformance-confirmed against Plonky3
|
|
> `p3-challenger` / `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: BaseFold + Jagged +
|
|
> sumcheck-zerocheck + LogUp-GKR), so FRI and DEEP-ALI do not apply to it and this skeleton does not
|
|
> verify SP1 6.1.0 proofs. (The duplex-challenger SHAPE carries over to SP1's Hypercube stack, but
|
|
> over the KoalaBear field with KoalaBear constants and a different observe/sample order.) 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. Precompile: Aere PQ STARK verifier at 0x0AE8. Scope: port-spec component (f).
|
|
|
|
## Result headline
|
|
|
|
- Transcript conformance KAT: **PASSED** (PASS=44 FAIL=0 across Python + Node + standalone Java;
|
|
standalone `ChallengerSelfTest` PASS=31 including a driver fail-closed gate model).
|
|
- It **closed the FRI betas/indices loop** that component (d) left open: transcript -> derived
|
|
betas/indices -> confirmed `Fri.verifyQuery` ACCEPTs the real FRI ground-truth proof and REJECTs a
|
|
tampered beta.
|
|
- `Challenger.spongePorted` flipped **true**. Top level stays **FAIL-CLOSED**: `StarkConstraints.
|
|
evaluateAtZeta` still returns UNAVAILABLE (component (e), the AIR, un-ported), gating ACCEPT before
|
|
the query loop and before the single `return ACCEPT`. `computePrecompile` returns EMPTY for every
|
|
input; ACCEPT is unreachable by construction. DO NOT ACTIVATE.
|
|
|
|
## What was confirmed from pinned source (p3-challenger / p3-fri 0.4.3-succinct)
|
|
|
|
`DuplexChallenger<BabyBear, Perm, WIDTH=16, RATE=8>` (duplex_challenger.rs) + `GrindingChallenger`
|
|
(grinding_challenger.rs), transcript order from p3-fri verifier.rs `verify_shape_and_sample_challenges`:
|
|
|
|
- `observe(felt)`: clear output buffer, push to input buffer, duplex when input buffer reaches RATE=8.
|
|
- `duplexing`: overwrite the first `input_buffer.len()` (<=RATE) of 16 lanes, permute (confirmed
|
|
Poseidon2), set output buffer = lanes [0..8).
|
|
- `sample`: duplex if inputs buffered OR outputs empty, then `Vec::pop` (LIFO, from the END). An
|
|
F_{p^4} sample = 4 sequential base pops [c0,c1,c2,c3] (`sample_ext_element` = `sample_vec(4)` then
|
|
`from_base_slice`).
|
|
- `sample_bits(bits)`: LOW `bits` of `as_canonical_u64` (confirmed LSB).
|
|
- `check_witness(pow_bits, witness)`: `observe(witness)` then `sample_bits(pow_bits) == 0`.
|
|
- FRI transcript order: per commit -> observe 8-elem digest, sample beta (F_{p^4}); then observe
|
|
final_poly (4 coords); then check_witness(pow_bits, pow_witness); then num_queries x
|
|
sample_bits(log_max_height). log_max_height = commits.len() + log_blowup.
|
|
|
|
Montgomery/serialization: all arithmetic canonical (the confirmed Poseidon2 reference already carries
|
|
the internal-layer R^-1 factor); the extractor emits `as_canonical_u32` and `as_base_slice` coords, and
|
|
the references reproduce them exactly.
|
|
|
|
## Ground truth (executed the pinned crates)
|
|
|
|
`pqc-fork/pq-stark/challenger-extractor/` (Cargo.toml + copied fri-extractor Cargo.lock, byte-identical
|
|
pins) compiled and ran (target dir in scratch, no repo pollution, live infra untouched). It emits
|
|
`challenger_ground_truth.json` with two blocks:
|
|
1. `duplex_kat`: a fully-scripted observe/sample transcript -> base samples, F_{p^4} ext samples,
|
|
sample_bits, the full 16-lane sponge state at a checkpoint, and a check_witness accept/reject table
|
|
(real grinding witness accepts, fixed non-witnesses reject).
|
|
2. `fri_transcript`: the SAME three FRI cases as `fri_ground_truth.json`, rebuilt (same seeds) so the
|
|
commit_phase_commits / final_poly / pow_witness / betas / query_indices match; the references derive
|
|
betas+indices+PoW-accept from (commits, final_poly, pow_witness) and match BOTH the extractor AND
|
|
fri_ground_truth.json.
|
|
|
|
The references reproduce every value byte-for-byte (all 3 languages identical), and the derived
|
|
betas/indices, fed to the confirmed `Fri.verifyQuery`, accept the real proof and reject a tampered beta.
|
|
|
|
## Files added/changed
|
|
|
|
Added: `challenger_reference.py`, `challenger_reference.mjs`, `ChallengerSelfTest.java`,
|
|
`test_challenger.py`, `challenger-extractor/{Cargo.toml,Cargo.lock,src/main.rs}`,
|
|
`challenger_ground_truth.json`, `../results/kat-results-challenger.json`.
|
|
|
|
Changed:
|
|
- `pqc-fork/precompiles/Sp1StarkVerifierPrecompiledContract.java`: `Challenger` class rewritten as the
|
|
confirmed duplex sponge over `Poseidon2Bb.permute` (observeField/sampleBaseField/sampleExtField/
|
|
sampleBits/checkWitnessField + byte-facing driver adapters); `spongePorted = true`; driver stage-4
|
|
comment clarifies the AIR gate; stale "spongePorted=false" docs updated. `StarkConstraints.
|
|
evaluateAtZeta` UNCHANGED (still returns UNAVAILABLE at line ~1077).
|
|
- `docs/AERE-STARK-VERIFIER-PORT-SPEC.md`: status header, section 5 note, section 7 rewritten
|
|
(IMPLEMENTED + CONFORMANCE-CONFIRMED), section 9 table row (f), section 10 files.
|
|
- `pqc-fork/pq-stark/README.md`: HONEST STATUS, sections (b)/(c)/(d) closings, new section (f), layout.
|
|
- `pqc-fork/pq-stark/test_fri_verify.py`: descriptive notes updated (f now confirmed; loop closed).
|
|
|
|
## Fail-closed proof
|
|
|
|
verify() control flow: ACCEPT requires stage 3 (PoW) AND stage 4 (AIR) AND stage 5 (queries) all pass.
|
|
Stage 4 = `StarkConstraints.evaluateAtZeta` ALWAYS returns UNAVAILABLE (component (e) un-ported) and is
|
|
checked (line 196) BEFORE the query loop and BEFORE the single `return ACCEPT` (line 228). So verify()
|
|
returns UNAVAILABLE for every input reaching stage 4; computePrecompile returns EMPTY. Flipping
|
|
spongePorted cannot create an ACCEPT path. Corroborated at runtime by `ChallengerSelfTest.gateModel`
|
|
(driver.gate.ACCEPT_unreachable_for_every_input PASS). The precompile is not compiled here (it is
|
|
applied via patch to a Besu build, no local Besu jar); the challenger algorithm is verified by the
|
|
byte-identical standalone `ChallengerSelfTest`.
|
|
|
|
## Remaining
|
|
|
|
Component (e), the SP1 recursion AIR (StarkConstraints, ~3-4 person-weeks, HARDEST), is the one
|
|
remaining un-ported crypto-core component. Real exported SP1 v6.1.0 transcript order is [MEASURE].
|
|
On-chain ZK verifiers remain classical BN254 until all 6 components + integration + a real SP1-proof
|
|
end-to-end KAT + audit + founder activation.
|