Public permissionless launch policy: enable basically every precompile for
builder convenience, ESPECIALLY wallet-curve VERIFY so users sign natively on
Lux from other chains (ed25519=Solana, sr25519=Polkadot, secp256r1=WebAuthn,
secp256k1/ecrecover=Ethereum). Disable ONLY actual security risks. Lux's own
consensus and identity stay PQ (quasar/p3q) — enforced in the consensus
layer, never by refusing an EVM verifier a dapp asked for.
Two layers, decomplected:
- builder EVM precompile surface : enable-all-verify (this change)
- chain consensus / finality : PQ-strict (consensus module, untouched)
Removed the RefuseUnderStrictPQ gate from 17 verify-only / key-safe custom
precompiles: ed25519, sr25519, secp256r1, bls12381 (EIP-2537, x7 ops),
kzg4844 (EIP-4844), blake3, poseidon, pedersen, babyjubjub, pasta, ring,
vrf, hpke, curve25519, x25519, cggmp21, frost, and the classical SNARK
verifiers in zk (Groth16/PLONK/Halo2/KZG/IPA/range/batch/commitment).
zk fflonk (0x03) stays DISABLED — but on its OWN forge-bug mechanism
(ErrFflonkDisabled, returned at dispatch), NOT strict-PQ: verifyFflonk has a
nil-vk soundness hole that forges any statement. Security disable, fully
PQ-independent.
The RefuseUnderStrictPQ helper + ErrClassicalForbiddenInPQ + StrictPQReporter
had zero remaining code callers (evm uses a local structural interface) —
deleted contract/strict_pq.go and its test. Rewrote zk's gate test to assert
the new policy (classical ops enabled, fflonk disabled). Removed the now-
orphaned isPedersenCommitment; fixed stale comments referencing deleted symbols.
Build: full module green. Tests: contract + zk + all 17 edited packages pass.
NOTE: the STANDARD eth precompiles (ecrecover, p256Verify, sha256, ripemd160,
blake2f, bls12381, kzg) are still refused by LuxStrictPQ() in the evm plugin —
a follow-up commit flips that to Permissive so ecrecover (every Ethereum dapp)
works at launch.
Under the enable-everything precompile policy these verify-only precompiles are
no longer refused under strict-PQ, so any divergence between the luxfi/accel
GPU/SIMD path and the pure-CPU path is a LIVE consensus split: GPU-equipped and
CPU-only validators would return different verify verdicts on the same input and
the chain forks. Several accel paths were also outright wrong-primitive forgery
surfaces.
LAW applied: the GPU verdict must be byte-identical to the CPU verdict on every
input, or the accel path is a consensus hazard and is removed. CPU (pure-Go,
CGO_ENABLED=0) is now the single source of truth for every on-chain verdict.
None of these GPU kernels can be proven byte-identical here, and several are
provably NOT, so every verdict-trusting accel branch is deleted and routed to
the canonical CPU verifier. Determinism over speed.
Active forgery/wrong-output in the DEFAULT build (accelcrypto CPU fallback
leaked non-CPU semantics, err==nil):
- sr25519: verified via the accel Ed25519 kernel -> a valid Ed25519 signature
was ACCEPTED as a valid sr25519/Schnorrkel signature (wrong-curve forgery).
- blake3: accelcrypto.Hash(HashBlake3) returns BLAKE2b, not BLAKE3; the "real
blake3" fallback was dead code. OpHash256 + MerkleRoot emitted BLAKE2b.
Latent splits (reachable only under -tags accel + a GPU backend):
- frost: FROST-Schnorr verified through the SigECDSA kernel (wrong primitive +
32B x-only key mis-framed as a 33B ECDSA key).
- cggmp21: SigECDSA kernel truncated the 65B key to 33B and skipped the CPU
path's recover-and-compare binding (ignored the v byte).
- ed25519: GPU Ed25519 kernel not proven byte-identical to the canonical
verifier on cofactor / non-canonical-S / small-order edge cases.
- secp256r1: P-256 inputs sent to the fixed-curve secp256k1 ECDSA kernel.
- mldsa: GPU verified with EMPTY context; precompile binds the FIPS-204 ctx
"lux-evm-precompile-mldsa-v1" -> different mu, different verdict.
- bls12381 / zk(groth16): GPU MSM result trusted without re-verification and
with no curve binding carried to the kernel.
- kzg4844: GPU commitment used the backend's OWN embedded trusted setup (empty
SRS) instead of the canonical EIP-4844 SRS -> breaks KZG binding.
- ai_mining (BatchVerifyMLDSA/BatchAccumulator): accel batch kernel truncates
messages to 32 bytes and is mode-fixed -> diverges from VerifyMLDSA.
- fhe: GPU computed BFV arithmetic while CPU computes TFHE (different scheme,
incompatible ciphertext, nil relin key) -> corrupt + divergent ciphertext.
Cleanups (no split, vestigial): removed dead mlkem encapsulateGPU; removed the
hpke GPU pre-warm that discarded its result and returned the CPU output anyway.
slhdsa was already CPU-only (prior identical fix).
Tests (differential / forgery proofs, all green CGO_ENABLED=0 and CGO -race):
- sr25519: a valid Ed25519 sig is now REJECTED; real sr25519 still verifies.
- blake3: OpHash256/MerkleRoot output == BLAKE3 and != BLAKE2b.
- cggmp21: flipped recovery byte is rejected (recover-compare authoritative).
- frost: a valid ECDSA sig is rejected as FROST-Schnorr.
- ed25519: verdict matches the canonical ed25519.Verify oracle over 64x4 vectors.
- mldsa: empty-ctx sig (the accel framing) rejected; precompileCtx sig accepted.
Net -593 LOC. No consensus code touched.
* precompile: pulsar threshold ML-DSA at LP-4200 0x012204
Adds the Pulsar precompile slot for threshold ML-DSA verification.
Pulsar's wire format is byte-equal to FIPS 204 ML-DSA (Class N1
manifesto), so the verification op dispatches to luxfi/crypto/mldsa
under a Pulsar-specific domain-separation context.
- Address: 0x0000000000000000000000000000000000012204 (LP-4200 unified
PQCrypto block; companion to ML-KEM 0x012201, ML-DSA 0x012202,
SLH-DSA 0x012203).
- Three modes: Pulsar-44 / Pulsar-65 / Pulsar-87 mirroring ML-DSA
Categories 2 / 3 / 5. Production target is Pulsar-65.
- Gas: per-mode base + 10/byte (matches ML-DSA precompile tier).
- Context: lux-evm-precompile-pulsar-v1 to keep on-chain transcripts
unambiguous vs. single-party ML-DSA at 0x012202.
Tests: 5 passing (address pin, gas table, round-trip across 3 modes,
tampered-signature rejection, invalid-mode rejection). GOWORK=off
go test -count=1 ./pulsar/... is green.
Imports github.com/luxfi/pulsar-m (pre-rename); will be retargeted to
github.com/luxfi/pulsar when the cross-repo move lands.
* precompile/LLM.md: mark Pulsar 0x012204 as IMPLEMENTED
Pulsar precompile now lives in precompile/pulsar/ and dispatches to
luxfi/crypto/mldsa under a Pulsar-specific context. Update the
LP-4200 canonical note to reflect deployment status alongside the
other three PQCrypto slots.
* precompile/zk: rip classical pairing-based opcodes (PQ-only)
Shor breaks Groth16/PLONK/fflonk/Halo2/KZG/IPA/Bulletproofs.
The 0x0900 EVM precompile now exposes only the PQ-safe ops:
hash-based nullifiers (0x21) and Merkle commitment inclusion (0x22).
PQ STARK verification is dispatched via the Z-Chain envelope path
(consensus/protocol/zchain/verify.go), not from this precompile.
* precompile/kzg4844: retire BLS12-381 KZG blob-opening precompile
KZG point-evaluation verifies via the BLS12-381 pairing
e(C - [y]G1, G2) == e(W, [tau - z]G2). The discrete-log assumption
that anchors BLS12-381's security is broken by Shor's algorithm, so
EIP-4844-style blob commitments give zero soundness against a future
quantum adversary. The Lux EVM is PQ-only.
Strip the implementation (gokzg4844, accel GPU MSM, all CPU opcodes).
The module still registers the address 0xB002 so the precompile
registry numbering stays stable, but every call to the precompile
returns the new contract.ErrClassicalForbiddenInPQ sentinel. The
minimum 21000 gas is deducted on every call to prevent the retired
address being used as a zero-cost probe.
Blob availability on the Lux EVM is now provided by hash-based
commitments (Blake3 / Poseidon2) and STARK-validated state roots.
* precompile/zk: strip Go-level classical pairing SNARK verifiers
The Run() path of the 0x0900 ZK precompile only exposes hash-based
nullifier checks and Merkle commitment inclusion, both PQ-safe. The
remaining Go-level methods on ZKVerifier (VerifyGroth16, VerifyPlonk,
VerifyFflonk, VerifyKZG, VerifyRangeProof, VerifyRollupBatch and
their helpers) all dispatched through a BN254 pairing check or a
BLS12-381 KZG opening, both broken by Shor's algorithm.
Strip those methods, their bn256 / kzg4844 / accel-MSM dependencies,
and the rollup-batch types (RollupBatch, RollupConfig, StateTransition,
KZGSetup, BN254P) that only existed to feed the pairing verifier.
Update verifier_test.go to cover only the PQ-safe surface that
remains: NewZKVerifier, RegisterVerifyingKey (Go-side bookkeeping),
nullifier check/spend, confidential pool create/add-commitment,
hash-based Merkle inclusion.
PQ STARK verification continues to be handled by the P3Q backend on
the Z-chain envelope path (consensus/protocol/zchain/verify.go); it
was never wired through this Go struct.
* precompile/babyjubjub: retire BN254-companion twisted Edwards curve
Baby Jubjub is the ZK-friendly twisted Edwards curve aligned to BN254's
scalar field (used by circom / iden3 / Polygon zkEVM / Semaphore /
RLN / EdDSA-on-BN254). Its security reduces to discrete-log on a curve
of BN254 scalar order; Shor breaks that in poly time. Every consumer
of Baby Jubjub is a classical pairing-based SNARK gadget with no PQ
analogue, so the Lux EVM retires the precompile rather than offer a
quantum-broken primitive.
Strip the gnark-crypto twisted-edwards implementation. The module
still registers address 0x0500..0007 so the precompile registry
numbering stays stable, but every call returns
contract.ErrClassicalForbiddenInPQ with 21000 minimum gas deducted.
* precompile/pedersen: retire BN254 Pedersen commitment precompile
The implementation built homomorphic Pedersen commitments over BN254 G1
(C = v*G + r*H, plus a 32-slot vector commitment). Both binding and
hiding reduce to the discrete-log assumption on BN254, which Shor's
algorithm breaks. The Lux EVM is PQ-only and retires the precompile
rather than offer a quantum-broken commitment.
Strip the gnark-crypto BN254 implementation and the 34 deterministic
generators. The module still registers address 0x0500..0006 so the
precompile registry numbering stays stable, but every call returns
contract.ErrClassicalForbiddenInPQ with the 21000 minimum gas
deducted.
PQ-safe commitment schemes on the Lux EVM use hash-based primitives
(Blake3 0x3202, Poseidon2 0x3201) and STARK gadgets on Z-chain.
* Revert "precompile/pedersen: retire BN254 Pedersen commitment precompile"
This reverts commit 328da64306.
* Revert "precompile/babyjubjub: retire BN254-companion twisted Edwards curve"
This reverts commit b535fd72c2.
* Revert "precompile/zk: strip Go-level classical pairing SNARK verifiers"
This reverts commit c0d6de9bea.
* Revert "precompile/kzg4844: retire BLS12-381 KZG blob-opening precompile"
This reverts commit 41be742d74.
* Revert "precompile/zk: rip classical pairing-based opcodes (PQ-only)"
This reverts commit db61a7e2ba.
* precompile: profile gate + P3Q + classical-primitive integration
Strict-PQ gate (contract.RefuseUnderStrictPQ) at the top of every
classical pairing/DLOG precompile's Run() refuses the call when the
chain's ChainConfig implements StrictPQReporter and reports true at
the current timestamp. Non-Lux chains that integrate Lux precompiles
without wiring StrictPQReporter see the classical primitives execute
normally -- forward perfection on Lux, full compatibility elsewhere.
Wired into kzg4844, zk (classical opcodes only -- Nullifier and
Merkle Commitment stay open), babyjubjub, pedersen, pasta.
P3Q strict-PQ STARK verifier at LP-4200 0x012205. Plonky3 fork with
cSHAKE256 Merkle / Goldilocks field / FRI low-degree test. Verifier
callback registered at node startup; default returns
ErrVerifierNotRegistered. 8 tests pass.
Gate semantics:
RefuseUnderStrictPQ(nil) → nil (test-permissive)
ChainConfig without StrictPQReporter → nil (non-Lux chains)
StrictPQReporter false → nil (pre-strict-PQ activation timestamp)
StrictPQReporter true → ErrClassicalForbiddenInPQ
Composable, orthogonal, complete. One and only one way.
* precompile: gate every classical Run() via contract.RefuseUnderStrictPQ
Decomplect: profile-gating logic stays in one place (contract.RefuseUnderStrictPQ);
each classical precompile calls it once at the top of Run() and otherwise stays in
its lane. Quasar/P3Q/Pulsar/ML-KEM/ML-DSA/SLH-DSA/Corona/X-Wing/FHE precompiles
are post-quantum and skip the gate.
Wired into:
bls12381 (7 EIP-2537 ops: G1/G2 add/mul/MSM + pairing)
ed25519 (signature verify)
cggmp21 (ECDSA threshold)
frost (Schnorr threshold)
ring (LSAG classical schemes; lattice scheme stays open)
vrf (ECVRF EDWARDS25519)
sr25519 (Schnorrkel/Ristretto255)
x25519 (DH)
curve25519 (raw Edwards25519 ops)
hpke (classical KEMs; Kyber/X-Wing hybrid stays open)
blake3 (classical hash)
poseidon (BN254 algebraic hash)
Already gated upstream: kzg4844, zk (classical opcodes), babyjubjub, pedersen, pasta.
secp256r1 uses the non-stateful Run([]byte) interface (EIP-7212 standard).
Gating that precompile requires hooking the geth EVM precompile overrider, not
this package -- left for the LuxPrecompileOverrider follow-up.
Tests pass across all twelve gated dirs plus contract/.
Wire GPU fast paths for ed25519, sr25519, slhdsa, frost, cggmp21,
corona, blake3, fhe via accel/ ops. All use try-GPU-then-CPU
fallback pattern. No gas cost or interface changes.
Total GPU-wired: 12/18 (was 2/18).
docs/ is the Fumadocs site for precompile.lux.network — should not
have been deleted. Package READMEs document input/output formats and
gas costs referenced by the docs site.
Cleanup:
- Delete docs/ (Next.js site in Go repo)
- Delete 11 package-level README.md files (AI-generated filler)
- Remove orphaned SelectorDonate and runDonate stub from dex
- Remove dead applyChainAdjustment (every branch returned input unchanged)
- Remove unused GetChainId (hardcoded to 96369, never called)
- Remove stale TODOs from fhe/module.go and contract/utils.go
Fixes:
- ai/verifyTEESignature: enforce min 64-byte signature length instead
of accepting any non-empty bytes (was a security hole)
- ai/ai_mining_test.go: use 64-byte test signature to match new check
All 28 packages pass.
Changed build tags from `//go:build cgo` to `//go:build gpu` across all
GPU-accelerated precompiles. This ensures builds don't attempt to link
against luxcpp/crypto libraries when not explicitly building with GPU.
Changes:
- ai/gpu: cgo -> gpu build tags
- blake3: gpu tag (already correct)
- ecies: fixed test address expectation (0x9201)
- fhe: added fhe_ops_stub.go for non-GPU builds, gpu tag on test
- frost: gpu tag
- hpke: fixed test address expectation (0x9200)
- kzg4844: fixed test address expectation (0xB002), gpu tag
- mldsa: gpu tag, added gpu_stub.go
- mlkem: gpu tag, added gpu_stub.go
- ring: fixed test address expectation (0x9202)
- corona: fixed address (0x...0B), gpu tag
- slhdsa: gpu tag, added gpu_stub.go
- threshold: gpu tag
- zk: gpu tag
All 23 precompile packages now build and test without GPU libraries.
- Change GPU-accelerated files to use luxgpu tag (opt-in)
- Change pure Go files to use !luxgpu tag (default)
- Ensures both CGO=0 and CGO=1 builds work
Change build constraints from 'cgo' to 'luxgpu' tag to prevent
compilation when Metal/CUDA libraries are not available.
This allows building with CGO_ENABLED=1 without requiring
the C++ GPU libraries to be installed.
- fhe_ops.go: !luxgpu (fallback)
- kzg4844_gpu.go: luxgpu
- blake3_gpu.go: luxgpu
- frost_gpu.go: luxgpu
- corona_gpu.go: luxgpu
- threshold_gpu.go: luxgpu
- poseidon_gpu.go: luxgpu
BREAKING CHANGE: Module renamed from precompiles to precompile (singular)
- Remove lattice/v6 indirect dependency
- Exclude lattice/v6 versions to prevent transitive imports
- Only lattice/v7 is used now