HIGH-3 host side. The always-on 0x9999 settlement precompile resolves its halt +
pot-seeding authority from contract.AtomicState.GovernanceController(); this is the
production wiring that supplies it — a per-network governance CONTRACT, never a
hardcoded mnemonic-derivable EOA.
- dex_value_wiring.go (installDEXValuePath): bind rt.GovernanceController =
dexGovernanceFor(networkID) onto the chain runtime at boot (the ONE production
site), alongside the asset resolver + native D client, and log it. dexGovernanceFor
is the genesis-set per-network governance-contract seam: it returns ids.ShortEmpty
for every network TODAY (no Governor/Timelock is deployed at a known address on any
Lux network yet), so every network runs FAIL-CLOSED (no halt authority — provably no
single key can DoS the DEX) until its real governance CONTRACT address is wired in,
per net. The directive is explicit: each entry MUST be a governance contract, NEVER a
dev-mnemonic EOA, and NEVER the retired 0x9011…94714. The boot log makes an unset
authority loud ("<unset: halt/seed FAIL-CLOSED — deploy + wire a governance contract>").
- core/precompile_overrider.go + precompile/registry/bridge.go: accessibleStateAdapter
and accessibleStateBridge implement GovernanceController() (ids.ShortID→common.Address,
exact 20-byte conversion); the internal twin interface gains the method. Returns the
zero address (fail-closed) when the runtime/atomic capability is absent.
- precompile/contract/atomic.go (evm-internal twin): AtomicState gains GovernanceController().
Builds clean (CGO off) across plugin/evm, core, precompile/registry; the dexsettle_guard
parity test passes. Normal trading is unaffected (halt switches are emergency controls,
off by default); only the ability to halt is gated — which previously only a mnemonic-EOA
holder had (a vuln), so no legitimate capability is lost and a censorship/DoS vector is removed.
This is the SOLE production install of the always-on 0x9999 DEX value-path trust
root — the seam Red flagged as fail-closed-by-absence (InstallAssetResolver /
InstallDChainClient had zero production callers, so every swap reverted
ErrNoAssetResolver). vm.Initialize now constructs and installs, from the node's REAL
chain context (never a constant):
- a registry-backed dexcore.AssetResolver bound to (networkID, cChainID): it derives
the canonical AssetID with the node's own ids and resolves it in a registry built
from the per-network manifest (EmbeddedManifestFor by EVM chainID, or the runtime-
rooted localnet native manifest). The manifest is identity-bound via
NewRuntimeVerifier (wrong-net/wrong-chain refused) and admitted under the locked-down
DefaultDexAssetPolicy synthetic-config deny-scan.
- the local in-process NativeDChainClient for the book/ledger custody seam (NOT a
remote keeper/venue/ZAP engine).
Wired for ALL networks (mainnet 96369 / testnet 96368 / devnet 96370 / localnet 1337)
so the DEX is live out-of-the-box. FAIL-CLOSED, NEVER FATAL: a construction failure is
logged and the resolver is left UNINSTALLED (swaps revert), never opened; the node
still boots for all non-DEX usage. The install binds the SAME (networkID, cChainID)
the swap path's AtomicState cross-checks, so a mismatch is fail-closed at swap time.
- core/precompile_overrider.go: forward GetCodeSize (EXTCODESIZE) on the concrete
stateDBAdapter so the value path's live-code verifier reads authoritative state.
The determinism half of the unified executor: parallel-eligible settlement
reproduces the canonical sequential state root. Reuses the REAL kernels
(detectConflicts via ConflictEdges, SettleBatch, geth IntermediateRoot) — no
second engine, no mock conflict detector.
Test9999_DisjointFillsParallelRootEqualsSequential
(== TestBlockSTM_DifferentMarketsParallel): fills on different markets ->
detectConflicts finds 0 edges -> SettleBatch root == sequential root. In-test
fail-without: collapsing the two onto one market yields an edge (proves the
detector keys on the market, not vacuously empty).
Test9999_ConflictingFillsReExecToSequentialRoot
(== TestHotMarketBatchDeterministicRoot): two fills sharing an account on one
HOT market -> detectConflicts returns edge (0,1) -> SettleBatch serializes in
ascending-INDEX order -> root == C-then-D AND != the swapped D-then-C. Uses an
order-SENSITIVE hot-market slot (keccak(prev||taker)) so the root genuinely
depends on apply order — a balance-only fill is commutative and would make the
determinism claim vacuous; the in-test guard rejects a commutative Apply.
Fail-without PROVEN by temporary revert: reversing SettleBatch's apply order
makes the root diverge to the swapped order and the conflicting-fills test FAILS
(restored after). Owner-named tests satisfied by the same bodies. Full
core/parallel suite green; no existing tests weakened.
Consumes luxfi/precompile v0.5.57 — the RED-CLEARED money-path fix adding the
CALL-only guard (ErrSettleWrongContext) on 0x9999/0x9996 Run. Pairs with this
repo's callableEnvAllowlist gate so the env Call surface reaches only the DEX
settlement family. go.sum regenerated via 'go mod tidy' against the tagged
module (GOWORK=off — the CI condition).
GetPrecompileEnv() is on the SHARED external contract.AccessibleState interface,
so the live Call surface (whose sub-call msg.sender is the asserting precompile's
OWN address) is reachable by every external precompile that type-asserts the
optional callableEnv capability. Bound that blast radius with defense-in-depth:
- precompile/registry/bridge.go: callableEnvAllowlist keyed on the geth env's
self-address (env.Addresses().Self), populated from the canonical
dex.LXSettleAddress (0x9999) + dex.DEXPositionManagerAddress (0x9996) constants
(one source of truth). Every other external precompile (PQ/ZK verifiers at
0x0122xx, future ones) gets a nil callable env and falls back to fail-secure
refusal. No live behaviour change today (none assert callableEnv yet) — it
prevents a FUTURE precompile silently acquiring sub-call power. Sits on top of
each settlement Run's CALL-only guard.
- core/precompile_overrider.go: GetPrecompileEnv comment corrected — the accessor
is off the EVM-INTERNAL interface but IS on the shared external one; reach is
bounded by the bridge gate + the settle CALL-only guard.
- precompile/registry/bridge_precompileenv_test.go: recordingEnv carries a self;
TestBridge_GetPrecompileEnv_NonSettlementSelf_Gated (0x012204 verifier -> nil,
no Call) + _PositionManagerSelf_Allowed (0x9996 -> non-nil); WiredForwardsCall
now models the 0x9999 self.
ROOT CAUSE: the external DEX 0x9999 settlement precompile's ERC-20 leg (transferFrom/
transfer/balanceOf on the token contract) needs the EVM Call surface, but the adapter
chain that delivers its AccessibleState narrowed Call away:
geth evmPrecompileEnv{Call}
-> evm/core accessibleStateAdapter{env} (env captured, not exposed)
-> evm/registry accessibleStateBridge (GetPrecompileEnv() => nil)
-> luxfi/precompile contract.PrecompileEnvironment (no Call)
so poolStateAdapter.callable() got nil and every ERC-20 deposit/withdraw refused with
ErrERC20VaultUnavailable (fail-secure — never minted). Native LUX settlement was
unaffected because it uses GetBalance/SubBalance, already plumbed through the bridges.
FIX (minimal, off the shared many-implementer interfaces):
- evm/core accessibleStateAdapter.GetPrecompileEnv() exposes the geth env it already
holds (concrete accessor, NOT a widening of the internal contract.AccessibleState).
- evm/registry accessibleStateBridge.GetPrecompileEnv() type-asserts that accessor and
returns precompileEnvBridge, which adds the callableEnv-shaped Call the precompile
type-asserts; nil (fail-secure) when no geth env is present.
- Call forwards with NO caller proxying: the token's msg.sender is the precompile self
(0x9999), matching the depositor's approve(0x9999) allowance. Proxying would check
the wrong allowance and break the pull.
geth is UNTOUCHED: vm.PrecompileEnvironment already declares Call; the DEX never runs
through geth's own accessibleStateAdapter (that path serves only the PQ precompiles).
Regression guards: core pins *accessibleStateAdapter satisfies the envProvider accessor
and returns its env; registry pins the bridge returns a non-nil callable env that
forwards Call (value passed, ZERO options) when wired, and untyped-nil otherwise
(no-env + typed-nil traps).
Asserts DefaultGPU().Available()==true and GPU-recovered sender == CPU sender,
proving the block-insert ecrecover path dispatches to the real Metal backend
(libluxgpu_backend_metal.dylib) rather than the CPU fallthrough.
Round-2 follow-up to the luxfi/genesis upgrade.json reschedule fix (17
live-at-genesis precompiles pinned to blockTimestamp 0; warp two-step
strict-PQ toggle moved after the zero-timestamp block; dead dexConfig
0x9010 dropped).
- relaunch_safety_test.go (new): drives the node's REAL fork-application
primitive extras.IsForkTransition (parent=nil reduces to fork <=
genesisTime, exactly what GetActivatingPrecompileConfigs evaluates when
a relaunched node builds its first rule set) against the embedded
canonical fixtures. Asserts the invariant: every precompile that
activates at/under the C-Chain genesis time (mainnet 1730446786 /
testnet 1730517266) is in the live-at-block-0 set AND pinned to exactly
0; every other entry is strictly future; and all 17 live precompiles are
present (no relaunch divergence). No entry sits in the (0, genesisTime]
retroactive-reschedule window.
- mainnet_upgrade_v46.json / testnet_upgrade_v47.json: regenerated
byte-for-byte from the corrected canonical genesis configs (46 / 44
entries; the 25 timestamp diffs were the 1766708400 -> 0 normalization
plus the warp reorder).
- precompile_upgrade_rollout_test.go: WarpRequiresPrimaryNetworkSigners
updated for the two-step toggle — warp is declared in the genesis
chainConfig, and the schedule carries disable@strictPQ-1 then
re-enable@strictPQ; the PQ signer-policy assertion targets the re-enable
entry, and same-key strict-increase is enforced (mirrors
verifyPrecompileUpgrades).
- upgrade_unmarshal_test.go / testnet_upgrade_unmarshal_test.go: fixture
doc comments rewritten to the live-at-0 / strict-PQ-toggle / BLS /
Quasar-Edition tier layout; RegressionProof entry-count assertion 45 ->
46 to match the embedded fixture.
Verified: go vet clean; CGO_ENABLED=0 go test ./params/extras/...
./params/extras_e2e_test/... — RelaunchSafe (mainnet+testnet),
IsForwardCompatibleWithLiveActivations, WarpRequiresPrimaryNetworkSigners,
PrecompileTimestampsAreMonotonic, RegressionProof all PASS.
node v1.30.27/28 dropped the 0x9010 DEX precompile; its config key
dexConfig is unregistered under the pinned luxfi/precompile v0.5.55, so
the vendored canonical fixtures carrying it made
TestMainnetUpgradeJSON_UnmarshalsAgainstRegistry FAIL with 'unknown
precompile config: dexConfig' (the C-Chain brick-on-boot the gate exists
to catch). Remove dexConfig from both fixtures (sync source: luxfi/genesis
configs/{mainnet,testnet}/upgrade.json, cleaned in lockstep), update the
canonical entry-count assertion 46->45, and drop dexConfig from the
lux-mainnet live-activations const so the forward-compat check stays
consistent. Verified CGO_ENABLED=0 go test ./params/extras_e2e_test/ -> ok.
SetEthUpgrades wrote a [SET-ETH-UPGRADES] line to stderr and GetFeeConfig
wrote two DEBUG lines to stdout on every call — leftover debugging, not
intentional logging. Removed all three; dropped the now-unused fmt and os
imports from config_extra.go.
NOTE: coordinated cascade — another agent is bumping this repo's version for
an unrelated change. This is a pure code edit (print removal) on its own
branch; fold into the cascade.
Pins the precompile tag that exports dex.DexSettleActivationTime so the
dexsettle_guard DRY test compiles and enforces extras==dex mirror equality.
go test -tags dexsettle_guard ./core/ PASSES against the pinned tag.
1766704800 is 2025-12-25T23:20:00Z, not midnight (1766620800). Value is the
canonical protocol constant, already live gating 0x9999 dispatch on devnet;
changing it would fork chains that crossed the boundary. Annotate the true
instant; warn against rounding. Comment-only — zero consensus change.
The guard TestDexSettleActivationTime_LayerMirrorsMatch references
dex.DexSettleActivationTime, which is defined in luxfi/precompile and post-dates
evm/go.mod's pinned precompile tag (v0.5.54). Under GOWORK=off (CI), evm/core
failed to build (undefined: dex.DexSettleActivationTime); it only compiled via an
untracked local go.work. The guard was therefore both inert in CI and a latent
CI break.
Move the single cross-repo guard into precompile_alwayson_dexsettle_guard_test.go
behind //go:build dexsettle_guard. Default CI excludes it, so evm/core builds and
tests against the pinned precompile tag with no go.work. The release step bumps
evm/go.mod to a precompile tag that exports the symbol and runs
`go test -tags dexsettle_guard ./core/`, enforcing the DRY invariant atomically
with the bump. precompile cannot host the guard (it does not import luxfi/evm; the
graph is strictly evm->precompile, so referencing extras.DexSettleActivationTime
from precompile would create a cycle). All other 0x9999 activation pins in
precompile_alwayson_test.go use only evm-local symbols and run on every CI build.
The 0x9999 activation timestamp lives in TWO layers that cannot share a symbol: the
canonical extras.DexSettleActivationTime (this evm layer, used by the dispatch gate +
marker transition) and a layer-local mirror dex.DexSettleActivationTime (luxfi/
precompile, used to gate the precompile's new consensus-visible logs — precompile is
below evm in the import graph so it cannot import extras). If they drift, a settlement
could dispatch (extras gate) where the precompile withholds its log (stale mirror) ->
settlement-without-log window -> consensus split on replay. This test imports BOTH and
fails CI the instant either constant moves without the other.
NOTE: compiling this test against the new dex export requires evm go.mod to pin a
luxfi/precompile release that exports DexSettleActivationTime (>= v0.5.55, to be cut
from the precompile branch). Verified locally PASS against the local precompile via a
temporary replace (reverted); the go.mod bump is part of the coordinated release.
Red review of the 0x9999 dated-fork activation (v1.99.33) found ONE HIGH that
blocked the production deploy, plus a money-path footgun and two guards. The
money path (V4 swap ABI, EXTCODESIZE marker, two-phase atomic settle) is
byte-for-byte unchanged — only the dispatch-path timestamp source, the SubBalance
fail-mode, the genesis-builder guard, and stale comments change. Bumps
precompile v0.5.53 -> v0.5.54 (Red L1 comment scrub).
H1 (HIGH, blocked deploy) — consensus-divergence on the relaunch/replay path:
core.LuxPrecompileOverrider.PrecompileOverride read the PROCESS-GLOBAL
params.lastRulesContext via GetRulesExtra(Rules{}). That global is rewritten
last-writer-wins by SetRulesContext on every Rules()/RulesAt() — including
concurrent eth_call/estimateGas/miner goroutines. The geth EVM invokes
PrecompileOverride LAZILY during opcode execution, a different instant than when
precompileHook set the global. On a live, RPC-serving post-fork node replaying a
PRE-fork RLP block (admin.importChain), a concurrent post-fork eth_call could
overwrite the global timestamp between the verify goroutine's NewEVM(pre-fork
block) and its tx dispatch -> the pre-fork block sees 0x9999 ENABLED and
dispatches SettleContract.Run during plain-account execution -> STATE DIVERGENCE.
Fix: the dispatch gate now decides from the overrider's OWN per-EVM fields
(o.chainConfig + o.timestamp) via the pure params.GetExtrasRules — which performs
the SAME dated-fork (extras.IsDexSettleActive) computation GetRulesExtra did,
minus the racy global read. Audited every GetRulesExtra reader: only the overrider
was on the precompile-dispatch path; no other dispatch/consensus-replay call site
consults the 0x9999 gate. Every replay of a given block now yields the same
enabled set on every validator, independent of concurrent activity.
L2 (money-path footgun) — registry stateDBBridge.SubBalance fail-open silent mint:
when the subBalancer type-assertion failed it logged + returned a zero 'previous
balance' WITHOUT subtracting, while the caller treats the debit as successful ->
native value minted. Now FAILS CLOSED: it panics, which the EVM recovers into a
reverted call, so the enclosing value move rolls back rather than committing
half-done. Structurally unreachable in prod (the concrete adapter implements
SubBalance) but no longer mints if a future StateDB omits it.
INFO1 (latent footgun) — genesis-config builders bypassed the dated fork:
SetAllGenesisPrecompiles/AllGenesisPrecompiles iterated ALL RegisteredModules()
incl the AlwaysOn 0x9999 and wrote timestamp-0 genesis configs, which would enable
0x9999 from block 0 on a fresh chain — bypassing the dated fork and re-introducing
the genesis-marker mutation. Both builders now skip AlwaysOn modules.
INFO2 (tests):
- core: -race regression TestDexSettle_PrecompileOverride_NoGlobalRace reproduces
the exact relaunch race (pre-fork verify vs concurrent post-fork eth_call
clobbering the global) and asserts the pre-fork side ALWAYS sees 0x9999 ABSENT,
post-fork ALWAYS PRESENT. PerEVMTimestamp pins the gate reads o.timestamp not the
global. Replay e2e asserts a pre-fork value transfer to 0x9999 credits a PLAIN
account WITHOUT invoking the precompile. All proven to FAIL under the old
global-read code and PASS under the fix; -race clean.
- extras + core: TestGenesisPrecompileBuilders_SkipAlwaysOn / _Skip9999 pin INFO1.
- subbalance test comment refreshed to 0x9999 + fail-closed semantics.
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.)
The host side of the 0x9999 native C<->D atomic seam, and the consensus-liveness
fix Red found.
THE BUG: Block.Accept applied the staged atomic ops to shared memory FIRST
(sm.Apply, no batch -> committed immediately) and advanced the node-local seq
marker LATER, in a separate versiondb.Commit(). atomic.SharedMemory.Apply is NOT
idempotent (duplicate Put errors). A crash BETWEEN them left shared memory
mutated but the marker unadvanced; on restart the same parent->current window
re-collected, sm.Apply re-applied the present Put, errored, and Accept returned
fatal -> the chain HALTED.
THE FIX (mirrors chains/dexvm commitAtomic): flushDexAtomic -> stageDexAtomic
stages the advanced dexAtomicSeqKey marker INTO the versiondb in-memory layer and
returns (reqs, sm). Block.Accept snapshots the versiondb (marker + lastAccepted)
via CommitBatch() and passes it to sm.Apply(reqs, batch) — its WriteAll commits
the shared-memory mutation AND the batch in ONE database write. The marker advance
and the SM mutation are now all-or-nothing: a crash applies-and-marks both or
neither, and a re-accept of an applied window sees an advanced marker -> empty
window -> no duplicate Put -> no halt. With no atomic ops the plain
versiondb.Commit() path is unchanged.
Also: core/precompile_overrider.go wires CallIndex() through the EVM accessible-
state adapter; precompile/registry/bridge.go + precompile/contract/atomic.go
forward the optional contract.AtomicState capability to luxfi/precompile.
Regression for the closed halt window lives in precompile
native_haltwindow_test.go (unit). The live-path test (real versiondb + crash
injection) is CI-only (CGO + luxfi/accel LUXCPP). Local symbol-resolution proof:
CGO_ENABLED=0 go build ./plugin/evm/ compiles clean.
Bump precompile v0.5.49->v0.5.50 (clean go.sum; 0x9999 surface unchanged) and
regenerate evm's go.sum from a clean module cache (v1.99.29's consensus/geth
hashes were poisoned by a tampered local cache). No evm code change. go mod
verify clean; build+test green.
The C-Chain DEX settles certified D-Chain fill receipts at the 0x9999 precompile
(LP-9999); it never queries a live matcher. Remove the deprecated
DexZapEndpoint -> SetBackend(NewZAPEngine()) wiring from vm.go and the
DexZapEndpoint config knob: a synchronous in-block ZAP query against the
D-Chain's moving book forks consensus (each validator observes differently-timed
fills -> divergent StateRoot, proven by chains/dexvm
TestRED_PerValidatorRelay_SplitsConsensus). ZAP survives only as the dexvm's own
build-path transport, never as a C-Chain live backend. Receipts ride in V4 swap
hookData and are verified inline (deterministic BLS).
Bump github.com/luxfi/precompile v0.5.47 -> v0.5.49 to embed the complete,
red-swarm-hardened 0x9999 receipt-settlement surface.
Task 2 (Red H2): StrictPQTimestamp was wired from config.PQ (vm.go) but the
gate was unproven inert. The canonical lux-mainnet config already sets
pq:true and upgrade.json sets strictPQTimestamp=Quasar-TS; added behavioral
tests proving NetworkUpgrades.IsStrictPQ activates (genesis when pq:true ->
StrictPQTimestamp=0; at the timestamp on running chains; Override carries it).
GUARDRAIL fix: vm.Initialize installed gethvm.AllForbidden() (=pq.Strict),
which forbids the standard bn256 precompiles 0x06-0x08 — breaking
Ethereum-compat dapps. Now installs the EVM-owned LuxStrictPQ() projection
(pq_profile.go): every classical family forbidden EXCEPT bn256. Lux's
security-critical pairing/DLOG is in CUSTOM precompiles (0x0900, 0x22
Pedersen, zkvm verifiers) + the consensus cert, each gated by the SAME
profile bit (config.PQ also pins StrictPQTimestamp=0 -> RefuseUnderStrictPQ).
Tests (b): IsStrictPQ true on a prod strict chain (genesis + timestamp +
Override paths), false when nil. (f): LuxStrictPQ admits bn256 0x06-0x08 and
still refuses every other family; differs from AllForbidden only by bn256.
Picks up the EVM-path 0x9010 cancel fix: the owner<->order cancel-auth binding
now lives in durable StateDB (PoolManager seam) instead of the backend's process
memory, so a cancel survives the EVM's ~5x multi-execution and a node restart.
Builds CGO on + off; core precompile SubBalance regression green.
CRITICAL: stateDBBridge.SubBalance type-asserts the concrete adapter else SILENTLY no-ops; the
internal contract.StateDB had AddBalance but no SubBalance and stateDBAdapter only implemented
AddBalance → a precompile debiting native value (DEX withdraw) CREDITED the caller while debiting
NOTHING = native mint (caught live: vault 100000, wallet +100000). The DEX is the first precompile
to debit native value so the path was never exercised. Added stateDBAdapter.SubBalance + regression
guard (core/precompile_statedb_subbalance_test.go); zero 'SubBalance fallback' warnings. Bump
precompile v0.5.43→v0.5.44 (PoolManager pure-ingress custody).
- luxfi/constants: pseudo-version v1.5.8-0.2026... -> real tag v1.5.8
- luxfi/upgrade: pseudo-version v1.0.1-0.2026... -> real tag v1.0.1
- precompilebind: emit MakeGenesisConfig in module template (Configurator
interface gained MakeGenesisConfig; generator was stale)
- precompilebind test: use reserved-range address 0x0200..00ff (the 0x03..
test address predates the tightened reserved ranges in modules/registerer.go)
Replaces non-canonical scale-set / org-prefixed labels with the
existing labels every arcd host registers with. Matches evo for
amd64 and spark for arm64. No new labels added.
Bump luxfi/precompile v0.5.37 -> v0.5.38 and register the two new AI-family precompiles:
- inference (0x0300..0003): deterministic on-chain int8 transformer (byte-exact across
Go/Rust/Metal/CUDA/HIP; the trustless on-chain 'thinking' path)
- modelregistry (0x0300..0002): governance-adopted versioned model-commitment registry
v0.5.38 also carries the ai (0x0300) atomic cross-chain mining methods (verifyAndMintWork/Data).
Additive + DORMANT: registering != activating. No chain runs these until its genesis/upgrade
config enables the ConfigKey (aiInferenceConfig / modelRegistryConfig). Mainnet C-Chain + zoo
do not; the Zoo L3 Beluga genesis does. Full registry init() verified clean — all precompiles
register with no address collision.
Co-authored-by: Hanzo Dev <[email protected]>
Picks up the 2026-06-06 relicensing:
- luxfi/vm v1.2.0 → Lux Ecosystem License v1.2 (was Lux Research v1.0).
License-only retag of the extracted runtime; patent reservation
preserved for runtime optimization surfaces, royalty-free for
Descending Chains.
- luxfi/sampler v1.1.0 → BSD-3-Clause (was Lux Research v1.0).
License restored to match the luxfi/node provenance (originally
extracted from node/utils/sampler).
- luxfi/staking v1.5.0 → BSD-3-Clause (was Lux Research v1.0).
License restored to match the luxfi/node provenance.
No code changes in this commit — go.mod/go.sum only.
Drop codec/wrappers import in trie_segments + statesynctest helpers.
luxfi/utils/wrappers has identical API (Packer, ByteLen/ShortLen/
LongLen, Errs) — it has been the canonical home since utils v1.1.x.
Bumps utils to v1.1.5 for the new json subpackage now used elsewhere
in the codec rip; statesync only uses /wrappers.
predicate/predicate_results.go:
Hand-rolled big-endian wire for map[Hash]map[Address][]byte:
u16 version | u32 outer | (32B hash, u32 inner, (20B addr, u32 len, bytes)*)*
Sorted by hash then address for deterministic output — block-hash
load-bearing across validators. Test suite (golden-byte multi-tx
cases) passes.
warp/messages/{codec,payload}.go:
Drops codec.Manager polymorphic registry — single payload family
(ValidatorUptime) doesn't need reflection. Fixed 44-byte layout:
u16 version | u16 type | 32B validation_id | u64 total_uptime
Parse() dispatches on type discriminator (u16) — extensible without
the codec dependency. initialize() / Bytes() / Parse() API unchanged.
Both Bytes()/Parse() public APIs preserved — consumers don't change.
Previous ExportChain returned (bool, error) and would re-export from
scratch every call, leaving partial files with no marker on Ctrl-C.
Now returns *ExportChainResult with first/last block hashes and a Status
field ("ok" | "noop" | "interrupted"). Behavior:
* Atomic JSON sentinel at <file>.sentinel (tmp+rename) updated every
1024 blocks and on terminal states.
* Idempotency: if file exists with a "done" sentinel matching
{first, last, firstHash, lastHash}, returns Status=noop without
re-exporting. Safe to call from a cron CronJob.
* Interrupt: ctx.Done() at every block boundary writes a sentinel
with Status=interrupted + HighestExported so the caller can resume.
* Block iterator still streams (no whole-chain buffering); gzip path
unchanged.
Symmetric with admin_importChain which returns *ImportChainResult, and
with the cmd/rlp-export CLI shipping alongside.
Server-side test coverage: writeSentinel atomicity, readSentinel
absent + corrupt fall-through, JSON shape pinning for both the
sentinel and ExportChainResult, exportProgressInterval stability.