mirror of
https://github.com/luxfi/p3q.git
synced 2026-11-02 14:41:48 +00:00
Replace the `not yet audit-gated` stub in verify_sha3/verify_keccak with
a real Fiat-Shamir-driven generic STARK / FRI low-degree verifier.
Wraps cleanly: AIR-specific Z-Chain circuit verifiers compose this
kernel and add their boundary/transition checks on top.
What lands
* wire.rs - canonical wire format v1 (10-byte header + public input +
trace root + FRI layer roots + final polynomial + per-query
openings). Every length/range/well-formedness check fires BEFORE
any crypto. Headers reject non-binary arities, out-of-range
log_blowup/log_degree_bound/num_queries/log_final_poly, reserved
bits, wrong version.
* transcript.rs - typed wrapper around p3q-challenger. Profile-keyed
customisation string (LUX-P3Q-V1-SHA3 / LUX-P3Q-V1-KECCAK) plus
per-observation tag prefixes (HEADER, PUBLIC-INPUT, TRACE-COMMIT,
FRI-COMMIT, FRI-FINAL, FRI-QUERY). Field-element squeezing uses
cSHAKE256 rejection sampling with a finite 32-iter cap.
* domain.rs - LDE-domain generator from Goldilocks 2-adicity (g=7),
binary FRI fold formula with two-test correctness proof, Horner
evaluation.
* verify.rs - the kernel: phase-1 transcript replay capturing per-
round fold challenges, phase-2 query-index re-derivation with echo
check, per-query trace+layer Merkle authentications, colinearity
check against folded carry, final-poly evaluation comparison. Every
failure mode maps to a precise P3qError variant - no unwrap, no
panic.
* prover.rs (feature `prover`) - minimal toy prover that produces
golden vectors. Used by in-crate roundtrip tests only; not in the
audited verifier compile graph.
* p3q-field: pow() and two_adic_generator() (g=7 quadratic non-
residue gives primitive 2^k-th roots up to k=32).
Verification properties tested
* sha3+keccak golden vectors verify (toy AIR: degree-16 poly, blowup
8, 16 queries, final poly of size 2)
* tampering rejected: profile byte swap, trace root bit-flip, FRI
layer root bit-flip, final-poly bit-flip, public-input bit-flip,
echoed query-index bit-flip (precise Transcript variant), arbitrary
per-query byte flip
* parsing rejects: short header, wrong version, non-binary arity,
log_final_poly >= log_degree_bound, reserved nonzero, truncated
body, trailing bytes, oversized Merkle path length
* never panics on random first-byte fuzz
* profiles do not cross-verify (cSHAKE customisation isolates them)
Golden vectors
vectors/p3q_v1_{sha3,keccak}_golden.{proof,public_input}.bin written
by the `write_golden_vectors` ignored test. `persisted_golden_vectors
_verify` reads them back and confirms acceptance - any wire-format
change forces a regen.
Verify wall-clock
Toy AIR (~30 KB proof): ~1 ms per verify in release on M2 Pro.
Workspace state
cargo fmt --check, cargo clippy --all-targets -- -D warnings, and
cargo test all clean. 87 tests passing (1 ignored vector writer).
scripts/lint_forbidden.sh clean (no bn254/kzg/groth16/blake3/poseidon
imports).
Out of scope for this commit
* AIR-specific Z-Chain circuits (boundary + transition constraints)
- p3q-zchain stays scaffolded, lands in v0.2.x
* recursive STARK composition - p3q-recursion stays scaffolded
* coset FRI / bit-reverse leaf permutation - dual-auth-path v1 wire
is intentionally simple; v2 can collapse to single path under
bit-rev
53 lines
1.9 KiB
Rust
53 lines
1.9 KiB
Rust
// =============================================================================
|
|
// build.rs - cbindgen-driven C header generation for p3q-c-abi
|
|
// =============================================================================
|
|
//
|
|
// Generates `include/lux/p3q.h` next to the crate so consumers can copy
|
|
// it into their own `$LUXCPP_PREFIX/include/lux/` (or homebrew's
|
|
// `/opt/homebrew/include/lux/`) alongside `lux/crypto/metal_mlkem.h`.
|
|
//
|
|
// We do NOT write into `$OUT_DIR` because the header is consumed by
|
|
// non-Rust callers that don't know about cargo's output convention.
|
|
// The header is regenerated on every build so it stays in sync with
|
|
// `src/lib.rs` — never edit it by hand.
|
|
// =============================================================================
|
|
|
|
use std::env;
|
|
use std::path::PathBuf;
|
|
|
|
fn main() {
|
|
// Re-run if src or cbindgen config changes.
|
|
println!("cargo:rerun-if-changed=src/lib.rs");
|
|
println!("cargo:rerun-if-changed=cbindgen.toml");
|
|
|
|
let crate_dir = env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR not set");
|
|
let out = PathBuf::from(&crate_dir)
|
|
.join("include")
|
|
.join("lux")
|
|
.join("p3q.h");
|
|
|
|
if let Err(e) = std::fs::create_dir_all(out.parent().unwrap()) {
|
|
eprintln!("p3q-c-abi: could not create include dir: {e}");
|
|
return;
|
|
}
|
|
|
|
let config = cbindgen::Config::from_file(PathBuf::from(&crate_dir).join("cbindgen.toml"))
|
|
.unwrap_or_default();
|
|
|
|
match cbindgen::Builder::new()
|
|
.with_crate(&crate_dir)
|
|
.with_config(config)
|
|
.generate()
|
|
{
|
|
Ok(bindings) => {
|
|
bindings.write_to_file(&out);
|
|
}
|
|
Err(e) => {
|
|
// Don't fail the build on header-generation issues — the
|
|
// .so / .dylib is the source of truth. Print so it's
|
|
// visible in cargo's output.
|
|
println!("cargo:warning=cbindgen failed: {e}");
|
|
}
|
|
}
|
|
}
|