aere-research/pq-stark/test_challenger.py
Aere Network 4a0b48588c Initial public release
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.
2026-07-20 01:02:30 +03:00

123 lines
7.8 KiB
Python

#!/usr/bin/env python3
# Conformance + cross-language harness for the Fiat-Shamir DUPLEX CHALLENGER (component (f)) of the PQ
# STARK-verify precompile 0x0AE8. It:
# 1. runs the Python challenger conformance KAT against the real ground truth emitted by the pinned
# p3-challenger / p3-fri 0.4.3-succinct crates (challenger-extractor -> challenger_ground_truth.json);
# 2. asserts Python, Node, and standalone Java produce byte-identical shared vectors (three
# independent implementations of observe / sample / sample_bits / check_witness);
# 3. closes the loop END TO END: derive the FRI betas + query indices FROM THE TRANSCRIPT, then feed
# them into the CONFIRMED FRI verify_query (component (d), fri_verify_reference) and confirm it
# ACCEPTS the real FRI ground-truth proof and REJECTS a tampered derived beta. This is
# transcript -> betas/indices -> FRI verify, the loop the FRI KAT left open.
#
# It writes ../results/kat-results-challenger.json. The top-level 0x0AE8 stays FAIL-CLOSED regardless
# (component (e), the AIR, is un-ported); this harness confirms component (f) only.
import json
import os
import subprocess
import sys
HERE = os.path.dirname(os.path.abspath(__file__))
RESULTS = os.path.join(HERE, "..", "results", "kat-results-challenger.json")
sys.path.insert(0, HERE)
import challenger_reference as C
import fri_verify_reference as FV # component (d), CONFIRMED
passed = 0
failed = 0
notes = []
def check(name, cond):
global passed, failed
if cond:
passed += 1
else:
failed += 1
notes.append("FAIL: " + name)
print(("PASS " if cond else "FAIL ") + name)
# ---- 1) Python conformance KAT ----
p, t, det = C.conformance_kats()
for d in det:
check("py." + d["check"], d["pass"])
# ---- 2) cross-language byte-identity ----
py = C.shared_vectors()
node = json.loads(subprocess.check_output(["node", os.path.join(HERE, "challenger_reference.mjs")]))
java_out = subprocess.check_output(
["java", "-cp", os.path.join(HERE, "out"), "ChallengerSelfTest", "--emit",
os.path.join(HERE, "challenger_ground_truth.json")],
cwd=HERE)
java = json.loads(java_out)
def norm(v):
return json.loads(json.dumps(v))
check("xlang.permZeros.py==node", norm(py["permZeros"]) == norm(node["permZeros"]))
check("xlang.permZeros.py==java", norm(py["permZeros"]) == norm(java["permZeros"]))
check("xlang.duplex.py==node", norm(py["duplex"]) == norm(node["duplex"]))
check("xlang.duplex.py==java", norm(py["duplex"]) == norm(java["duplex"]))
check("xlang.fri.py==node", norm(py["fri"]) == norm(node["fri"]))
check("xlang.fri.py==java", norm(py["fri"]) == norm(java["fri"]))
# ---- 3) end-to-end loop closure: transcript -> betas/indices -> CONFIRMED FRI verify_query ----
with open(os.path.join(HERE, "challenger_ground_truth.json")) as fp:
ch_gt = json.load(fp)
with open(os.path.join(HERE, "fri_ground_truth.json")) as fp:
fri_gt = json.load(fp)
fri_by_name = {c["name"]: c for c in fri_gt["cases"]}
for tc in ch_gt["fri_transcript"]:
name = tc["name"]
betas, indices, pow_accept = C.derive_fri_challenges(
tc["commit_phase_commits"], tc["final_poly"], tc["pow_witness"],
tc["pow_bits"], tc["log_blowup"], tc["num_queries"])
fc = fri_by_name[name]
# Build a FRI case that uses the TRANSCRIPT-DERIVED betas (indices already match fc queries).
check("e2e[%s].derived_indices==fri_gt" % name, indices == [q["index"] for q in fc["queries"]])
synthetic = {
"log_blowup": tc["log_blowup"],
"log_max_height": len(tc["commit_phase_commits"]) + tc["log_blowup"],
"commit_phase_commits": tc["commit_phase_commits"],
"final_poly": fc["final_poly"],
"betas": betas, # <-- derived in-circuit from the transcript
"num_queries": tc["num_queries"],
"queries": fc["queries"], # real openings from the confirmed FRI ground truth
}
# transcript -> derived betas/indices -> CONFIRMED FRI verify_query must ACCEPT the honest proof.
check("e2e[%s].fri_verify_accepts_with_derived_betas" % name, FV.verify_case(synthetic) is True)
# and a tampered derived beta must be REJECTED (soundness of the closed loop).
check("e2e[%s].fri_verify_rejects_tampered_beta" % name, FV.verify_case(synthetic, "beta") is False)
# ---- write results ----
total = passed + failed
result = {
"component": "Fiat-Shamir transcript / Poseidon2 DuplexChallenger (port spec component (f))",
"precompile": "0x0000000000000000000000000000000000000ae8",
"scope": "port spec section 7; the duplex-sponge challenger DuplexChallenger<BabyBear, Perm, WIDTH=16, RATE=8> and the GrindingChallenger check_witness: observe (overwrite-mode absorb), sample (squeeze, pop from the end of the rate buffer), sample_bits (low-bits query-index reduction), check_witness (observe witness then sample_bits==0). Wired behind the still-fail-closed top level.",
"validates": "CONFORMANCE: ground truth emitted by executing the pinned Plonky3 p3-challenger 0.4.3-succinct DuplexChallenger + GrindingChallenger (and the p3-fri verify_shape_and_sample_challenges transcript ORDER). (1) a fully-scripted observe/sample transcript reproduces every sampled base + F_{p^4} ext value, the full 16-lane sponge state, and a check_witness accept/reject table byte-for-byte; (2) the FRI betas + query indices + grinding acceptance are DERIVED from the transcript (commit_phase_commits + final_poly + pow_witness) and match the pinned p3-fri challenger AND the confirmed FRI ground truth; (3) END TO END: those derived betas/indices, fed into the CONFIRMED FRI verify_query (component (d)), accept the real FRI proof and reject a tampered beta. Python/Node/standalone-Java are byte-identical.",
"does_not_validate": "the SP1 recursion-AIR constraint evaluation (component (e)): the reduced openings, DEEP zeta/alpha challenges, quotient consistency, and vk binding. That is un-ported (StarkConstraints.evaluateAtZeta = UNAVAILABLE). A real exported SP1 v6.1.0 proof's transcript (vkey digest, trace/quotient commitments, opened values order) is still [MEASURE].",
"top_level_verifier": "unchanged, still FAIL-CLOSED (returns EMPTY for every input). Even with component (f) confirmed and Challenger.spongePorted flipped true, StarkConstraints.evaluateAtZeta returns UNAVAILABLE (component (e) un-ported), so verify() returns UNAVAILABLE before any query loop and computePrecompile returns EMPTY. ACCEPT is unreachable. DO NOT ACTIVATE.",
"confirmed_config": "DuplexChallenger<BabyBear, Poseidon2<BabyBear,...,16,7>, WIDTH=16, RATE=8>; Witness=BabyBear; Challenge=BinomialExtensionField<BabyBear,4>; sample pops from the END of the rate buffer (Vec::pop); sample_bits masks the LOW `bits` of as_canonical_u64; check_witness = observe(witness) then sample_bits(bits)==0.",
"pinned_conformance_target": "p3-challenger 0.4.3-succinct (duplex_challenger.rs, grinding_challenger.rs), transcript order p3-fri 0.4.3-succinct verifier.rs (verify_shape_and_sample_challenges), sponge = confirmed Poseidon2-BabyBear (component (b)); byte-identical to the repo Cargo.lock. Extractor: pq-stark/challenger-extractor -> pq-stark/challenger_ground_truth.json.",
"cross_language": "Python (challenger_reference.py), Node (challenger_reference.mjs), standalone Java (ChallengerSelfTest.java) emit byte-identical shared vectors.",
"flags": {"Challenger.spongePorted": True, "note": "flipped true: component (f) confirmed. StarkConstraints.evaluateAtZeta stays UNAVAILABLE (component (e)), so the top level stays fail-closed and ACCEPT is unreachable."},
"notes": notes,
"passed": passed,
"failed": failed,
"total": total,
}
os.makedirs(os.path.dirname(RESULTS), exist_ok=True)
with open(RESULTS, "w") as fp:
json.dump(result, fp, indent=1)
print("\nchallenger conformance: PASS=%d FAIL=%d TOTAL=%d" % (passed, failed, total))
print("results -> %s" % os.path.normpath(RESULTS))
sys.exit(0 if failed == 0 else 1)