Mirrors the P-chain gate (vms/platformvm/txs/mempool) onto the X-chain
(xvm) mempool. The X-chain wraps every credential in fxs.FxCredential,
so the gate unwraps the inner verify.Verifiable before handing the
slice to auth.EnforceCredentialPolicy — the policy package only sees
the canonical *secp256k1fx.Credential type.
Same opt-in shape as the P-chain wiring: SetAuthPolicy installs the
ChainSecurityProfile + ClassicalCompatRegistry; without a profile the
gate is bypassed.
Three integration tests cover: strict-PQ + no registry refuses
classical, no policy admits classical, strict-PQ + empty registry
refuses classical (the all-allow-listed contract is that the registry
names the allowed addresses; an empty registry admits nothing).
Wires the canonical credential-policy gate (vms/txs/auth) into the
P-chain mempool. Adds an opt-in setter so existing callers that have
not migrated their construction path observe no behavioural change —
without SetAuthPolicy, the gate is bypassed and every tx the legacy
path accepted is still accepted.
Once a chain pins a non-nil *config.ChainSecurityProfile via
SetAuthPolicy, every Add path runs through auth.EnforceCredentialPolicy
before the underlying mempool sees the tx. A classical
secp256k1fx.Credential under RequireTypedTxAuth=true returns
auth.ErrLegacyCredentialUnderStrictPQ.
The originator is left at ids.ShortEmpty here because P-chain txs do
not bind a single canonical "from" address (the input owners can name
many keys). Chain builders that supply a ClassicalCompatRegistry whose
IsAllowed depends on tx-level context layer that resolver on top of
this gate.
Three integration tests cover: strict-PQ + no registry refuses
classical, no policy (legacy default) admits classical, classical-
compat profile admits classical. The full P-chain mempool suite
(TestBlockBuilderMaxMempoolSizeHandling et al.) is unchanged.
Adds the canonical credential-policy gate that the P-chain and X-chain
mempools use to enforce a strict-PQ ChainSecurityProfile at admission
time. One package, one entry point (EnforceCredentialPolicy), one
error (ErrLegacyCredentialUnderStrictPQ).
- ClassicalCompatRegistry: read-only allow-list interface. Names a
bounded set of legacy operator addresses that may still post
classical secp256k1 credentials on a chain that otherwise pins
RequireTypedTxAuth=true. The chain's governance pathway is the
only writer; the mempool only reads.
- NewStaticClassicalCompatRegistry: frozen registry constructed from
a fixed []ids.ShortID slice. Useful for tests and for genesis-
pinned allow-lists before the governance path is online.
- EnforceCredentialPolicy: idempotent gate, branches on
profile.RequireTypedTxAuth:
false → admit unconditionally (classical-compat profile)
true → walk creds; if any is *secp256k1fx.Credential and the
originator is not in the registry, return
ErrLegacyCredentialUnderStrictPQ.
10 tests cover: nil profile (ErrNilProfile, programmer error), classical
profile admits, strict-PQ admits PQ-only creds, strict-PQ refuses
classical without registry, strict-PQ refuses originator not in
registry, strict-PQ admits allow-listed originator, mixed creds with
one classical refuses, empty creds always admits (syntactic verifier's
job to require ≥1), nil-safe staticRegistry, distinguishes addresses.
Wiring into platformvm/txs/mempool and xvm/txs/mempool lands in the
follow-up commits.
Re-exports the canonical ML-DSA-65 (FIPS 204) UTXO feature extension
from github.com/luxfi/utxo/mldsafx so platformvm and xvm callers depend
on a single import path:
github.com/luxfi/node/vms/mldsafx
One feature extension, one import path. The wire types (Credential,
TransferInput/Output, MintOutput, MintOperation, OutputOwners, Input,
Fx, Factory) and the security-level constants are type aliases against
the upstream package — adding mldsafx as a node-side bridge introduces
zero runtime code.
This is the foundation the P-chain and X-chain mempool admission gates
(vms/txs/auth) and the strict-PQ tx variants build against.
The prior commit referenced luxfi/consensus v1.23.3, but that tag was
already in use on the remote. Retagged the new ChainSecurityProfile
ValidatorSchemeID work as v1.23.4 and update node's go.mod/go.sum to
match. luxfi/ids v1.2.10 is unaffected.
NodeIDScheme enum (MLDSA65=0x42 canonical, Secp256k1=0x90 classical-
compat-unsafe only). NodeID derivation now domain-separated via
SHAKE256-384("LUX_NODE_ID_V1" || chain_id || scheme || pubkey).
Wire encoding includes a leading scheme byte so the receiver can
verify without trusting the profile alone.
Strict-PQ profile rejects secp256k1 NodeIDs.
Classical-compat profile accepts both (transition path).
Migration: hardfork activation switches strict-PQ chains from
mixed-scheme to ML-DSA-only at a configured activation block.
network/peer/scheme_gate.go is the single primitive consumers funnel
inbound NodeIDs through: SchemeGate.Classify(nodeID, scheme, height,
site) returns a TypedNodeID once the chain policy admits the pair.
The ActivationHeight field implements the hardfork transition window;
ClassicalCompatUnsafe mirrors the operator opt-in flag (refused under
strict-PQ regardless, honoured on permissive).
Existing 20-byte ids.NodeID array stays byte-identical for storage /
map keys / codec; the scheme byte travels alongside it on the wire
via ids.TypedNodeID. Consumers of the 20-byte form (peer/upgrader,
proposervm block proposer, platformvm validator registry, mempool
sender) continue to work unchanged at the storage layer; the
SchemeGate boundary is what stamps the scheme byte and runs the
cross-axis check.
Bumps luxfi/ids v1.2.9 -> v1.2.10 (NodeIDScheme, TypedNodeID,
DeriveMLDSA, FullDigest, error surface) and luxfi/consensus
v1.23.2 -> v1.23.3 (ValidatorSchemeID accessor, AcceptsValidatorScheme,
ErrValidatorSchemeMismatch).
Tests:
- TestSchemeGate_NewSchemeGate_RejectsNilProfile
- TestSchemeGate_StrictPQ_AcceptsMatchedScheme
- TestSchemeGate_StrictPQ_PostActivation_RejectsClassical
- TestSchemeGate_StrictPQ_PreActivation_AcceptsBothSchemes
- TestSchemeGate_StrictPQ_PreActivation_RejectsClassicalAfterCutover
- TestSchemeGate_Permissive_AcceptsClassicalUnderUnsafeFlag
- TestSchemeGate_RejectsUnknownSchemeByte
- TestSchemeGate_PinsProfileScheme
- TestSchemeGate_SiteTagIncludedInError
Patch-bump: v1.26.9 -> v1.26.10.
Replaces classical X25519/ECDH peer handshake with ML-KEM-768 (default)
and ML-KEM-1024 (high-value validator/DKG channels). Node identity is
signed with ML-DSA-65 over a TupleHash256-bound transcript.
cSHAKE256 derives the AEAD session key with customization
"LUX_NODE_AEAD_V1", binding profile_id, chain_id, both nodes' ML-DSA
public keys, and the shared KEM secret.
DKG channels in pulsar/pulsar-m force ML-KEM-1024.
Strict-PQ profile refuses peers offering X25519Unsafe KEM.
Patch-bump.
Only P-Chain (chain manager startup) and X-Chain (UTXO infra) are
required primary-network chains. C-Chain (EVM), D-Chain (DEX),
B-Chain (Bridge), T-Chain (Threshold) — and any subnet blockchain —
must be explicitly tracked via --track-chains. A validator gets exactly
the topology its operator asks for, nothing more.
Pre-fix: every primary-network blockchain in genesis was created
unconditionally because `constants.PrimaryNetworkID != tx.ChainID`
short-circuited the TrackedChains gate. <tenant> validators (no
C-Chain plugin shipped) ran into "failed to initialize VM: parsing
genesis: unexpected end of JSON input" on every health check.
Replace the primary-network bypass with a per-VM check: only
tx.VMID == constants.XVMID skips the gate. Everything else (including
C-Chain on primary network) goes through TrackedChains.
Two related fixes that make chain tracking explicit per-validator:
1. config/config.go: drop the silent default
`if TrackedChains == nil { TrackAllChains = true }`. That made any
node without an explicit --track-chains list try to spin up every
chain in P-chain state, including ones whose VM plugin wasn't
loaded. The setting was a footgun. Empty TrackedChains now means
"track only P/X (built-in)". TrackAllChains stays as an explicit
opt-in escape hatch.
2. chains/manager.go: when buildChain returns ErrNotFound (VM plugin
not registered for this chain's vmID), treat it as the validator's
"I don't validate this one" signal — log info, mark the slot
bootstrapped, and return WITHOUT registering a per-chain failing
health check. The chain stays in pending state for hot-load.
Previously this registered a permanent 503 on /ext/health for the
chain-alias, which made kubelet liveness probes kill any pod that
didn't ship plugins for every chain in the primary genesis. Now
validators participate per-plugin: load liquid-evm/dex/fhe → those
chains validate; don't load Lux's upstream EVM plugin → C-Chain
stays in pending without crashing the node.
Real failures (e.g. genesis decode error, db corruption) still register
the failing health check — those are operator misconfigurations, not
deliberate opt-outs.
Future: a Lux-signed featured-chains.json + --track-featured (default
true) would make this opt-in safer at scale — limits the DDoS surface
of unbounded chain creation while still letting validators earn yield
on the curated set out of the box.
Adds platform.getChains alongside platform.getNets — same wire shape
(IDs in, list of {ID, ControlKeys, Threshold} out), names that match
the user-facing concept ("chain") instead of the internal "net" /
"subnet" jargon. APIChain replaces APINet, ClientChain (alias for
ClientNet) keeps drop-in source compatibility.
Why now: GetNets was already marked deprecated via the "deprecated
API called" log line, but no canonical replacement existed — callers
had to keep calling the deprecated method. This commit lands the
replacement so the bootstrap binary's chain idempotency check
(EnsureChainNetwork → list chains, match owner set) can target the
canonical name.
GetNets stays available indefinitely for wire-protocol compatibility.
GetChains delegates to GetNets internally so the two methods can
never diverge — bug-fix one, both fix.
Test pinned in service_test.go alongside GetNets test (existing file
has //go:build skip but the test is documentation for the contract).
createCChainIfNeeded is the one-time recovery path for the historical
mainnet migration where post-merge geth state was imported under a fixed
blockchainID (no CreateChainTx). On every other launch the function
either silently no-ops on the missing data dir or — worse — risks
touching the filesystem before the data dir exists during a fresh
genesis. Gating it behind LUX_MIGRATE_CCHAIN=1 keeps the migration path
exactly where it belongs: opt-in, run once, never again.
The wire protocol uses two flags on responses:
MsgResponseFlag (0x80) — set on EVERY response (success or error)
MsgErrorFlag (0x40) — set ONLY on error responses
The transport's readLoop already converts MsgErrorFlag responses into a
non-nil err out of Conn.Call, so by the time the client checks respType
the response is guaranteed successful. Treating MsgResponseFlag as the
"is this an error?" predicate fired on every successful Initialize and
rendered the binary InitializeResponse payload (LastAcceptedID, parent,
height, bytes, timestamp) as if it were an error string, producing
"vm error: <unprintable bytes>" and crashing chain creation despite the
plugin reporting "VM initialized successfully".
Symptom in the field: lqd repeatedly logged
failed to create chain on net 11111111111111111111111111111111LpoYY:
failed to initialize VM: zap initialize: vm error: \x00\x00\x00 ...
while the plugin's own log read "ZAP VM initialized successfully" and
"Chain initialized successfully". Health check C reported permanent
failure, eth_getBalance returned 404, no blocks were ever produced.
Fix: drop the spurious MsgResponseFlag check and strip both flags before
comparing the message type so that any well-formed response is accepted.
Regression test in client_initialize_test.go pins both halves of the
contract: a success response is decoded (lastAcceptedID round-trips),
and a server-side error is surfaced through err with the original
message intact.
Picks up luxfi/genesis@2b16f454 — adds operator-overridable C-Chain
genesis at the embedded-loader layer. Pairs with f8ddc426 (this repo's
LUX_AUTOMINE_CCHAIN_GENESIS_PATH for the automine single-node path).
With both env vars wired, downstream networks can reuse stock lqd
without forking configs/network/cchain.json or patching this binary.
The automine path embeds the C-Chain genesis bytes into the primary-
network genesis at boot. Until now that genesis was a hardcoded constant
(chainId 31337) — fine for stock lqd local dev, but downstream networks
(<tenant> at chainId 8675312, etc.) had no way to override without
patching the binary. The chain-config-dir scan happens AFTER the EVM
plugin has already initialised from the embedded bytes, so dropping a
genesis.json into configs/chains/C/ is silently ignored.
Adds LUX_AUTOMINE_CCHAIN_GENESIS_PATH: when set, lqd reads the JSON at
that path and embeds it as the C-Chain genesis. Unset → unchanged
behaviour (chainId 31337 default).
The saved AutomineNetworkConfig also mirrors the resolved value so the
on-disk record is true to what was embedded.
Match the OPT-IN comment at genesis/builder/builder.go:617. The
initChainManager path was hard-erroring when the platform genesis
had no constants.EVMID chain — that contradicted the documented
opt-in behaviour and forced consumers (<tenant> Network) to bake
a placeholder EVM into the primary genesis just to satisfy the
node init.
With this change, networks that register their own EVM as a
dedicated chain via platform.createChainTx (the canonical L2-on-Lux
or sovereign-chain flow) leave the platform genesis EVM-less and
cChainID stays ids.Empty. Chain manager + aliasing already accept
the empty ID — no further changes needed.
Picks up the localnet genesis regen from LIGHT_MNEMONIC. lux/node's
default genesis for network-id=1337 now funds the same 100 P/X wallets
the lux/node + <tenant>/node toolchains derive against, so local
dev no longer has to set --genesis-file or hand-edit allocations.
The struct `zapwire.InitializeRequest.LuxAssetID` is generated from a
.proto in luxfi/api — renaming it requires regenerating the wire
bindings, bumping luxfi/api, then bumping luxfi/node to that. Out of
scope for this rename pass; revert the one Go-side assignment so the
build resolves against the existing wire type. The local variable
already reads from `Context().XAssetID` so the data path is consistent;
only the on-the-wire field name lags.
The field has always semantically been the X-Chain asset ID — the JSON
tag is already `xAssetID`. The Go name `LuxAssetID` was a holdover from
when "LUX" was the only asset name on the chain, which created confusion
when readers expected `XAssetID` to mirror `XChainID` / `CChainID` /
`DChainID` (all of which already used the chain-letter naming).
Renamed across config, node, vms/platformvm, vms/rpcchainvm/zap and the
static_service API request types. JSON tag and protobuf wire fields are
unchanged, so on-disk configs and inter-process plugin RPC are
backwards-compatible — only Go callers see the new name.
X-Chain genesis at builder.go:603-613 references 9 FxIDs (secp256k1fx,
nftfx, propertyfx, mldsafx, slhdsafx, ed25519fx, secp256r1fx, schnorrfx,
bls12381fx) but chains/manager.go only registered the first 3. Chain
init failed with "fx qC5JEjDhfXD66cGuhtiL3Lkka2SgZyht74nHVQFmYFyDiLqXe
not found" — that ID encodes 'mldsafx' which the genesis builder added
when post-quantum support landed but wasn't paired with a manager-side
registration.
Add all 6 missing factories so any FxID emitted by the genesis builder
loads at chain create time. The fxs map is now structurally identical
to the X-Chain FxIDs slice — drift between the two will become a
review-time diff in chains/manager.go vs. genesis/builder/builder.go.
Anchor /lux + /luxd binary patterns at repo root so the gitignore
rule doesn't accidentally match every directory called "lux" deep
in the tree (which was excluding vms/components/lux that the
previous commit tried to land).
Add the actual UTXO type tree files so v1.26.5 ships with the
package contents external consumers (liquidity/network-bootstrap
etc) need.
* vms/components/lux: restore the X-Chain UTXO type tree from
v1.24.29. External consumers (the tenant repo/network-bootstrap,
PlatformVM tx builders, AVM tx builders) import this exact type
tree to interop with the X→P export path. Documented in CLAUDE.md
as a known anomaly pending #58 consolidation; until that lands the
package must be present.
* vms/thresholdvm/thresholdvm.go: re-export shim mirroring vms/dexvm.
The Threshold (FHE / MPC) VM moved out of node/vms/thresholdvm
into github.com/luxfi/chains/thresholdvm. Callers (liquidity/fhe
plugin etc) keep building unchanged.
No on-the-wire behaviour change.
Two compatibility seams that downstream consumers need post-Z-Wing
restructure:
1. proto/pb/* stubs (build tag grpc) — every grpc-tagged file under
proto/<name>/<name>_grpc.go (and the legacy proto/rpcdb/rpcdb_grpc.go)
imports proto/pb/<name>. Those dirs were empty (git ignores empty
dirs), so consumers' `go mod tidy` walked the imports under the
grpc tag and failed to resolve the package. Default builds use
ZAP and never enter these stubs; the grpc-tag path now compiles
cleanly even though the protobuf types themselves still need
regeneration when someone actually wants the gRPC transport.
2. vms/dexvm/dexvm.go — re-export shim for the canonical chains/dexvm
package. The DEX VM moved out of node/vms/dexvm into
github.com/luxfi/chains/dexvm; existing callers (the tenant repo/
node, etc.) keep building unchanged.
Default build clean; grpc-tagged build also resolves. No on-the-wire
behaviour change in the ZAP default path.
* consensus v1.23.2 — pulls threshold v1.6.7 with the LSS-Pulsar
adapter (PulsarConfig, DynamicResharePulsar) needed by the epoch
manager's resharing path.
* New proto/zap/rpcdb package — pure-Go wire types for the database
RPC, no protobuf. Mirrors the proto/zap/{vm,p2p,sync,platformvm}
pattern.
* New proto/rpcdb/rpcdb_zap.go (build tag !grpc) — full ZAP-native
DatabaseClient + DatabaseServer + Register hook, implements
database.Database against any Transport. The host's underlying DB
is luxfi/database (zapdb in production); the protocol shape is
identical regardless of which engine the host runs.
* Renamed the existing rpcdb.go to rpcdb_grpc.go so the two
variants are mutually exclusive: build with no tag (ZAP default)
or with `-tags=grpc` (protobuf path), never both.
examples/multi-network/multi-network-poc.go: drop duplicate-key map
entry where Q-Chain was illustrated as a separate network — Q-Chain
shares the primary network ID per LP-134, see comments on
MainnetID entry.
Net effect: full default build is ZAP-only PQ-secure-channel-aware,
RPC over a Z-Wing-encrypted ZAP transport, no gRPC/protobuf in the
critical path.
Pulls in:
* luxfi/constants v1.5.2 — LocalID/CustomID semantic split, IsCustom()
* luxfi/genesis v1.9.2 — same split mirrored at the configs layer
* luxfi/zwing v0.5.2 — full PQ secure channel (X-Wing + ML-DSA-65 +
ChaCha20-Poly1305) with cross-language
wire-byte interop verified against Rust /
Python / TypeScript ports
* luxfi/api v1.0.10 — zap.NewListener seam (used by zwing.ListenZAP)
* luxfi/netrunner v1.18.1 — PQ-mandatory zapwire control RPC + same
LocalID rename
* luxfi/geth v1.16.87 — verkle.Fr type fix (drop bandersnatch import)
* luxfi/consensus v1.23.1 — banderwagon path move tracked
Source-side rename in this repo: every constants.CustomID call was
"the local 1337 dev network", so they all become constants.LocalID
(both the upgrade-config validation switch and the test fixtures).
The old constants.CustomID literal is now 0 — used as the explicit
"this is a user-defined custom network" sentinel, separate from the
LocalID dev network.
Plus a clarifying comment on the upgrade-config switch noting which
network IDs are permitted to override their upgrade schedule (any
non-well-known ID, including DevnetID/UnitTestID and any genuinely
custom user-defined network ID).
Z-Wing dialer + tests still green; all network/dialer, config,
config/node, config/spec, and genesis/builder tests pass.
Introduces ZWingDialer + ZWingListener which wrap any underlying
net.Conn (TCP, hostname, RNS mesh link, Unix socket, in-memory pipe)
with the canonical Lux PQ secure channel:
IETF X-Wing KEM (X25519 + ML-KEM-768)
Hybrid Ed25519 + ML-DSA-65 identity, signed transcript
ChaCha20-Poly1305 with sequence-numbered nonces
Z-Wing's contract is "any net.Conn" — the same secure channel rides
unchanged on TCP today and on the existing RNS transport tomorrow
without a per-transport rewrite. The legacy LP-9701 in-RNS-link crypto
(rns_link.go) stays in place during the transition; new p2p paths
should layer ZWingDialer over the EndpointDialer instead of relying on
LP-9701's inline encryption.
Adds:
network/dialer/zwing_dialer.go ZWingDialer + ZWingListener
network/dialer/zwing_dialer_test.go 5 e2e tests covering:
- missing-identity rejection (dialer + listener)
- real TCP listener + Z-Wing handshake + payload round trip
- Wrap() over an arbitrary net.Conn (net.Pipe stand-in for RNS)
- identity mismatch (MitM defence)
- DialEndpoint over an Endpoint (works for IP, hostname, future RNS)
Bumps:
github.com/luxfi/zwing v0.5.2 (full FIPS 204 PQ stack, cross-language
wire-byte interop with Rust/Py/TS)
github.com/luxfi/api v1.0.10 (NewListener seam used by zwing.ListenZAP)
luxd-side wiring (node.go construction with a loaded LocalIdentity) is
the next follow-up; this commit lands the seam without behavioural
changes to the existing dialer interface.
Lux's primary wire protocol is ZAP (Zero-Copy App Proto). The protobuf
definitions and generated .pb.go files in proto/ trees were vestigial:
- _grpc.pb.go gRPC service stubs were already gated behind
//go:build grpc and not part of the default build
- .pb.go message types had ZAP equivalents (*_zap.go) on every active
code path
Default build was already 100% ZAP. Deleting the .proto + .pb.go +
_grpc.pb.go files removes dead code without functional change.
Verified clean build of node, p2p, vm after deletion.
Remove unused bloom filter, test helpers, metrics scaffolding, and
linearizable VM wrapper. Trim stale go.sum entries. Add genesis builder
helpers and xvm FX/genesis initialization.
Blocking (would cause production incidents):
- env var prefix: LUX_* → LUXD_* (27 vars). Viper reads only LUXD_*;
LUX_* silently ignored → node would start with default config on every env.
- admin API enabled + sybil disabled defaults: explicit LUXD_API_ADMIN_ENABLED=false
and LUXD_SYBIL_PROTECTION_ENABLED=true in base ConfigMap.
- bootstrap peer config absent in new overlays: added LUXD_BOOTSTRAP_IPS /
LUXD_BOOTSTRAP_IDS keys to base ConfigMap; overlays/operator populate per-env.
Defense-in-depth additions:
- ServiceAccount `luxd` with automountServiceAccountToken: false (no API access).
- PodDisruptionBudget maxUnavailable=1 (works at 5 → 100 validator scales).
- NetworkPolicy: ingress 9631/TCP from any (P2P), 9630/TCP cluster-only
(HTTP), 9090/TCP monitoring-namespace only (metrics).
- podAntiAffinity preferredDuringScheduling by hostname — spread across nodes.
- updateStrategy: OnDelete — operator drains one pod at a time.
- podManagementPolicy: Parallel — validators start in any order.
Swarm verdict was NO-GO until these fixes. Addresses red critical #1 (admin
API), #2 (sybil), high #4 (gateway bypass via direct LB), medium #7 (no
NetworkPolicy/RBAC), and scientist blockers on env prefix + bootstrap.
Single ghcr.io/luxfi/node image now includes luxd + EVM plugin +
all 11 chain VM plugins (aivm, bridgevm, dexvm, graphvm, identityvm,
keyvm, oraclevm, quantumvm, relayvm, thresholdvm, zkvm) built from
github.com/luxfi/chains.
Dockerfile: added plugin-builder stage that clones luxfi/chains and
builds each VM as a static binary placed at the CB58-encoded VM ID
path under /luxd/build/plugins/.
compose.yml: single-node local dev config (network-id=3, sybil off,
sample=1) replacing the stale docker/compose.yml.
k8s: replaced ad-hoc mainnet-only manifests with kustomize base +
4 overlays (localnet, devnet, testnet, mainnet). Each overlay patches
replicas, resources, storage class, network-id, and image tag.
Deleted: docker/Dockerfile, docker/compose.yml, docker/compose.genesis.yml,
k8s/luxd-statefulset.yaml, k8s/mainnet/ (entire directory).
Goreleaser runs with GOWORK=off so it cannot resolve local workspace
modules. Pin published versions:
- github.com/luxfi/utxo v0.2.7 (adds InitRuntime forwarder)
- github.com/luxfi/chains/{aivm,bridgevm,dexvm,graphvm,identityvm,
keyvm,oraclevm,quantumvm,relayvm,thresholdvm,zkvm} v0.1.0
No replace directives — every dependency resolves to a published tag.
Restores the missing entry point that .goreleaser.yml, Makefile, and
scripts/build.sh all expect at ./main. Without this file, the release
pipeline fails with 'couldn't find main file: stat main: no such file
or directory'.
The main wires:
config.BuildFlagSet → config.BuildViper → config.GetNodeConfig
→ log.NewFactoryWithConfig → ulimit.Set
→ node.New(*node.Config, log.Factory, log.Logger)
→ n.Dispatch() with SIGINT/SIGTERM handling
→ exit n.ExitCode()
--version short-circuits to print version.CurrentApp.String() (e.g.
'luxd/1.23.25').
Not using the orphan app/ package (app.New takes nodeconfig.Config which
is a parallel type definition not produced by config.GetNodeConfig). The
app/ package can be migrated separately or removed.
One place, one way to define UTXO primitives. Delete the in-tree duplicate
at node/vms/components/lux/ and import the standalone luxfi/utxo package
with a 'lux' alias so existing call-sites (lux.UTXO, lux.TransferableInput,
etc.) remain unchanged in consumers.
Files changed: 167 imports rewritten, 2 directories deleted.
Build still green on the whole tree except pre-existing examples/multi-network
QChainMainnetID issue (unrelated).
Next: rename luxfi/utxo/luxmock → utxomock for full brand-neutrality; that
is a separate PR since it requires touching all consumer alias names.
VM implementations moved to github.com/luxfi/chains/<name>.
node/node/vms.go now imports from chains/ paths.
node/vms/ retains only primary network VMs (platformvm, xvm, evm)
plus VM infrastructure (manager, registry, rpcchainvm, tracedvm, etc).
The minimum quantum-safe validator set is:
P — staking, validators, rewards (implicit)
Q — Quasar PQ consensus (BLS + Corona + ML-DSA)
Z — universal receipt registry + ZK verification
X — assets (kept for LUX token / fee UTXOs, backward compat)
Opt-in (only created if *ChainGenesis provided):
C — EVM contracts
D — DEX
B — Bridge
T — Threshold/FHE/MPC
Validators stake extra + validate opt-in chains to earn their fees.
Fee split: each chain's tx fees distributed to its validators
proportional to stake weight.