registry kept ~400 address constants beside the addresses the precompiles
actually claim in their init(). Two records of one fact, and they had already
drifted: the prose mapped chain nibble 3 to D-Chain while the constants used it
for Q-Chain. The package now derives InFamily from modules.RegisteredModules(),
so a question asked here and an address dispatched by the EVM cannot disagree.
PrecompileAddress, ChainSlot and FamilyPage compute what the table enumerated.
RegisteredModules returned the package slice itself. The host walks it to build
the per-block precompile set, so its order is consensus-visible, and any caller
that sorted or truncated it reordered the registry for every subsequent block.
It returns a copy. The duplicate 0x9000-0x9FFF allowlist range is one entry.
Corona is a Module-LWE threshold signature (Ringtail/Raccoon line,
ePrint 2024/1113; module dims M=8, N=7 over Z_q[X]/(X^256+1), rank>1).
Pulsar is Module-LWE (FIPS-204 ML-DSA). Neither is Ring-LWE; the "M"
in ML-KEM/ML-DSA is Module.
Fixes the 0x012206 slot-map comments (p3q, magnetar, starkfri,
modules/registerer), the Corona precompile doc comments, the registry
CORONA description string, and the README/LLM/corona-README docs.
Comments, strings, and docs only — no identifier, precompile address,
selector, gas value, or on-chain digest changed.
P3Q (0x012205) is the rollup-commit PQ verifier (kind-byte dispatch to
Pulsar/Corona/Magnetar), NOT a STARK — STARK-FRI is at 0x012220. Correct
three stale comments (magnetar, modules/registerer, fhe/dos_audit) that
labeled 0x012205 a STARK. Align the registry.go C-nibble doc-comment to
the authoritative node/genesis/builder/registry.go: remove phantom
M-Chain, add D-Chain at C=3, mark Zoo/Hanzo/SPC as sovereign-L1 EVM.
Comments only; no constant or VM logic changed. Builds clean.
0x9999 — the native C<->D atomic DEX settlement money path — is now ALWAYS-ON:
present + active on every chain that loads the EVM plugin, from genesis, with NO
dexSettleConfig genesis/upgrade entry. This is the production-launch design; the
per-net config-gate is killed. Forward-perfect, one activation way.
Activation: modules.Module gains an AlwaysOn flag (+ modules.AlwaysOnModules()).
SettleModule sets AlwaysOn=true. The Configurator is retained only to satisfy the
interface; for an always-on module the host never invokes Configure (no activating
config). SettleConfig collapses to a parameter-free {Upgrade} marker that always
Verifies — 0x9999 takes no per-net parameters.
Params resolved at RUNTIME, not config (Configure has no consensus context, so it
cannot resolve them):
- protocolFeeController: built-in DefaultDAOTreasury (set in init). One canonical
DAO governance authority across every network; not a compromised dev key.
- networkID / cChainID: already sourced at runtime via contract.AtomicState.
- dChainID (THE crux): contract.AtomicState gains DChainID(). The 4 native-client
sites that read loadDChainTarget(stateDB) now read atomicState.DChainID(), which
the host resolves from the chain topology (consensus-context "D"/dexvm alias).
ids.Empty (no dexvm on this network) closes the seam — fail-secure, no mint.
Money-path logic is byte-for-byte unchanged: DeriveIntentID/DerivePositionCommitID,
lock/stage/credit, conservation, exactly-once, rail discriminator, FIX-2 one-batch
flush, nonReentrant/custody guards all untouched. The ONLY change is HOW the
precompile activates and obtains its params (runtime, not config). The Empty-dChain
revert is preserved (strengthened: SubmitSwapIntent/SubmitPositionCommit now revert
on Empty instead of falling back to the C-Chain's own id).
Dead config machinery removed (one way, no dead code): SetSettle{ChainIdentity,
DChainTarget}, loadDChainTarget, loadSettleChainIdentity, cfg{Net,CChain,DChain}IDKey
slots, the STM read-set entries for them, and the now-unused kvAdapter Configure bridge.
Tests: full dex suite 335/335 PASS, 0 fail, 0 skip (CGO_ENABLED=0; the test binary's
only CGO dep is luxfi/accel, unrelated to the pure-Go money path). Harness feeds the
D peer via the runtime AtomicState capability, mirroring production exactly — proof
that the money path is intact with zero config.
go.sum: reconcile e2e threshold v1.9.4 to the origin-canonical hash (upstream re-tag).
DEX module previously defaulted ProtocolFeeController to Anvil/Foundry
account #0 (0xf39Fd6...92266) whose private key
(0xac0974...80) is in every Foundry user's ~/.foundry. At Quasar Edition
activation, any random caller could have invoked PauseDEX, ResumeDEX,
or the irreversible FreezePool on the chain-wide AMM precompile — a
chain-wide compromise the cryptographer review caught.
Fix is a fail-closed activation gate:
- dex/module.go::Configure() returns ErrDEXNoProtocolFeeController when
upgrade.json dexConfig omits protocolFeeController (zero address).
luxd will refuse to advance past the activation block until the
operator populates the field with a real KMS-controlled multisig.
- compromisedDevControllers map blocks all ten Anvil deterministic
accounts; any explicit attempt to install a public dev key fails with
ErrDEXCompromisedController (the address is included in the error).
- dex/dex_test.go covers: zero-address rejection, all 10 anvil
addresses rejected, a real address activates cleanly, random callers
cannot invoke admin functions even with a real controller, and the
Configure interface still returns *Config.
Same audit pass also wires three previously-stub-only precompiles into
the modules.RegisterModule registry so upgrade.json can gate them:
- kzg4844 (LP-3665 KZG point-evaluation extensions, ConfigKey=kzg4844Config)
- ed25519 (LP-3211, ConfigKey=ed25519Config) — Solana/TON/XRP signature
verify lands as a first-class registered precompile alongside the
legacy NewModule() metadata wrapper.
- secp256r1 (EIP-7212 P256-verify, ConfigKey=secp256r1Config) — stateful
adapter wraps the existing Contract.Run(input) into a contract
StatefulPrecompiledContract that does gas deduction and strict-PQ
refusal uniformly.
- modules/registerer.go extends reservedRanges with 0x100-0x1FF
(post-EVM-standard EIP band, for P256) and 0x32000...0000 to
0x32FF000...0000 (high-byte LP-3xxx classical-signature page, for
ed25519). Both ranges are narrow enough that no other precompile
family overlaps.
router precompile audit: no Anvil default existed there (RouterConfig
already used zero-as-skip semantics rather than zero-as-default).
Tests:
go test -count=1 -race -timeout 120s ./dex/... ./kzg4844/... \\
./ed25519/... ./secp256r1/... ./modules/...
All pass; full ./... suite (49 packages) green.
Magnetar is the threshold sibling of SLH-DSA in the LP-4200 unified PQCrypto
block. The construction (Pedersen-VSS DKG + MPC evaluation over the WOTS+/
FORS/Merkle signing tree of FIPS 205) produces a signature byte-equal to a
single-party FIPS 205 signature on the same message and group public key.
The precompile dispatches to luxfi/crypto/slhdsa.VerifySignatureCtx under
the hood — the verifier is interchangeable with single-party SLH-DSA. The
dedicated slot 0x012207 binds the verification to "this signature came
from a threshold ceremony" for on-chain code that cares about the binding,
provides gas-tier separation, and reserves the slot for future non-FIPS-
205 verification paths (identifiable-abort hint, aggregated-cert envelope).
Mirrors the pulsar precompile shape (threshold ML-DSA at 0x012204) and the
slhdsa precompile shape (single-party SLH-DSA at 0x012203). All 12 FIPS 205
parameter sets (SHA2/SHAKE × 128/192/256 × s/f) are accepted with the same
per-mode gas schedule as slhdsa.
Tests pin the canonical address, exercise the per-mode gas table, reject
short / unsupported-mode inputs, round-trip a valid FIPS 205 signature
through the precompile, and confirm a flipped signature byte yields the
0x00 result word.
The pre-LP-4200 quasar package was a tangle:
- verklePrecompile shipped a forgeable oracle (rip-shipped in commit b949327)
- blsPrecompile + blsAggregatePrecompile + coronaPrecompile + hybridPrecompile + compressedPrecompile were classical-crypto contracts living in the 0x0300..0020-25 slots — overlapping the 0x0300-0x03FF range that AI Mining officially owns
- quasar/module.go only registered the (forgeable) verkle slot; the other 5 were declared but unwired
All 6 precompiles now refuse-and-deprecated; the LP-4200 0x012200 block (ML-KEM, ML-DSA, SLH-DSA, Pulsar, P3Q, Corona) is the canonical PQ home.
Rip:
- precompile/quasar/ deleted (IQuasar.sol, contract.go, contract_test.go, module.go)
- blank-import dropped from lux/chains/evm/main.go + liquidity/evm/main.go
- modules/registerer.go: AI Mining is now sole owner of 0x0300-0x03FF; comment updated to record the quasar retirement so future readers know not to re-claim the range.
Neither pulsar/ nor p3q/ had a module.go. ML-DSA / ML-KEM / SLH-DSA /
Corona each had an init() that calls modules.RegisterModule(...) at
import time — that's how the registry binds the precompile's
Configurator + Contract to its Address. Without a module.go for
pulsar + p3q, neither was reachable from chain transactions; calling
the address fell through to 'no code' (silently returned empty,
caller saw 'verification = 0').
The Pulsar slot is the canonical Module-LWE threshold signature
verifier in the LP-4200 block — the StrictPQ profile's primary
threshold-signing path. P3Q is the strict-PQ STARK proof verifier
for the family canonical Plonky3-fork backend.
Add module.go for both, mirroring the mldsa/corona pattern:
configurator + Config + init() registering against the canonical
address. Tests pass for both packages.
Also update modules/registerer.go to document the full 0x012200
block (was only listing through Corona-Threshold=0x12204; now
explicit through 0x12206 Corona).
Per LP-4200 unified PQCrypto block:
- ML-KEM 0x012201 (was 0x0200...0007)
- ML-DSA 0x012202 (was 0x0200...0006)
- SLH-DSA 0x012203 (was 0x0600...0001)
- Corona 0x012204 (was 0x0200...000B)
Adds LP-12xxx range (0x012000-0x012FFF) to modules/registerer
whitelist so the new block validates as in-range.
LLM.md updated to mark legacy 0x0600 / 0x0800 ranges as superseded
by the unified block.
Solidity callers updated in luxfi/standard (#59).
Spec docs reconciled in lp-3500/lp-3520 (#61).
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