Tail of the xvm codec kill (ddb3fbca93): the X-chain wallet builder + signer
now rebuild signed wire bytes as unsigned ‖ fx credential envelopes over the
native luxfi/utxo/wire form, and components/lux parses fx Inputs from their wire
envelope by concrete type. No linearcodec, no reflection on the wallet path.
Co-authored-by: Hanzo Dev <dev@hanzo.ai>
Route registration, client URL builders, and log lines all move to /v1/
(wallet primary api, chains/rpc handler_manager+chain_integration, xvm
client+wallet_client, xsvm api client, multi-network example, manager logs).
Zero /ext/ literals remain. One endpoint namespace, no transition alias.
Co-authored-by: Hanzo Dev <dev@hanzo.ai>
CreateNetworkTx no longer carries genesis chains — the executor never created
them, so tx.Chains() was a latent stub (declared, verified, silently dropped).
Model A resolves it by decomplection, not by implementing a second chain path:
- CreateNetworkTx = ∅→Network birth (owner + security.Mode + own validator set).
- CreateChainTx = the ONE chain constructor. An L1 spawn is a BLOCK of
CreateNetworkTx + N CreateChainTx (block-atomic, not tx-atomic).
- Removed NetworkChain, its wire (writeNetworkChains/readNetworkChains/
ncStride/sliceIDs), the chain error set, MaxNetworkChains, the chains param.
- managerChainIdx (index into genesis chains) -> managerChainID (direct
ids.ID), now SYMMETRIC with ConvertNetworkTx's manager ref. ids.Empty =>
P-Chain-governed; a set chainID => Contract-governed staking-contract host.
Net: less code (write as little as possible), one-and-one-way chain creation,
no stub. platformvm build+vet PASS; SovereignL1/InheritedL2/HybridL2/Convert
round-trips PASS.
Co-authored-by: Hanzo Dev <dev@hanzo.ai>
vms/platformvm/... build + vet PASS with pcodecs/txs.Codec gone from the VM.
Executor semantics (standard_tx_executor):
- registerOwnSet(): shared primitive — per-validator state.L1Validator with
native owner blobs (txs.MarshalOwner/UnmarshalOwner, no codec), active-set
capacity check, EndAccumulatedFee=balance+accruedFees, SetNetToL1Conversion
manager-authority recording (byte-for-byte legacy tail).
- ConvertNetworkTx: promote endomorphism (owner-authorized, folds old
ConvertNetworkToL1Tx), gated by security.Mode.Manager.
- CreateNetworkTx: base (AddNet/SetNetOwner) + sovereign path registers own set.
- Deleted CreateSovereignL1Tx + ConvertNetworkToL1Tx.
- txs.UnmarshalOwner added: canonical owner marshal/unmarshal pair, no codec.
All ~13 txs.Visitor impls carry ConvertNetworkTx; gossip/block Parse(b) codec-free;
wallet/network/primary off txs.Codec.
KNOWN GAP (pending design decision, NOT silently green): CreateNetworkTx reads
tx.Chains() only to derive managerChainID — it does NOT create the genesis
chains (no AddChain). Atomic-spawn-with-chains vs decomplect-to-CreateChainTx is
the open call. 17 codec-era _test.go parked as .bak for follow-up rewrite.
Co-authored-by: Hanzo Dev <dev@hanzo.ai>
Pull the network security model out of the tx wire into a small orthogonal
package (vms/platformvm/security): pure values, stdlib-only, no zap/tx/state
deps. One definition — security.Mode{RestakeParent, Admission, Threshold,
Manager} + Sovereign() + Valid() — composed by tx wire, executor, and state
(Rich Hickey: values not places; Rob Pike: no stutter, small orthogonal pkg).
Two orthogonal axes replace the flat Inherited/Sovereign byte:
- RestakeParent: lean on parent's validator set
- own set: Admission(NoOwnSet|Open|Gated) + Manager(PChain|Contract)
Their product spans every mode incl. HYBRID L2 (restake AND additive own set),
which the flat byte could not express. Invariant RestakeParent || own-set on
Mode.Valid(); Sovereign derived, never flagged.
CreateNetworkTx + ConvertNetworkTx carry security.Mode on the wire via shared
setSecurity/readSecurity. Round-trip green incl. new HybridL2 case + Convert
target-mode. Wallet builders pass the explicit restaked-L2 Mode.
Co-authored-by: Hanzo Dev <dev@hanzo.ai>
- SlashValidatorTx ripped (unwired stub; Avalanche has no slashing; consensus
only detects, never enforces). Type+executor+tests+Visitor surface removed.
- CreateAssetTx + OperationTx ripped from P-CHAIN (X-chain/AVM types; LP-0130:
asset creation + fx ops are X-chain money-rail, not P staking-rail; no P
producer). xvm/avm untouched.
- Staker interfaces (StakerTx/ValidatorTx/DelegatorTx/ScheduledStaker) restored
on the 5 validator types via pure accessors + FxID-on-stake preserved.
- Remaining real complex types: ConvertNetworkToL1Tx + CreateSovereignL1Tx.
Drop the Avalanche-heritage /ext prefix; /v1 is the single canonical route
surface (one way, no backward compat). The node's baseURL is the source of
truth; clients, SDKs, CLI, indexer, maker, genesis, netrunner, and the
k8s/compose/gateway/explorer configs are updated to match.
Co-authored-by: Hanzo Dev <dev@hanzo.ai>
wallet/network/primary/api.go:
- Drop the parseUTXOAnyWire dispatcher and route AddAllUTXOs straight to
lux.ParseUTXO (ZAP wire). Wire format has one representation; the
wallet has one decoder.
- Drop the now-unused ptxs and luxwire imports.
go.mod / go.sum:
- Replace orphan pseudo-versions with the published tags they tracked.
luxfi/constants v1.5.8-0... → v1.5.8
luxfi/oracle v0.1.1-0... → v0.1.1
luxfi/upgrade v1.0.1-0... → v1.0.1
luxfi/sdk v1.17.6 → v1.17.9
luxfi/kms v1.11.3 → v1.11.4
Validators validate networks; chains live on networks. AddValidatorTx
is the universal add-validator-to-a-network tx whether the target
network is Lux primary (1/2/3/1337) or any downstream sovereign L1's
own primary at its chosen networkID. AddChainValidatorTx is legacy
and kept for one release cycle of wire/codec compat with pre-LP-018
binaries.
- vms/platformvm/txs/add_chain_validator_tx.go: file-header + struct
Deprecated: notice pointing to AddValidatorTx.
- vms/platformvm/txs/chain_validator.go: ChainValidator descriptor
marked Deprecated.
- vms/platformvm/txs/add_validator_test.go: new
TestAddValidatorTxSyntacticVerify_ArbitraryPrimaryNetworkIDs covers
Lux primaries (1/2/3/1337) plus four synthetic IDs across the uint32
range, asserting the tx body has no per-chain field and accepts any
valid primary networkID.
- wallet/chain/p/wallet/wallet.go + with_options.go: Deprecated on
IssueAddChainValidatorTx; IssueRemoveChainValidatorTx doc reworded
to network-centric language.
- wallet/chain/p/builder.go + builder/builder.go: Deprecated on
NewAddChainValidatorTx interfaces.
- wallet/network/primary/examples/{add-permissioned-chain-validator,
bootstrap-hanzo, deploy-chains}/main.go: file-header notes that
these examples exercise the legacy path; new code uses AddValidatorTx.
- node/validator_manager.go: 4 log strings + 1 comment block rewritten
to talk about network validator (not chain validator).
- vms/platformvm/health.go: error string 'current chain validator of'
→ 'current network validator on'.
The fallback if/else had identical branches (dead code); collapse to a single assignment. Rename stale LUXAssetID comment + utxosByLUXAssetID local var to the canonical UTXOAssetID.
Co-authored-by: Hanzo Dev <dev@hanzo.ai>
External (HTTP / JSON-RPC) is the only place JSON is legitimate. Every
existing encoding/json import in node/ moves to github.com/go-json-experiment/json
(v2 root, not the v1 sub-package). NewEncoder/NewDecoder rewrite to
MarshalWrite/UnmarshalRead. MarshalIndent rewrites to Marshal with
jsontext.WithIndent. json.RawMessage rewrites to jsontext.Value.
*json.SyntaxError rewrites to *jsontext.SyntacticError.
81 files migrated. LLM.md captures the rule + v1->v2 delta table.
Known v2 semantic deltas surfaced by existing tests (followups, not regressions):
- [N]byte fields with no MarshalJSON now marshal as base64 string (v1 marshalled
as JSON array of byte numbers). Affects vms/platformvm/txs/*_test.go fixtures
with embedded BLS proofOfPossession.
- time.Duration has no v2 default representation; configs that wire-format
Duration as nanoseconds (vms/{xvm,platformvm}/config, config/spec) need to
switch to string-form Duration or carry an explicit option. v2 root does not
re-export FormatDurationAsNano.
- v2 enforces strict UTF-8 (vms/chainadapter/messaging fixture has non-UTF-8).
- json.MarshalWrite does not append a trailing '\n' (v1 NewEncoder.Encode did);
service/auth/auth_test.go expectation updated.
- nil []byte round-trips to empty (not nil); config_test deep-equal fixtures
surface this.
All affected sites are at the API boundary; ZAP wire envelope already covers
the internal data paths (state, P2P, consensus, MPC, threshold). Internal
JSON sites that should move to ZAP next (separate work):
- vms/da/store.go (DA blob/cert storage as JSON)
- vms/platformvm/airdrop (airdrop claims as JSON in db)
- vms/chainadapter/appchain (SQLite materializer schema/data blobs)
- vms/chainadapter/messaging (conversation codec)
- staking/kms.go (KMS HTTP client — external technically, leave)
- utils/{bimap,ips} (small marshaler shims — low priority)
luxfi/keys v1.2.0 took *ServiceIdentity out of LoadMnemonic. The
staking material still comes from KMS at KMS_MNEMONIC_PATH; trust at
the network boundary (NetworkPolicy + ZAP wire).
6de839a515 (codec Wave 1D) migrated the wallet UTXO loader to
lux.ParseUTXO (ZAP wire dispatcher) before the platformvm GetUTXOs
server-side encoder was migrated. Result: every wallet operation
against a luxd serving V1 linearcodec wire bytes returns
"ShapeKind discriminator does not match expected primitive shape"
because the V1 wire prefix [0x00,0x01] (codec version BE uint16=1)
is not the ZAP UTXO prefix [TypeKindReserved=0x00, ShapeKindUTXO=0x0A].
Lux-mainnet validators (v1.28.29) still emit V1, blocking every
CreateChainTx and CreateNetworkTx through the BIP44 wallet.
Fix: dispatch on the 2-byte envelope prefix in AddAllUTXOs. ZAP
envelopes (byte[1] == ShapeKindUTXO) route through lux.ParseUTXO;
anything else falls through to ptxs.Codec.Unmarshal. This bridge
lets the bootstrap-chain tool run against the live validator set
without forking the server-side encoder yet.
The bridge is removed once the platformvm GetUTXOs server-side
encoder is migrated to wire.NewUTXO (tracked separately).
Verified: bootstrap-chain --uri=https://api.lux.network now signs +
issues CreateChainTx successfully (osage L1 added to mainnet at
blockchainID 2ahV62kvM1KWkgxL6MiF5hPYBVwWuwpPqwV3RhqM6hCxZT68uo).
Wave 1D of the luxfi/codec final rip: leaf-misc tier under node/.
This batch removes the direct codec/wrappers, codec/linearcodec, and
codec.Manager imports from p2p network protocol, IP/port encoding,
indexer/keystore on-disk storage, warp socket IPC, x/archivedb +
merkledb internals, and the primary-network wallet UTXO loader.
Migration shape:
(a) codec/wrappers.Packer + length constants
→ node/utils/wrappers (same shape, already in tree as the local
canonical home for binary IO helpers). 14 files: indexer/,
network/, utils/ips/, utils/metric/, warp/, x/.
(b) codec.Manager + linearcodec for on-disk container storage
→ hand-rolled big-endian binary marshal/unmarshal. Hard cut;
no codec-version prefix; payload size bounded explicitly:
- indexer/codec.go: marshalContainer/unmarshalContainer
- service/keystore/codec.go: marshalHash/unmarshalHash and
marshalUser/unmarshalUser, 16 MiB blob cap retained.
(c) codec.Manager for cross-chain UTXO parsing (wallet/network/primary)
→ utxo.ParseUTXO via the ZAP wire dispatcher already registered
by node/vms/components/lux. Drops the per-chain codec.Manager
slot from FetchState; AddAllUTXOs no longer takes a codec.
Underscore-import of vms/components/lux ensures the dispatcher
is wired even for callers that don't already pull platformvm.
No snow/snowman/snowball/avalanche/avm/subnet residue introduced.
Phase 6 of the LP-023 ZAP migration. luxfi/utxo v0.3.5 (commit 559e482)
drops codec.Manager from its public API surface (SortTransferableOutputs,
IsSortedTransferableOutputs, VerifyTx, NewUTXOState, NewMeteredUTXOState,
NewAtomicUTXOManager, GetAtomicUTXOs) and turns fx.Initialize into a
no-op. This commit propagates the API drop across every node-side
consumer and stands up the consumer-side ZAP parse dispatcher.
Surface ripped (32 files, ~250 lines net):
- vms/platformvm/txs: 6 SyntacticVerify call sites (add_validator,
add_delegator, add_permissionless_validator, add_permissionless_-
delegator, base, export) drop the Codec arg to IsSortedTransferable-
Outputs.
- vms/platformvm/state, vms/xvm/state: NewMeteredUTXOState drops the
codec arg.
- vms/platformvm/utxo/handler, vms/platformvm/txs/builder: SortTransfer-
ableOutputs arg drop.
- vms/xvm/txs/executor/syntactic_verifier: 5 lux.VerifyTx call sites
drop v.Codec.
- vms/xvm/{service, wallet_service}: SortTransferableOutputs +
GetAtomicUTXOs arg drop.
- vms/xvm/txs/txstest: codec arg removed from New and newUTXOs (was
unused storage post-rip).
- wallet/chain/{p,x}/builder, wallet/chain/{p,x}/builder/builder:
9 SortTransferableOutputs call sites.
- Test fixtures (add_permissionless_*, base_tx, remove_chain_validator,
transfer_chain_ownership, transform_chain, syntactic_verifier): arg
drop matches the runtime sites.
New: vms/components/lux/utxo_parser.go — the luxd-side ZAP wire
dispatcher. utxo.ParseUTXO is consumer-registered via
utxo.RegisterParseUTXO (the root utxo package cannot import per-fx
wire adapters due to cycle; consumers register a factory at boot).
The dispatcher routes on (wire.TypeKind, wire.ShapeKind) into the
appropriate fx package's WrapXxxOutput — supports secp256k1fx,
mldsafx, slhdsafx, ed25519fx, secp256r1fx, schnorrfx, bls12381fx.
vms/xvm/txs/parser.go: utxo v0.3.5 secp256k1fx.Fx.Initialize is a
no-op (no codec RegisterType chain). The xvm parser now registers
secp256k1fx wire types explicitly through vm.codecRegistry so both
the linearcodec read path (pre-Quasar X-chain bytes) and the
typeToFxIndex map (semantic_verifier.getFx) stay populated.
vms/{xvm,platformvm}/state/state.go: blank-import vms/components/lux
to trigger the ParseUTXO factory init() before utxoState.GetUTXO
deserializes off disk.
Activation: ZAPActivationUnix=0 preserved (always-on, per LP-023).
Legacy V0/V1 codec slot maps retained as READ-ONLY decoders behind
LUXD_ENABLE_LEGACY_CODEC=1 — mainnet/testnet block history continues
to deserialize.
Build/test status:
- go build ./... clean.
- go vet ./... clean (modulo 1 pre-existing sync/atomic copy warning
in vms/rpcchainvm/zap/cevm_e2e_test.go unrelated to this rip).
- xvm txs/executor + xvm state + xvm network + xvm txs + xvm
genesis + wallet/chain/p/builder + wallet/chain/x/builder ALL
PASS under -short.
- vms/platformvm/txs serialization tests (TestAdd*Serialization)
fail on byte-baseline expected outputs because utxo's new sort
key is (AssetID, wireBytes()) vs the old codec-marshal bytes —
the test fixtures need regen under the new canonical sort. Same
story for xvm/state_test.go TestFundingAddresses / TestVerifyFxUsage
using utxo.TestState whose Out type doesn't satisfy
wireSerializable. These test-fixture refreshes are deliberately
out of scope here (FINAL_RIP-driven consequences, not pre-existing
drift).
go.mod: luxfi/utxo v0.3.2 -> v0.3.5, luxfi/api transitive v1.0.11
-> v1.0.12. luxfi/codec v1.1.4 stays direct (network/peer, warp
internal, indexer, keystore et al. still use codec.Manager for
non-UTXO concerns; those packages are out of UTXO-rip scope).
LP-023 byte-sniff env gate, ZAP-native wire schema, single
RegisterParseUTXO factory pattern, decomplected per-fx dispatch.
Adds an opt-in escape hatch that pins the fee-payment asset to a
specific 32-byte asset ID, bypassing platform.getStakingAssetID. Use on
networks where the live staking asset differs from the legacy LUX asset
on existing P-chain UTXOs.
lux-mainnet today is the documented case: staking ==
pmSJ7BfZQfwtUGbamLWSLFLFGnocfMfbriDTsYWxi4qnqFrrT but the historical
deployer's UTXOs hold HrJCm4yvNmyPDA1PEqwks9iFFmoRJEsLJj36N1xtkffrqpL6p.
Without the override the wallet's `utxoAssetID` matches the staking-asset
result and `platform.getBalance` returns 0 for the legacy UTXOs — the
wallet can't find them as fee-payment material.
NOTE: this addresses the wallet-side identification only. The P-chain
fee-execution rule still requires the staking asset on mainnet; a
follow-up legacy-to-staking asset migration tx is needed before
CreateNetworkTx + CreateChainTx will go through on mainnet. Tracked as
the 2026-06-03 mainnet brand L2 re-fire blocker.
Empty / unset is a no-op. Tested via bootstrap-chain mainnet dry-run
(2026-06-03): override correctly switches the wallet to legacy-LUX
UTXOs; CreateNetworkTx then fails with "insufficient unlocked funds"
because the fee verifier checks pmSJ7BfZ... balance — which is the
expected second-half blocker.
Post-coreth networks (test+dev primary that bake only P + EVM genesis chains)
run in P-only mode where the X alias is not registered. The wallet's
FetchState now degrades the X-Chain context to a sentinel ids.Empty
BlockchainID instead of erroring, and skips the X-chain entry from the
chain-pair UTXO scan when X is unavailable.
Required to drive IssueCreateChainTx via the canonical primary wallet
against the fresh test+dev primaries (closes universe #168).
We don't use gcr.io across lux/hanzo/zoo. Switch the heartbeat-tx example
runtime stage from gcr.io/distroless/static-debian12:nonroot to
FROM scratch, copying ca-certs, tzdata, and passwd/group from the builder.
The KMS consensus-auth gate now requires every secret-opcode envelope
to carry a signed identity. Derive a bootstrap ServiceIdentity from
KMS_BOOTSTRAP_MNEMONIC (or MNEMONIC) under the well-known servicePath
"luxd/staking-bootstrap" and thread it into the LoadMnemonicFromKMS
call so the dial envelope is signed.
Bootstrap mnemonic is provisioned out-of-band (sealed envelope, HW
token unwrap, etc.); the operational staking material on disk still
comes from the KMS-held mnemonic the dial then fetches.
Track A finish: KMS goes back to being a generic secret store; mnemonic
semantics live in luxfi/keys alongside the existing BIP-39 + BIP44
derivation. Imports flip from luxfi/kms/pkg/zapclient.LoadMnemonicFromKMS
to keys.LoadMnemonicFromKMS — same signature, same behavior.
Deps:
luxfi/keys v1.0.8 → v1.0.9 (carries the new LoadMnemonic helper)
luxfi/kms v1.9.12 → v1.9.13 (LoadMnemonic removed, secret store only)
Build clean.
Adds a KMS_ADDR-gated production path between the MNEMONIC env var
(priority 1) and the on-disk key files (priority 3). When set, the
mnemonic is fetched via luxfi/kms zapclient.LoadMnemonicFromKMS —
the same canonical loader every Lux-derived service (luxd, netrunner,
lux/cli, descending-L1 bootstraps) now shares.
New priority chain:
1. MNEMONIC env var
2. KMS_ADDR + KMS_ENV + KMS_MNEMONIC_PATH (native ZAP, default path /mnemonic)
3. Key name from os.Args[1] (~/.lux/keys/<name>/)
4. ~/.lux/keys/default/
Production env contract matches the <tenant> operator's render
(KMS_ADDR + KMS_ORG + KMS_ENV + KMS_MNEMONIC_PATH); every Lux chain
inherits the same scheme.
Dep:
+ github.com/luxfi/kms v1.9.12 (carries zapclient.LoadMnemonic)
Build clean.
The field is the primary network's UTXO fee asset (P+X), not
X-chain-specific. P-chain CreateChainTx / AddChainValidatorTx and
X-chain transfers all burn it for fees. Same number on P and X by
construction; the name should reflect the function, not the chain.
Renames applied across:
- wallet/chain/{p,x}/builder.Context.UTXOAssetID
- wallet/chain/x/builder.Backend.UTXOAssetID() trait method
- node/config: resolveUTXOAssetID + nodeConfig.UTXOAssetID
- vms/platformvm + vms/xvm + chains/manager consumers
- examples (deploy-chains, get-p-chain-balance, ...)
Includes local replaces for luxfi/{runtime,consensus} pending their
release tags landing. Drop after upstream tags ship.
Companion PRs: luxfi/runtime#2, luxfi/consensus#11. Downstream
consumers (sdk, cli, genesis, liquidity) follow.
* feat(platformvm): CreateSovereignL1Tx — single-tx sovereign L1 launch
Adds a new platformvm tx type that atomically registers a sovereign L1
in one P-chain commit. Replaces what is today the four-step flow:
CreateNetworkTx + AddChainValidatorTx ×N + CreateChainTx ×K + ConvertNetworkToL1Tx
with one signed tx. After commit, the primary network has a permanent
record of the L1's network ID + initial validator set + chain manifest
+ on-chain validator-manager contract — but it does NOT track-chains
or validate the L1's blocks. The L1 runs its own consensus from
genesis. L2/L3/L4 follow the same pattern recursively.
Type shape:
type CreateSovereignL1Tx struct {
BaseTx
Owner fx.Owner // CreateNetworkTx parity
Validators []*ConvertNetworkToL1Validator // genesis validator set
Chains []*SovereignL1Chain // VM ID + genesis blob per chain
ManagerChainIdx uint32 // index into Chains[]
ManagerAddress types.JSONByteSlice // validator-manager contract
}
type SovereignL1Chain struct {
BlockchainName string
VMID ids.ID
FxIDs []ids.ID
GenesisData []byte
}
SyntacticVerify enforces:
- at least one validator (sorted, unique)
- at least one chain, ≤ MaxSovereignL1Chains (16)
- ManagerChainIdx is in range of Chains[]
- ManagerAddress ≤ MaxChainAddressLength
- per-chain name + VMID + FxIDs + genesis bounds
- BaseTx + Owner + each Validator each verify
Wired into:
- Visitor interface
- codec (registered as the next tx type after ConvertNetworkToL1Tx)
- signer + complexity + metrics + executor stubs across all visitor
implementations
Executor body is stubbed with a TODO. The atomic state transition
(mint new networkID from tx hash, seed validator-manager state,
register each Chain, charge fee) lands in a follow-up PR. This PR is
the type definition + interface plumbing so downstream tools (wallet,
CLI, fee calc, metrics) can target the tx type while the executor is
implemented.
* chore(node): kill subnet — chain/network vocabulary across node
Zero remaining `subnet|Subnet|SUBNET` in node Go source. Per canonical
no-subnet rule.
## Wire types (Go fields only; byte-level wire encoding unchanged)
message/wire/types.go TrackedSubnets → TrackedChains
message/wire/zap.go same on Read/Write
proto/p2p/p2p_zap.go SubnetUptime alias → ChainUptime
(consumes luxfi/proto rename in companion PR)
## Comment scrub
message/wire/types.go "(chain, subnet) pair" → "(chain, network) pair"
network/peer/handshake.go "primary-network or subnet" → "primary-network or per-chain"
node/node.go "per-subnet" → "per-chain"
"P→subnet warp" → "P→chain warp"
genesis/builder/builder.go "their own subnets" → "their own chains"
vms/platformvm/client.go dropped "subnet jargon" reference
vms/platformvm/service.go dropped "net / subnet jargon" reference
vms/platformvm/config/internal.go "subnet-spawned blockchain" → "per-chain blockchain"
## ICPSubnet (Internet Computer adapter)
ICPSubnet → ICPNet (type rename — unrelated to platform subnet,
but still a subnet word; killed for consistency)
map field `subnets` → `nets`
## Examples
wallet/network/primary/examples/bootstrap-hanzo/main.go:
--subnet-id → --network-id (CLI flag)
existingSubnetID local var → existingNetID
"subnet ID" log lines → "network ID"
"SUBNET_ID=" output → "NETWORK_ID="
"subnet-evm VM ID" → "EVM VM ID"
All comments rephrased.
wallet/network/primary/examples/heartbeat-tx/main.go: one comment
rephrase.
go.work workspace cleanup:
- Removed ./operator/go entry (placeholder; lux/operator polyglot
layout pending the cross-repo migration)
- Removed ./operator entry (mid-migration; nothing to build)
Build clean. Zero `grep -rIn "subnet|Subnet" --include="*.go"` matches.
Cleanup pass on the C-Chain-disabled future-work comments. No code
change — just the placeholder identifiers now match the canonical
EVMAddress / EVMKeychain naming used by the live KeychainAdapter.
ethAddrs → evmAddrs
FetchEthState → FetchEVMState
Forward-only completion of the strip-aliases work. Examples (18 main.go
files + example_test.go + debug_balance_test.go) used the old
EthKeychain field name on WalletConfig literals. Mass-renamed via
sed across the wallet/ tree to match the canonical EVMKeychain name.
Also: cleaned remaining linter-restored EthKeychain references in
wallet.go (WalletConfig struct field, comments in MakeWallet).
Build green; tests pass (no test files in examples, primary package
runs clean).
Per CLAUDE.md x.x.x+1.
Workspace-wide reconcile to the runtime-data-model axis (EVM) the
state team established earlier. Decomplect by what things ARE:
- EVMAddresses = 20-byte account addresses on EVM-runtime chains
- The internal hash primitive (Keccak256 of secp256k1 pubkey) is
HOW the value is computed, not WHAT it is
wallet/network/primary/wallet.go:
- EVMKeychain interface is canonical (GetByEVM, EVMAddresses)
- KeccakKeychain retained as Deprecated alias
- EthKeychain retained as Deprecated alias
- KeychainAdapter implements all three interfaces; canonical
implementations on EVMKeychain methods, deprecated aliases delegate
wallet/network/primary/common/options.go:
- Options.EVMAddresses() is canonical
- KeccakAddresses() and EthAddresses() Deprecated aliases delegate
- WithCustomEVMAddresses() option helper is canonical
- WithCustomKeccakAddresses() and WithCustomEthAddresses() Deprecated
Deps bumped:
- luxfi/utxo v0.3.1 → v0.3.2 (EVMAddrs canonical, deprecated aliases)
- luxfi/crypto v1.19.13 → v1.19.15 (EVMAddress canonical)
- luxfi/genesis v1.12.11 → v1.12.14 (transitive dep of utxo bump
for crypto/keccak package path)
Per CLAUDE.md x.x.x+1.
Wave 3 of the workspace-wide eth* naming purge. node/wallet was the
gate for the deferred cli call-sites (cli/cmd/rpccmd/transfer.go and
cli/pkg/chain/local.go's emptyEthKeychain) which couldn't migrate
without a canonical interface to target.
wallet/network/primary/wallet.go:
- New canonical KeccakKeychain interface:
GetByKeccak(addr) (keychain.Signer, bool)
KeccakAddresses() set.Set[gethcommon.Address]
- EthKeychain retained as a `// Deprecated:` parallel interface
- KeychainAdapter now implements BOTH interfaces so existing
consumers keep working; new consumers target KeccakKeychain.
- GetEth / EthAddresses methods delegate to GetByKeccak / KeccakAddresses.
wallet/network/primary/common/options.go:
- New canonical Options.KeccakAddresses() method
- New canonical WithCustomKeccakAddresses(...) option helper
- EthAddresses / WithCustomEthAddresses retained as `// Deprecated:`
aliases delegating to the canonical names
Deps bumped:
- luxfi/utxo v0.3.0 → v0.3.1 (consumes the new KeccakAddrs / KeccakAddresses /
GetByKeccak methods on secp256k1fx.Keychain)
- luxfi/crypto stays at v1.19.13 (PrivateKey.KeccakAddress)
This unblocks cli/cmd/rpccmd/transfer.go to call kcAdapter.KeccakAddresses()
and cli/pkg/chain/local.go to implement KeccakKeychain — a future cli
wave (v1.100.5+).
Per CLAUDE.md x.x.x+1.
Final cleanup for the upgrade-name purge (lux/node now runs full
feature-set from genesis under activate-all-implicitly):
- vms/xvm: `var durango = upgrade.Default` -> `var activeUpgrade =
upgrade.Default`. Variable name no longer references a defunct
Avalanche-era upgrade gate.
- wallet/chain/p/builder_test.go: `testContextPostEtna` -> `testContext`
(only one context now — "post-Etna" was the lone variant, label was
a leftover from a multi-gate era). Test names "Post-Etna" /
"Post-Etna with memo" -> "default" / "default with memo".
No behavior change. `go build` + `go vet` clean.
vms/registry/registry.go: drop 17 [Registry]/[VMGetter] debug fmt.Printf
calls that were left from an AI debugging session. Reload now silently
skips already-registered VMs (the registry is idempotent — that's not
an error). Doc-commented for what each return value means.
node/node.go: drop the [BOOTSTRAP] fmt.Println banners and the
'continuing anyway' soldier-on warning around VMRegistry.Reload. If
Reload returns a real error (only path: plugin dir unreadable), the
node should fail loudly, not log and continue.
vms/xvm/vm_benchmark_test.go, vms/platformvm/validators/test_manager.go,
wallet/chain/p/wallet/backend_visitor.go, wallet/keychain/keychain_test.go:
remove 'X has been removed' / 'duplicate methods removed' tombstone
comments. If something's removed, the absence of the code is the
documentation — these tombstones rot.
keyutil.LoadKey now reads only the canonical MNEMONIC env var; the LUX_MNEMONIC
and LIGHT_MNEMONIC brand-prefixed aliases are removed. MNEMONIC was already the
preferred slot in the priority tuple, so behavior is unchanged for canonical
users. Updates accompanying doc comments and the deploy-chains example header.
The companion commit `7496606282` retired the gRPC fallback inside
vms/rpcchainvm/. This commit closes the loop by deleting every
remaining `//go:build grpc` file under node/, removing the dual-build
plumbing entirely. ZAP is the only wire protocol; there is no
`-tags=grpc` opt-in.
Per the "one and only one way to do everything" mandate, the
backwards-compat scaffolding (gRPC adapters, protoc stubs, connect-go
example handlers, OTLP gRPC exporter, x/sync gRPC sync engine,
keystore-over-gRPC client/server, rpcwarp gRPC signer, gRPC alias
reader) is removed forward-only — no aliases, no deprecation period.
Deletions (84 files):
- proto/pb/{aliasreader,http,io,keystore,message,messenger,net,p2p,
platformvm,rpcdb,sdk,sender,sharedmemory,signer,sync,
validatorstate,vm,warp}/ — protoc stubs (entire tree)
- proto/{p2p,platformvm,sync,vm}/*_grpc.go — gRPC type re-exports
- db/rpcdb/{grpc_server,grpc_client,grpc_test}.go — rpcdb gRPC adapter
- service/keystore/rpckeystore/ — keystore-over-gRPC (dead consumer)
- x/sync/ — entire merkledb sync engine (100% gRPC-tagged)
- internal/ids/rpcaliasreader/ — gRPC alias reader (dead consumer)
- connectproto/ — connect-go XSVM ping handler scaffolding
- vms/platformvm/warp/rpcwarp/{client,server}.go — gRPC warp signer
- vms/platformvm/network/warp.go — protobuf-based warp justification
handler (warp_zap.go retains the no-op verifier consumers expect)
- vms/components/message/message_grpc.go + message_test.go
- vms/example/xsvm/api/ping.go + vm_http_grpc.go + cmd/{run,xsvm}/
- wallet/network/primary/examples/sign-l1-validator-* (5 dead
example main packages)
- trace/{exporter_grpc,exporter_type,exporter_type_test,noop,tracer}.go
(OTLP gRPC exporter + duplicate Tracer types — trace_zap.go has
the canonical Tracer interface + no-op tracer)
- message/bft_grpc.go (Simplex BFT wrapper)
Edits (9 files): every surviving `_zap.go` drops its `//go:build !grpc`
constraint (the files are unconditional now) and drops stale
"ZAP version" / "ZAP mode" inline comments.
Doc updates:
- LLM.md ZAP Transport section: remove the `-tags=grpc` opt-in
language. Replace with "ZAP is the only wire protocol... there is
one and only one way". Update Latest Tag to v1.26.31. Update the
rpcdb topology section to reflect single-adapter state.
go.mod: connectrpc.com/connect, connectrpc.com/grpcreflect,
otlptracegrpc moved out of direct deps. google.golang.org/grpc +
protobuf demoted to indirect (still pulled transitively via luxfi/dex).
Verified:
- `go build ./...` (default, no tags) clean
- `go test ./db/rpcdb/... ./trace/... ./message/... ./vms/rpcchainvm/...
./vms/components/message/... ./vms/platformvm/network/...
./vms/example/xsvm/... ./service/keystore/... ./proto/...` clean
- `grep -rln '//go:build grpc' --include='*.go' node/` returns zero
- `grep -rln '//go:build !grpc' --include='*.go' node/` returns zero
- `grep -rln 'google.golang.org/grpc' --include='*.go' node/` returns
zero (only transitive deps remain in go.sum)
Pre-existing TestGraniteNetworkIDConfiguration failure in tests/ is
unrelated — fails on the parent commit too (verified via stash).
- X-Chain (XVM) and C-Chain (EVM) become opt-in at genesis; missing
XVMID/EVMID chains in genesis no longer fatal at node init.
- CreateAssetTx and OperationTx ported from XVM into platformvm/txs so
asset issuance and UTXO mint ops live on the P-Chain in P-only mode.
- Cross-chain P->X->C flows replaced by direct P->subnet warp transfers.
- Signer visitor + backend visitor wired for the new tx types.
- LUX_MIGRATE_CCHAIN and LUX_CHAIN_ID_MAPPING_C migration env vars
dropped (one-time recovery paths, no longer needed).
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.