mirror of
https://github.com/luxfi/genesis.git
synced 2026-07-27 04:11:41 +00:00
chore: kill UPGRADE_NOTES.md (AI-generated upgrade-note slop; upgrade.json is canonical)
This commit is contained in:
@@ -1,209 +0,0 @@
|
||||
# Lux Primary C-Chain — `upgrade.json` Activation Notes
|
||||
|
||||
Source-of-truth for the Lux primary network C-Chain (chainId `96369`) precompile
|
||||
activation schedule. This file lives next to `upgrade.json` to explain why each
|
||||
activation is dated where it is, and which precompiles are deliberately
|
||||
excluded from the primary-network safe subset.
|
||||
|
||||
## Safe-subset framing
|
||||
|
||||
- **L2** (hanzo, zoo, pars, spc) carry **42 precompiles** baked into
|
||||
their genesis at block 0 after the `eciesConfig` scrub in this patch.
|
||||
See `~/work/hanzo/universe/configs/genesis/mainnet/genesis.json` for the
|
||||
canonical L2 set; the precompile keys are identical across the four
|
||||
L2 genesis files.
|
||||
- **Primary C-Chain safe subset = 42** (the L2 set), plus three
|
||||
burn-handler precompiles (`deadConfig`, `deadFullConfig`,
|
||||
`deadZeroConfig`) that exist only on the primary network, plus
|
||||
`warpConfig` and the AI-Mining / DEX / Router stack already activated
|
||||
at genesis (`blockTimestamp: 0`).
|
||||
- **NOT in this patch** (deferred to v2 — see *Followup* below):
|
||||
`kzg4844Config`, `secp256r1Config`, `ed25519Config`. The pinned
|
||||
`luxfi/precompile v0.5.27` ships these as primitive modules but does
|
||||
not declare `RegisterModule(...)` for them, so the EVM extras
|
||||
`PrecompileUpgrade.UnmarshalJSON` parser rejects them with
|
||||
`"unknown precompile config"`. Re-add after bumping
|
||||
`luxfi/precompile` to a version that registers all three (and
|
||||
rebuilding the EVM plugin image so the registry side-effect imports
|
||||
pick them up).
|
||||
|
||||
## Exclusions
|
||||
|
||||
### `eciesConfig` — deliberately excluded
|
||||
|
||||
ECIES requires the caller to pass the recipient's *private key* in calldata
|
||||
so the precompile can perform the discrete-log unwrap. Calldata on a public
|
||||
chain is permanently world-readable; placing a private key there
|
||||
irrecoverably exposes it. The same reasoning applies to the symmetric-key
|
||||
precompiles (AES, ChaCha20), which were removed for the same reason — see
|
||||
`~/work/lux/precompile/x25519/contract.go` lines 17-23 (the live comment
|
||||
block carrying the rationale).
|
||||
|
||||
X25519 key exchange is retained because the shared secret is *not* in
|
||||
calldata — only ephemeral public points are. ML-KEM-768 / HQC / X-Wing are
|
||||
retained for the same reason: they expose only ciphertexts and encapsulated
|
||||
keys, never secrets.
|
||||
|
||||
### `contractDeployerAllowListConfig`, `contractNativeMinterConfig`, `txAllowListConfig`, `rewardManagerConfig` — deliberately excluded
|
||||
|
||||
These are admin-gated allow-list precompiles whose activation requires the
|
||||
operator to provide a stable `adminAddresses` list. The primary C-Chain is
|
||||
permissionless by design — there is no admin address to assign — so
|
||||
activating these would either (a) hand admin to an arbitrary address and
|
||||
create a trust hazard, or (b) activate them empty and leave the gate
|
||||
dormant, which is strictly worse than not activating them at all. They
|
||||
remain available for L2 / subnet activation where an admin role makes
|
||||
sense.
|
||||
|
||||
### `feeManagerConfig` — removed from canonical
|
||||
|
||||
Previously parked at `blockTimestamp: 900000000` (1998-07-09 UTC) as a
|
||||
registry-slot reservation, but that placement violates the monotonicity
|
||||
rule enforced by `extras.ChainConfig.verifyPrecompileUpgrades` (timestamps
|
||||
must be non-decreasing across the upgrade list). The entry is removed from
|
||||
canonical; if the FeeManager precompile is ever needed, it can be activated
|
||||
via a separate forward-dated governance event with admin addresses
|
||||
explicitly assigned. The current `chain-configs/lux-mainnet/config.json`
|
||||
already pins `feeConfig` directly, so no behavioural change.
|
||||
|
||||
## Activation tiers
|
||||
|
||||
| Tier | Timestamp (Unix) | Timestamp (UTC) | Count | Notes |
|
||||
|------|------------------|-----------------|-------|-------|
|
||||
| Live-at-genesis | `0` | block 0 | 17 | The exact set inlined in `~/work/lux/universe/k8s/lux-mainnet/luxd-startup.yaml` `UPGRADE_JSON` (`aiMiningConfig`, `blake3Config`, `cggmp21Verify`, `deadZeroConfig`, `deadConfig`, `deadFullConfig`, `routerConfig`, `fheConfig`, `frostVerify`, `graphConfig`, `hpkeConfig`, `mldsaVerify`, `mlkemConfig`, `pqcryptoConfig`, `ringConfig`, `slhdsaVerify`, `zkConfig`). These are ACTIVE AT BLOCK 0 on the running mainnet. They MUST stay pinned to `blockTimestamp: 0`. RESCHEDULING ANY OF THEM to a post-genesis timestamp is a relaunch fork / boot-refusal hazard: a relaunched node would treat them as inactive before that timestamp and diverge from the canonical chain (or `checkPrecompileCompatible` refuses boot). Pinned and proven by `IsForwardCompatibleWithLiveActivations` and `relaunch_safety_test.go`. `warpConfig` is NOT in this tier — it lives in the genesis chainConfig (`cchain.json` `config.warpConfig` at the genesis timestamp `1730446786`). |
|
||||
| Strict-PQ warp toggle | `1766708399` / `1766708400` | 2025-12-26 00:19:59 / 00:20:00 UTC | 2 | `warpConfig` disable@`1766708399` then re-enable@`1766708400` with the PQ-hardened policy (`quorumNumerator: 67`, `requirePrimaryNetworkSigners: true`). Both are strictly AFTER genesis time, so this is a genuine FUTURE upgrade, not a reschedule of the genesis warp. |
|
||||
| BLS12-381 family | `1766708400` | 2025-12-26 00:20:00 UTC | 7 | EIP-2537 G1/G2 add/mul/MSM + pairing, activated at the strict-PQ fork so each is gated by `RefuseUnderStrictPQ` from its first active block. |
|
||||
| Quasar-Edition safe-subset | `1766708400` | 2025-12-25 00:00 UTC | 20 | Forward-dated to a clean Q3 boundary, after the strict-PQ gate, so every classical primitive in the tier is registered behind the gate. L2 already carry these at their own block-0 genesis. Three EIP precompiles (`kzg4844Config`, `secp256r1Config`, `ed25519Config`) are NOT included — see *Exclusions* and *Followup*. |
|
||||
|
||||
Total entries: **46** (17 live-at-`0` + 2 warp toggle + 7 BLS + 20 forward-dated). All entries with a non-zero timestamp are strictly greater than the C-Chain genesis timestamp `1730446786`, so a relaunch from the canonical genesis re-fires NO already-applied activation. The array is ordered so all `blockTimestamp: 0` entries come first, satisfying the non-decreasing monotonicity rule in `verifyPrecompileUpgrades`.
|
||||
|
||||
## Strict-PQ profile gate
|
||||
|
||||
`strictPQTimestamp = 1766708400` (2025-12-25 16:20 PST / 2025-12-26 00:20
|
||||
UTC) activates `contract.StrictPQReporter` on the ChainConfig. All
|
||||
classical pairing-based / discrete-log precompiles (BLS12-381 G1/G2/Pairing,
|
||||
BabyJubJub, Pasta) call `contract.RefuseUnderStrictPQ(state)` at the top of
|
||||
`Run()` and return `ErrClassicalForbiddenInPQ` while the chain is in
|
||||
strict-PQ mode.
|
||||
|
||||
The forward-dated activations at `1766708400` fall *after* the strict-PQ
|
||||
activation, so every classical precompile in tier 2 is registered behind
|
||||
the gate from its first block of activation. No window exists where a
|
||||
classical precompile would execute permissively on the primary network.
|
||||
|
||||
## Forward-date rationale
|
||||
|
||||
Today's date when this patch was authored: **2026-06-02**.
|
||||
Chosen activation: **`1766708400` (2025-12-25 00:00 UTC)** = **29 days of
|
||||
buffer**.
|
||||
|
||||
The 14-day minimum buffer requested by Task #114 is exceeded comfortably.
|
||||
A clean calendar boundary (Q3 start) was chosen so validators have an
|
||||
unambiguous, easy-to-remember mark. UTC midnight has no DST drift.
|
||||
|
||||
## Deployment surfaces
|
||||
|
||||
This canonical file is the source-of-truth. The runtime distribution
|
||||
surfaces that consume it:
|
||||
|
||||
1. `~/work/lux/genesis/configs/mainnet/upgrade.json` — canonical (this
|
||||
file).
|
||||
2. `~/work/lux/universe/k8s/lux-mainnet/luxd-startup.yaml` — k8s
|
||||
StatefulSet ConfigMap. The canonical content above is mirrored
|
||||
byte-for-byte in the `cchain-upgrade.json` data key. The startup
|
||||
script copies that file to `/data/configs/chains/C/upgrade.json`
|
||||
at boot (PRIMARY C-CHAIN ONLY). L2 chain config directories
|
||||
are NOT touched — L2 genesis files carry their precompile
|
||||
activations baked at block 0 and overwriting their upgrade.json
|
||||
with the C-Chain schedule could reschedule an already-live
|
||||
precompile and brick the chain via `checkPrecompileCompatible`.
|
||||
Sync rule: this file and the ConfigMap data key are a coordinated
|
||||
pair; updating one without the other is a silent drift footgun.
|
||||
|
||||
## Followup (v2 patch)
|
||||
|
||||
1. Bump `github.com/luxfi/precompile` (currently pinned at `v0.5.27` in
|
||||
`~/work/lux/evm/go.mod:36`) to a version that declares
|
||||
`RegisterModule(...)` for the `kzg4844`, `secp256r1`, and `ed25519`
|
||||
modules. Each module must have an `init()` that registers a
|
||||
`precompileconfig.Config` factory keyed on `kzg4844Config`,
|
||||
`secp256r1Config`, and `ed25519Config` respectively.
|
||||
2. Rebuild the EVM plugin image so its
|
||||
`~/work/lux/evm/precompile/registry/registry.go` side-effect imports
|
||||
pick up the new `init()` calls.
|
||||
3. Re-add the three keys to `~/work/lux/genesis/configs/mainnet/upgrade.json`
|
||||
at a forward-dated timestamp (still after Quasar activation
|
||||
`1766708400` so the strict-PQ gate is in force when they activate).
|
||||
4. Re-run the new
|
||||
`TestMainnetUpgradeJSON_UnmarshalsAgainstRegistry` end-to-end test
|
||||
to confirm the parser accepts every key in the canonical file.
|
||||
|
||||
## Brand genesis scrub (this patch)
|
||||
|
||||
`eciesConfig` is deliberately not registered in
|
||||
`luxfi/evm/precompile/registry/registry.go` (the ECIES precompile was
|
||||
removed because the recipient secret key sits in calldata — see
|
||||
*Exclusions* above). The L2 genesis files at
|
||||
`~/work/lux/genesis/configs/{hanzo,zoo,pars,spc}-{mainnet,testnet,devnet}/genesis.json`
|
||||
(11 of 12) carried `eciesConfig` in their block-0
|
||||
`precompileUpgrades` array, which would fail the same
|
||||
`PrecompileUpgrade.UnmarshalJSON` parser as soon as any CTO-side
|
||||
canonical-genesis rebootstrap re-fired a `CreateChainTx` against them.
|
||||
The scrub strips `eciesConfig` from those 11 files in this same
|
||||
patch. `zoo-mainnet` had no `precompileUpgrades` array and was
|
||||
untouched.
|
||||
|
||||
## Verification
|
||||
|
||||
After luxd reads this file at boot, validators on Lux mainnet should see:
|
||||
|
||||
- Existing primary-C-Chain RPC behaviour preserved (no boot regression);
|
||||
`IsForwardCompatibleWithLiveActivations` is the gate.
|
||||
- After `2025-12-25 00:00 UTC`: `eth_call` to BLS12-381 precompile
|
||||
addresses succeed for honest inputs and return
|
||||
`ErrClassicalForbiddenInPQ` (chain reports strict-PQ from `1766708400`).
|
||||
- ML-DSA, SLH-DSA, ML-KEM, HQC, X-Wing precompiles available after
|
||||
forward-date.
|
||||
- Pulsar / Magnetar / Corona / P3Q threshold + PQ-STARK precompiles
|
||||
available after forward-date.
|
||||
|
||||
## Red review checklist
|
||||
|
||||
- [x] Every classical precompile in tier 2 (BLS12-381 family, BabyJubJub,
|
||||
Pasta, Pedersen, Poseidon) is verified to call
|
||||
`contract.RefuseUnderStrictPQ` at the top of its `Run()` method. If
|
||||
any does not, that is a separate latent bug — flag in followup, do
|
||||
not block this patch.
|
||||
- [x] The 27 new activations at `1766708400` do not introduce any
|
||||
precompile whose source has not been audited or which has been
|
||||
deprecated.
|
||||
- [x] Activation date `1766708400` (2025-12-25 00:00 UTC) is no earlier
|
||||
than 14 days from merge time.
|
||||
- [x] No `eciesConfig`, `contract*AllowListConfig`, `*NativeMinterConfig`,
|
||||
`rewardManagerConfig`, or `*MinterConfig` in the activation list.
|
||||
- [x] No `kzg4844Config`, `secp256r1Config`, or `ed25519Config` in this
|
||||
patch (Red vector 8 CRITICAL — deferred to v2 per *Followup*).
|
||||
- [x] All 17 already-live precompiles stay at `blockTimestamp: 0` so
|
||||
`checkPrecompileCompatible` does not refuse boot AND a relaunch from
|
||||
genesis does not re-fire an already-applied activation. Pinned by
|
||||
`IsForwardCompatibleWithLiveActivations` and the new
|
||||
`relaunch_safety_test.go` (mainnet + testnet). `warpConfig` is in the
|
||||
genesis chainConfig, not this tier; its strict-PQ disable/re-enable
|
||||
toggle is strictly future (`1766708399`/`1766708400` > genesis
|
||||
`1730446786`).
|
||||
- [x] Monotonicity test passes (no entry has a smaller blockTimestamp than
|
||||
its predecessor in the list).
|
||||
- [x] k8s StatefulSet startup script patched in lockstep
|
||||
(`~/work/lux/universe/k8s/lux-mainnet/luxd-startup.yaml`): new
|
||||
`cchain-upgrade.json` data key mirrors this canonical byte-for-byte;
|
||||
script seeds it onto `/data/configs/chains/C/upgrade.json` only;
|
||||
the 4 L2 chain dirs are NOT touched.
|
||||
- [x] End-to-end UnmarshalJSON regression test added at
|
||||
`~/work/lux/evm/params/extras_e2e_test/upgrade_unmarshal_test.go`
|
||||
(Red vector 9 MEDIUM): asserts the canonical parses cleanly via
|
||||
`extras.UpgradeConfig.UnmarshalJSON` after registry side-effects
|
||||
AND asserts unregistered keys (kzg4844/secp256r1/ed25519) are
|
||||
rejected with the canonical "unknown precompile config" error.
|
||||
- [x] Brand mainnet/testnet/devnet genesis files scrubbed of
|
||||
`eciesConfig` (Red vector 6 INFO): 11 of 12 had it; all 11
|
||||
cleaned; `zoo-mainnet` had no precompileUpgrades array.
|
||||
Reference in New Issue
Block a user