precompile v0.19.22 renames the eleven modules that keyed off Verify, Threshold or
Encapsulate, so the fifty-six this registry links now answer to one convention.
Co-authored-by: Hanzo Dev <[email protected]>
This file named 41 suite packages one by one so their init() would run, because
luxd rejects an upgrade.json activation for a configKey it has never seen with
"unknown precompile config". That made it a second record of what the suite
contains, and it had drifted: aivmbridge, swap and v3 registered in the suite and
were absent here, so no Lux chain could activate them however its genesis was
written.
Importing the suite's registry instead cannot drift — that package derives its set
from the packages that actually register rather than from a list. One record, kept
in the repository that owns the thing recorded.
The list was also pinned to suite v0.19.8 while the suite had moved to v0.19.21,
so even the import alone left 14 of 56 modules linked. With the bump the surface
a luxd built from this module dispatches is 56, which is exactly what the suite
holds — measurable rather than asserted: cmd/surface prints it, and it is the
list an upgrade.json is allowed to name.
Co-authored-by: Hanzo Dev <[email protected]>
Three things, all in the import path.
The tree did not build. plugin/evm/vm_database.go names AncientShared and
FreezeThreshold on rawdb.OpenOptions; luxfi/geth v1.20.2 has neither and v1.20.3
has both, so a clean checkout failed to compile before any change. Pinned to
v1.20.3.
The blocks-per-round was the literal 2500 inside importBlocksFromFile, so the
only way to change it was to edit and rebuild. It is a parameter now:
admin_importChain takes an optional count, --import-chain-batch sets it for the
startup path, and defaultImportBatch stands in when neither says. The right
number is a property of the machine -- a host with memory to spare amortises one
InsertChain call over far more blocks -- so it does not belong in the source.
Both importers wrote their own copy of the same closure to advance the accepted
marker and commit. There is one persistAcceptedBlock now and both hand it to the
importer. What genuinely differs between them -- the startup path reconciles the
export frontier and syncs consensus state afterwards, because it runs before the
engine is live -- stays where it belongs.
Co-authored-by: Hanzo Dev <[email protected]>
github.com/luxfi/utils is now github.com/luxfi/util. Import paths and the
module requirement follow the rename, and the luxUtils alias becomes luxUtil.
The old module stays as an indirect requirement that published dependencies
still pull in.
Co-authored-by: Hanzo Dev <[email protected]>
The C-Chain runs as a plugin, so the ZAP server that answers the node lives in
this binary, not in luxd. A node bump alone leaves the old server in place.
v1.3.8 refuses a VM that returns neither a block nor a reason instead of
dereferencing it, and stops reporting an unnamed failure with the code that
means success.
Co-authored-by: Hanzo Dev <[email protected]>
The 0x9999 swap intent gains the operation segment (market, side, limit, size)
appended to the unchanged 69-byte value head. Without it the D-Chain could
consume an intent and credit the taker, but had nothing to place an order with,
so no swap ever crossed and no settlement object was ever produced for
ImportSettlement to consume.
No change to this repo's own code: the D->C settlement object it authenticates
at Block.Verify is still the canonical 69 bytes. Only the C->D leg widened, and
only the C->D leg instructs.
Co-authored-by: Hanzo Dev <[email protected]>
Picks up the cross-chain atomic capability across the ZAP plugin boundary.
The EVM runs out of process, so its rebuilt Runtime had SharedMemory nil and
BCLookup absent; every DEX 0x9999 settlement reverted "requires the
cross-chain atomic capability" and the Accept-time flush of staged C->D
exports silently no-opped. vm v1.3.6 dials the node's per-chain atomic
server at Initialize and installs both.
precompile v0.19.5 additionally tags C->D swap intents with the discovery
trait the D-Chain enumerates by, without which a real intent is invisible to
D's import drive even once the transport works.
accel v1.2.4 -> v1.3.1 is forced by MVS through precompile, not chosen:
pinning it back drags precompile to v0.19.3.
Co-authored-by: Hanzo Dev <[email protected]>
node v1.36.35 bumped luxfi/vm to v1.3.3 for a chain.State map race. The C-Chain
does not run in the luxd binary — it is a plugin built from THIS module, and
this module still pinned v1.3.1. So the fix shipped in the process that does not
verify blocks and was missing from the one that does. Read straight off the two
binaries in a v1.36.35 pod:
/luxd/build/luxd github.com/luxfi/[email protected]
/luxd/build/plugins/mgj786NP7… github.com/luxfi/[email protected]
It fired in production on lux-testnet luxd-0 within 5 minutes of the roll:
fatal error: concurrent map writes
github.com/luxfi/vm/components/chain.(*BlockWrapper).Verify
luxfi/[email protected]/components/chain/block.go:44
github.com/luxfi/vm/rpc.(*zapVMServer).handleBlockVerify
luxfi/[email protected]/rpc/vm_server_zap.go:580
v1.3.3 is exactly the fix for that line — it takes state.blocksLock around
verifiedBlocks[blkID] = bw, which v1.3.1 wrote unlocked from every concurrent
ZAP RPC handler.
Worth flagging separately: the plugin dies while luxd survives, so the pod stays
Ready with restarts=0 and info.isBootstrapped(C) keeps answering true while
eth_blockNumber on /v1/bc/C/rpc times out. The readiness probe cannot see this
failure class; only a per-node tip probe can. Recovery is a pod delete.
go build ./plugin/... ./core/... clean, go vet clean, plugin/evm tests pass.
Co-authored-by: Hanzo Dev <[email protected]>
gpu v1.0.1/v1.1.0 declare `#cgo pkg-config: lux-gpu` while including
<lux/gpu.h>. Those are two different libraries with confusingly similar names:
luxgpu.pc unified GPU acceleration, -lluxgpu, Cflags -I${includedir}
lux-gpu.pc the DEX order matcher, -llux_gpu, Cflags -I${includedir}/lux
Under lux-gpu the include resolves to .../include/lux/lux/gpu.h, so every cgo
build failed with "fatal error: lux/gpu.h file not found" — the arm64 stall
signature documented in CLAUDE.md. libluxgpu.dylib is also what actually
exports _lux_gpu_ecrecover_batch.
gpu v1.1.2 already declares luxgpu; only the pin was stale. Builds clean with
CGO_ENABLED=1.
Co-authored-by: Hanzo Dev <[email protected]>
These hashes were recorded from direct VCS fetches made while GOPRIVATE covered
github.com/luxfi/*, which switches off the module proxy and checksum
verification together. Tags moved afterwards, so what was written down no longer
matches what sum.golang.org holds, and the build refuses to verify.
proxy.golang.org still serves the originally-published bytes and those verify
against the log, so dropping only the disagreeing lines and re-tidying restores
the build with no version change. Lines that agree were left alone — this is not
a regenerated go.sum.
The mismatch followed the machine, not the repository: the same commit built
wherever the cache was clean. Any machine or CI runner that fetched luxfi
modules under the old GOPRIVATE holds the same poisoned entries.
Co-authored-by: Hanzo Dev <[email protected]>
eventstream -> v1.7.8, service/s3 -> v1.97.3 (minimum patched versions), plus
the luxfi escape versions that keep this module off the deleted geth v1.16.x
tags (crypto v1.20.2, constants v1.6.2, warp v1.24.1, ids v1.3.2, p2p v1.22.1).
Verified against a measured baseline: go test ./... is 58 ok / 7 fail BOTH with
and without this change — identical, so the failures are pre-existing.
Chasing these one at a time was whack-a-mole — every repair surfaced the next
moved tag (age, pq, threshold, zap, consensus, constants, sdk, keys…). This is
the systematic pass instead: every distinct luxfi/hanzoai module@version in the
workspace (692 of them) was resolved against proxy.golang.org + a direct fetch,
and any go.sum line disagreeing with what is actually served was corrected.
Not an attack, and checked rather than assumed: for each case the proxy and a
direct fetch AGREE with each other and differ from the recorded hash. Two
independent transports agreeing means nothing is rewriting bytes in flight —
the tags moved at source. sum.golang.org still holds the original, but
GOPRIVATE covers github.com/luxfi/* with GOSUMDB=off, so our own modules never
consult the checksum DB and a moved tag splits consumers silently instead of
failing at publish time.
Only the recorded hash changes. No selected version moves, and `go mod tidy`
was deliberately NOT run, so nothing is upgraded as a side effect.
Verified: `go list -m all` resolves with no checksum error.
The durable fix is upstream: treat a published version as immutable. Cut
x.y.z+1 instead of moving a tag — with GOSUMDB off, no consumer can detect it.
Picks up dex 34c2ad2 / v1.14.2, which gates the secp256k1 Metal anchor behind
dexgpu so a default darwin build links: previously the anchor referenced
secp256k1_ecrecover_address_batch_metal while the lib that defines it was only
linked under -tags dexgpu, breaking the link of the C-Chain plugin binary that
pulls dex/pkg/dex. `go build ./plugin/` now links the 68 MB plugin clean in the
default build.
Co-authored-by: Hanzo Dev <[email protected]>
api v1.0.16 added Capabilities uint64 to zap InitializeResponse (Quasar-export
capability bit). The plugin built at v1.0.15 answered Initialize 8 bytes short,
so node-side Decode died with unexpected EOF — the cross-process ZAP e2e
(node vms/rpcchainvm/zap TestCEvm_DialsZAPdbServer) caught it. Additive field;
plugin leaves the bit clear until the EVM wires CapQuasarExport.
Co-authored-by: Hanzo Dev <[email protected]>
Fresh-start block-production stall root cause. The node-side notification
bridge (node/chains/manager.go) calls VM.WaitForEvent in a loop with a
non-cancellable context.Background(), starting BEFORE SetState(NormalOp)
initializes vm.builder. The first WaitForEvent therefore parks in the
builder==nil select { ctx.Done | stateSyncDone | shutdownChan } and, since the
context never cancels and state-sync is off, blocks FOREVER -- the serial
bridge loop never re-calls WaitForEvent to observe the now-ready builder, so no
PendingTxs ever reaches the engine and no block is ever built.
ava avoids this with common.NotificationForwarder + cn.OnChange=CheckForEvent,
which cancels the outstanding WaitForEvent and re-subscribes on VM state change.
Lux replaced that with the hand-rolled bridge and lost the recovery path.
Fix (self-contained, VM-side): add builderReady chan, closed once by
onNormalOperationsStarted after vm.builder is set, and add a select case so a
WaitForEvent that parked early re-fetches the ready builder and proceeds.
Makes WaitForEvent correct for any caller, cancellable or not.
go.sum: re-record luxfi/age,pq checksums after upstream re-tag (verify ON).
Picks up the enable-everything custom-precompile change (RefuseUnderStrictPQ
removed from all verify-only precompiles; fflonk fail-closed; accel byte-
identity; dex/stableswap/graph determinism). Pairs with this module's standard-
precompile enable so the full builder surface is live in one plugin build.
warp v1.23.0 = typed finality-evidence model + Pulse->Corona relabel.
No evm consumer references the renamed symbols; warp/, warp/validators/, and
precompile/contracts/warp/ tests all pass. Pure dependency bump.
Rename across the evm warp backend and the warp precompile: warp.Core ->
warp.Message, NewCore -> NewMessage, ParseCore -> ParseMessage, the .Core field
-> .Message, and local core/msgCore/testCore vars -> msg/testMsg. deps: warp
v1.21.0 -> v1.22.0, precompile v0.11.0 -> v0.12.0.
geth's core/types and core/vm imports are untouched — the rename is scoped to
the warp message type, never the geth core package. ./warp/... and
./precompile/contracts/warp/... green, including TestWarpWrongNetworkID.
Track the upstream rename across the evm warp backend
(warp/{service,backend,verifier_backend}) and the warp precompile
(precompile/contracts/warp/{config,contract,contract_warp_handler},
precompileconfig): SignedCore->Core, WarpEnvelope->Envelope.
Identifiers only — the digest D, ZAP c14n bytes and BLS Beam / Corona Pulse /
ML-DSA signing domains are unchanged. ./warp/... and
./precompile/contracts/warp/... green, including the 10k-validator predicate
sign->aggregate->verify.
Hard-fork the last evm warp consumers onto the canonical ZAP message format
(one and one way only). warp v1.20.0 deleted UnsignedMessage; the signed
subject is now warp.SignedCore (D = keccak256(coreDST ‖ zap_c14n)) and the
signed wrapper is warp.WarpEnvelope. The BLS Beam signs warp.BeamSigningBytes(D).
deps: warp v1.19.5 -> v1.20.0, precompile v0.5.59 -> v0.10.0.
- warp/{backend,verifier_backend,service}: UnsignedMessage -> SignedCore;
NewMessage -> NewWarpEnvelope; sign/verify over BeamSigningBytes(core.ID()).
- precompile/contracts/warp: ParseMessage -> ParseWarpEnvelope; .UnsignedMessage
-> .Core; .Signature.(*BitSetSignature) -> .Beam (concrete). VerifyPredicate
verifies the Beam over D with exported primitives (Bits + bls +
BeamSigningBytes) because the precompile quorum denominator must include
keyless validators, which warp.VerifyEnvelope/GetCanonicalValidatorSet reject.
- precompileconfig.WarpMessageWriter.AddMessage(*SignedCore) (mirrors precompile v0.10.0).
- plugin/evm/vm.go: warpVerifierAdapter over *SignedCore; collapse the duplicate
external-warp import alias (warptypes -> luxwarp).
- remove redundant warp/{aggregator,bls_adapter,signer_adapter}.go: provably-dead
duplicates of warp.SignatureAggregator / warp.NewSigner (zero production refs).
- tests migrated to ZAP; fixed a pre-existing offchain message-key test bug;
revived the predicate suite (validator state injected via Runtime, the
production path, replacing the decoupled WithValidatorState key).
Verified: go build ./... green; go test ./warp/... ./precompile/contracts/warp/...
green (incl. 10k-validator TestWarpPredicate). grep UnsignedMessage = 0.
chains v1.3.19's dexvm/registry references AssertNoForbiddenAssetRefs,
looksLikeASCIITickerID, toHex and fromHex, but the file defining them
(forbidden.go) was dropped in that re-tagged release, so plugin/evm failed to
build (undefined symbols). The helpers are present again from v1.3.21; bump to
the latest v1.3 patch, v1.3.22.
MVS pulls the patch-level deps chains v1.3.22 requires: consensus v1.25.29,
crypto v1.19.26, dex v1.5.20 (zap v0.8.11 indirect). All x.x.x+ patch bumps, no
minor/major. Full repo builds; core/params/snapshot suites green and unchanged.
The re-tag automation staled multiple luxfi module hashes ([email protected], [email protected],
[email protected], ...) so evm would not build/test (SECURITY ERROR). Re-recorded go.sum
fresh (GOPRIVATE bypasses sumdb for luxfi; public deps re-verified). Also removed a
stale DurangoTimestamp from a geth ChainConfig literal in config_test.go (the field
moved to extras.NetworkUpgrades). Unblocks the build. NOTE: params/extras (the
genesis-active StrictPQ work) passes; full evm green still needs the genesis-hash
regen (0x9999 genesis-active) + pre-existing failures (TestUpstreamParamsValues
param drift, snapshot geth-limitation) addressed — separate foundational pass.
precompile v0.5.58 carries the 0x9999 value-path soundness floor (Block-STM
access-set, decentralized halt, in-state-vault/EVM-Call resolution; contains
the gate-b commit 23f49b6) and runtime v1.1.3 the GovernanceController seam
the value path reads. Builds standalone without go.work; gate-b closed
(evm no longer links the pre-fix precompile v0.5.57).
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).
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.
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.)
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.