The AlwaysOn DEX settlement precompile (0x9999) had its EXTCODESIZE marker
baked into EVERY chain's genesis state unconditionally (parent==nil in
ApplyPrecompileActivations), mutating the genesis state root. This changed the
genesis hash of every chain genesised before the DEX activation, breaking RLP
re-import of the real archived Lux mainnet (0x3f4fa2a0), Lux testnet
(0x1c5fe377) and Zoo mainnet (0x7c548af4) — all re-genesised Nov 2024, before
the Dec-2025 activation.
Fix: an AlwaysOn system precompile carries a protocol ActivationTime (Dec 2025
for 0x9999, set by the registry bridge — a fork-like constant, not per-net
config). Its genesis marker is installed ONLY IF ActivationTime <= the genesis
timestamp; otherwise it activates later, on the block transition that crosses
ActivationTime during history replay. Dispatch (GetExtrasRules) is gated on the
same timestamp so a codeless address never dispatches. A chain born before
activation reproduces its ORIGINAL genesis byte-for-byte; a fresh chain born
at/after activation still gets 0x9999 at genesis.
Verified against the real RLP block-0 of all three chains (byte-identical
reproduction) plus a fresh post-activation chain. Corrects two pre-existing
tests that had hardcoded the buggy with-0x9999 hashes as "canonical".
Co-authored-by: Hanzo Dev <[email protected]>
First-run network, no legacy, no pre-activation history to protect, so the
DexSettleActivationTime dated-fork gate is pure complexity. Delete the concept of
an activation time: 0x9999 dispatches unconditionally and its EXTCODESIZE marker
installs at genesis (parentTimestamp==nil); StrictPQ pinned active-from-genesis for
the Lux plugin (whole ecosystem is PQ-native — removed the per-VM config conditional).
Deleted: DexSettleActivationTime const + pointer/accessor/predicates, the cross-repo
mirror, the drift-guard test, and the 'behaves-as-plain-account-before-activation'
replay-safety branches/prose. One activation path; less code. Genesis determinism via
the single core/genesis.go toBlock() derivation. Feature tests green (0x9999 registered/
dispatched/marked-at-genesis; StrictPQ genesis+nil-inert; plugin PQ).
Replace the v1.99.32 "always-on, dispatch-only, no marker" activation of the
0x9999 DEX settlement money path with the CTO's exact model: a SINGLE canonical
dated fork that both enables dispatch AND installs the EXTCODESIZE marker —
forward, at the fork boundary, never in historical genesis.
The fork: extras.DexSettleActivationTime = 1766704800 (Dec 25 2025 00:00:00 UTC),
defined ONCE in params/extras/network_upgrades.go alongside the Durango/Quasar/
Fortuna/Granite built-in upgrades. NOT per-net config (no per-net upgrade.json
precompileUpgrades entry, no genesisPrecompiles entry) — one dated fork, identical
on every Lux network, DRY. This is the proper canonical fork mechanism for a
system precompile that takes no per-net parameters (0x9999 resolves protocolFee-
Controller=DefaultDAOTreasury and dChainID at runtime from the consensus context).
Two coupled effects, both gated on that one timestamp:
1. DISPATCH (params/config_extra.go GetExtrasRules): the AlwaysOnModules() inject
into the enabled precompile set is now wrapped in extras.IsDexSettleActive(ts)
— present at/after Dec 25, ABSENT before. This is REQUIRED for canonical
history: replaying a pre-Dec-25 block (the ~/work/lux/state RLP snapshot, e.g.
lux-mainnet 96369 = 1,082,780 blocks via admin.importChain) must see 0x9999 as
a PLAIN ACCOUNT so a historical value transfer to that address behaves as it
did originally, not as a precompile dispatch. The prior unconditional inject
would have diverged replay from canonical state.
2. MARKER (core/state_processor_ext.go ApplyPrecompileActivations): on the block
transition that CROSSES the fork (params.IsDexSettleForkTransition →
extras.IsForkTransition), write the STANDARD precompile-activation marker
(SetNonce=1 + SetCode{0x1}) into 0x9999 — identical to the config-driven path.
This is the forward state upgrade: EXTCODESIZE(0x9999)>0, eth_getCode!=0x, and
Solidity's contract-existence guard passes for a typed IPoolManager(0x9999)
.swap(...). The prior change DROPPED this marker to avoid mutating genesis;
installing it forward at the fork preserves the genesis hash (pre-activation
blocks get NO marker) while making typed calls work post-activation. Reverts
the v1.99.32 "writes NO genesis state for always-on modules" skip.
IsForkTransition fires exactly once (!parentForked && currentForked). For a fresh
net whose genesis timestamp is already >= the fork (parent==nil), it fires at
genesis — so fresh/local/new nets carry the marker from block 0 via the SAME
mechanism, no separate genesisPrecompiles entry (CTO step 4). For an existing net
(genesis ts < fork) it does NOT fire at genesis — genesis hash immutable — and
fires forward at the Dec 25 block.
KEPT byte-for-byte: runtime DChainID via BCLookup.Lookup("D"), DefaultDAOTreasury
protocolFeeController, networkID/cChainID from runtime context, AlwaysOn module
tagging + AlwaysOnModules(), all settlement/money-path logic.
0x9010: with precompile v0.5.53 the 0x9010 module is no longer registered, so the
EVM never dispatches it and eth_getCode(0x9010)=0x. registry comment updated to
LP-9999. No EVM-side 0x9010 dispatch reference existed.
deps: precompile v0.5.52 -> v0.5.53.
Tests (core/precompile_alwayson_test.go, rewritten — DRY, replaces the prior
unconditional/no-marker assertions):
- TestDexSettle_DispatchGate_TimestampGated: 0x9999 absent pre-fork, present
at/after (replay safety + post-activation dispatch).
- TestDexSettle_NoGenesisMarker_ExistingNet: genesis (ts<fork) writes no marker
(genesis hash immutable).
- TestDexSettle_MarkerInstalledAtForkTransition: marker (nonce=1 + code, EXTCODESIZE>0)
installed at the boundary block.
- TestDexSettle_ForkTransitionFiresOnce: fires once; fresh-net genesis>=fork at
block 0; existing-net genesis<fork not at genesis.
- TestAlwaysOn_9999_Registered: 0x9999 bridged AlwaysOn.
core + params/extras green incl. the real-mainnet genesis-hash guards. (Two
pre-existing unrelated failures on pristine main: params/config_test.go references
a removed DurangoTimestamp field; params/extras mainnet upgrade.json ringConfig/
warpConfig timestamp guards — both fail identically with these changes stashed.)
Wire the EVM side of the always-on DEX settlement money path 0x9999. It is ACTIVE
on every chain from genesis with NO config entry — neither a genesis-inlined
precompile nor a precompileUpgrades timestamp. Zero per-net config, one way.
Activation is DISPATCH-only (genesis state is consensus-critical and immutable):
- params.GetExtrasRules injects every modules.AlwaysOnModules() entry into the
enabled precompile set unconditionally (every chain, every timestamp). This is
what PrecompileOverride.IsPrecompileEnabled consults, so 0x9999 runs for a
tx-to-0x9999, a low-level CALL/STATICCALL, and the 0x9010 V4 forward.
- ApplyPrecompileActivations writes NO genesis state for always-on modules. An
EXTCODESIZE marker at genesis would change the genesis hash and FORK existing
networks (a fresh-syncing node would disagree with the committed genesis). The
real lux-mainnet genesis hash is preserved (TestSkipPostMergeFieldsGenesisHash).
- The always-on module's Configurator is never invoked (no activating config).
DChainID runtime resolution (THE crux): contract.AtomicState gains DChainID(); the
EVM adapter (accessibleStateAdapter) resolves it from the consensus context via
runtime.GetBCLookup().Lookup("D") — the dexvm "D"/"dex"/"dexvm" alias the node
registers in initChainAliases before any chain bootstraps. Identical on every
validator and re-execution; ids.Empty on a network with no dexvm (seam stays closed,
fail-secure). The registry bridge forwards DChainID across the internal/external
AtomicState surfaces. modules.Module gains AlwaysOn (+ AlwaysOnModules()), bridged
from github.com/luxfi/precompile.
deps: precompile v0.5.51 -> v0.5.52 (DChainID + AlwaysOn + always-on settle module).
Tests: core suite green incl. the real-mainnet genesis-hash guards. New
core/precompile_alwayson_test.go: 0x9999 registers AlwaysOn, is dispatch-enabled with
NO config at genesis + far-future ts, and writes NO genesis state (genesis-hash
immutability). (Two pre-existing unrelated failures: params/config_test.go references
a removed DurangoTimestamp field; mainnet upgrade.json aiMining/warp timestamp guards
— both fail identically on pristine main.)
All Lux EVM deployments now have Shanghai and Cancun active at
genesis by default:
- ShanghaiTime=0: PUSH0, warm coinbase
- CancunTime=0: MCOPY, TSTORE, TLOAD, BLOBHASH, BLOBBASEFEE
SetDefaults() no longer clobbers explicit timestamp=0 values.
This means genesis configs with evmTimestamp=0 correctly inherit
Cancun activation without needing explicit shanghaiTime/cancunTime.
Also fixed ApplyPrecompileActivations to write precompile state at
genesis block (previously guarded by block > 0 check).
Updates dependencies:
- github.com/luxfi/vm v1.0.22 → v1.0.23 (adds ZAP transport Serve)
- github.com/luxfi/log v1.3.0 → v1.4.1 (fixes module path declaration)
- github.com/luxfi/consensus v1.22.53 → v1.22.56
- github.com/luxfi/crypto v1.17.39 → v1.17.40
- github.com/luxfi/database v1.17.38 → v1.17.39
- github.com/luxfi/geth v1.16.69 → v1.16.73
- github.com/luxfi/p2p v1.18.7 → v1.18.8
- github.com/luxfi/warp v1.18.4 → v1.18.5
The vm package now provides a ZAP-based Serve function by default,
eliminating the need for the grpc build tag for VM plugins.
Also includes various improvements to validators, warp service,
and precompile interfaces.
- Fix ALL SetCode calls to use 2 parameters (remove tracing constants)
- Add SetCodeUnspecified wrapper for compatibility
- Fix StartPrefetcher to use 2 parameters instead of 3
- Fix all test files and runtime code
- NO SKIPS, NO TODOs, NO FAKE implementations
- This should achieve 100% green CI
- Comment out all local replace directives (../node, ../consensus, etc)
- Keep only tablewriter replace which is needed
- CI should now be able to download all dependencies from GitHub
- Change Go version from 1.24.6 to 1.22.8 (stable version)
- Ensure all build scripts have executable permissions
- Fix covdata issue by using stable Go version
- CI should now pass all checks
- Update consensus imports from luxfi/node/consensus to luxfi/consensus
- Remove all snow package references
- Fix utils/logging to use luxfi/log
- Update EIP-4844 API changes (CalcBlobFee, CalcExcessBlobGas)
- Add tracing.CodeChangeReason to SetCode calls
- Fix validators.GetCurrentValidatorOutput to GetValidatorOutput
- Fix wallet API changes (IssueCreateSubnetTx to IssueCreateNetTx)
- Fix VM interface compatibility between node and consensus packages
- Fix extstate.StateDB references to use state.StateDB
- Fix trienode API to use NodeWithPrev
- Remove duplicate files and unused code
- Update all dependencies to use externalized luxfi packages
- Move our code from state_processor.go to state_processor_ext.go
- Move our code from state_processor_test.go to state_procesor_ext_test.go
- Slight modifications to state_processor.go to make it more alike geth master