The last thresholdvm braid: F-Chain and M-Chain shared constants.ThresholdVMID
and were disambiguated by BlockchainName string-match (mode dispatch). Flip to
distinct VMs per LP-134/LP-7050:
- builder.go:604 F-Chain VMID ThresholdVMID -> FHEVMID
- builder.go:608 M-Chain VMID ThresholdVMID -> MPCVMID
- alias map + Aliases() now dispatch directly on FHEVMID/MPCVMID (no
BlockchainName string-match, no shared-VM path)
- zero ThresholdVMID remains in live genesis wiring
Shard JSON 'vm' fields left as-is to preserve per-chain GenesisData byte-stability
(routing is by builder VMID). Fixed a pre-existing self-contradictory test
(TestLP134_MChainIsMPCOnly required AND banned alias 'mpcvm').
Gate: configs + builder suites green (chainID locks + LP-134 split tests pass).
Prod unaffected — node pins genesis v1.13.16 (T-model); this is un-adopted branch
state pending a coordinated node release.
Co-authored-by: Hanzo Dev <dev@hanzo.ai>
The genesis/builder submodule go.sum pinned the pre-retag h1 hash
(pFlQm1...) for luxfi/pq v1.0.3, which no longer matches the module the
proxy and the rest of the fleet resolve. Every active repo that depends on
pq v1.0.3 (api, chains, cli, consensus, dex, evm, node, ...) records the
current hash ksw1dmf...; only a few stale/archived trees still carry the
old one. This submodule was one of the stale ones, which broke
`go build`/`go test` in the builder module with a checksum-mismatch
SECURITY ERROR. Reconcile to the fleet-canonical hash (verified against the
already-extracted local module cache). Dependency-hygiene only; no genesis
content, chain ID, or code changes.
Co-authored-by: Hanzo Dev <dev@hanzo.ai>
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>
The legacy T-Chain conflated two orthogonal threshold primitives on one
ThresholdVM substrate. LP-134 decomplects them into two distinct
primary-network chains, both served by the shared ThresholdVM library:
F-Chain (F/fhe/fhevm) — threshold FHE, confidential compute / off-EVM
threshold DECRYPT. ThresholdVM in FHE mode.
M-Chain (M/mpc/mpcvm) — threshold MPC, CGGMP21/FROST bridge-custody
SIGNING. ThresholdVM in MPC mode. EVM verify
surface = 0x0800..02 (FROST) + 0x0800..03
(CGGMP21), verify-only, ceremony off-EVM.
Forward-only: tchain.json is retired on all 4 nets (T was never bootstrapped
with live state — a clean 404 forward-split, no state migration). F-Chain
occupies T's vacated shard slot (same VM, keeps Z/G/K byte-stable); M-Chain
is appended. Fresh non-colliding chainIds at the next free indices 9/10
(F: 97269/97268/97270/32327, M: 97369/97368/97370/32437); T's 96869-series
is not recycled.
K-Chain decomplect ("K stays keyvm" per LP-134): the M/mpc/mpcvm aliases and
MPC params were erroneously carried on the KeyVM shard (the "mpc->K backwards"
wiring). Moved them to M-Chain; kchain.json is now pure KMS key management.
Chain-level wiring (one way, data-driven by shard presence):
- pkg/genesis: ConfigOutput/Config gain FChainGenesis (T's slot) +
MChainGenesis; TChainGenesis removed.
- configs.go chainSet/primaryChainShardFiles/slots + config.go
optionalChainShards: tchain.json -> fchain.json, append mchain.json.
- builder: F and M both map to constants.ThresholdVMID (shared substrate)
and are disambiguated in Aliases() by CreateChainTx.BlockchainName.
- chain_mapping role taxonomy: RoleThreshold("T") retired -> RoleFHE("F")
+ RoleMPC("M"); RoleKMS unchanged. (Canonical native FChainID/MChainID
await a luxfi/ids change; the closed P/C/X/Q/A/B/T/Z/G/I/K/D set is
out of scope here. Active path pins EVM chainIds in the shards.)
Tests: chain_shards_test updated to 11 chains + new F/M chainIds; builder
emits 11 chains; new lp134_split_test asserts the decomplect invariants
(F is FHE-only, M is MPC-only, K is KMS-only, no alias collision across
{F,M,K}, tchain.json retired). go build/vet/test green (GOWORK=off).
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.
Wave 2G-Archive completes the genesis-builder migration: the staging
shim builder/zap_codec.go (created during Wave 2G-Genesis to mirror
proto/zap_codec's surface before it was tagged) is now deleted, and
builder.go imports proto/zap_codec directly.
Changes:
- builder.go: pvmGenesisCodec / newXVMParserCodecs swap to
proto/zap_codec.NewPVMGenesis / NewXVMParser
- builder/zap_codec.go: deleted (staging shim no longer needed)
- go.mod: bump luxfi/proto v1.1.0 → v1.3.0 (first tag that exposes
proto/zap_codec; v1.3.0 also includes the proto/internal test
helper migration from Wave 2G-Internal)
luxfi/codec demoted to // indirect — kept transitively via deps that
haven't re-tagged yet. Will drop on the next upstream tagging cascade.
Migrates pvmGenesisCodec() and newXVMParserCodecs() off the legacy
linearcodec big-endian wire format onto the ZAP-native zapcodec
little-endian wire format. Aligned with LP-023 ZAP-native activation
(proto/zap_native: ZAPActivationUnix=0 — "ZAP mandatory from genesis"),
forward-only.
Wire-format verdict: BREAK.
ZAP-native bytes are NOT byte-equivalent to legacy linearcodec bytes —
endianness flips for every uint16/uint32/uint64/string-length prefix.
The canonical signature (PVM Genesis with Timestamp=0x0102030405060708,
InitialSupply=0x1112131415161718, Message="z"):
legacy linearcodec (BE): ... 01 02 03 04 05 06 07 08 11 12 13 14 15 16 17 18 ...
ZAP-native zapcodec (LE): ... 08 07 06 05 04 03 02 01 18 17 16 15 14 13 12 11 ...
No coordinated network upgrade needed: this is the first ZAP-native
genesis tag, and no production validator was running legacy linearcodec
in the post-LP-023 fleet. Mainnet/testnet/devnet/localnet all bootstrap
ZAP-only from genesis.
Code changes:
- builder.go:
Drop github.com/luxfi/codec + .../linearcodec direct imports.
Drop math.MaxInt32 inline construction.
pvmGenesisCodec() calls newZapCodecPVMGenesis(CodecVersion)
+ pchainblock.RegisterTypes(cm).
newXVMParserCodecs() calls newZapCodecXVMParser(CodecVersion).
Mirrors sdk/wallet/chain/p/pcodecs.NewPVMGenesisCodec and
sdk/wallet/chain/x/constants.go::newXVMParserCodecs which already
moved to proto/zap_codec in Wave 2G-Wallet.
- zap_codec.go (new):
Staging shim mirroring proto/zap_codec's public surface
(NewPVMGenesis, NewXVMParser, Manager.{Marshal,Unmarshal,Size,
RegisterType,SkipRegistrations}). When Wave 2G-Wallet lands and
tags github.com/luxfi/proto/zap_codec, this file deletes and the
two helpers in builder.go swap to that import — mechanical, no
behavioural change because both shim and target wrap the same
zapcodec.Codec backend.
- wire_test.go (new):
TestWire_PVMGenesisRoundtrip — bytes-out, parse-back, re-marshal stable
TestWire_PVMGenesisVersion — LE codec-version prefix
TestWire_PVMGenesisDeterministic — same Config → same bytes + UTXOAssetID
TestWire_XVMGenesisRoundtrip — XVM ParserCodecs construction smoke
TestWire_XVMGenesisDeterministic — UTXOAssetID stability across builds
TestWire_HexSignature_PVMGenesis — mainnet 32-byte prefix tripwire
TestWire_FormatBreak_Documented — pinned hex wire-format signature
- go.mod:
luxfi/codec v1.1.4 (indirect) → v1.1.5 (direct: zapcodec backend)
luxfi/accel v1.1.4 (indirect) → v1.1.8 (transitive)
Constraint-compliance:
- NO github.com/luxfi/codec imports in builder.go (verified by grep).
- NO snow/avalanche/subnet naming.
- All builder tests pass (go test ./...).
- All parent genesis + cmd modules build clean.
- Race detector clean.
TODO (wave-2g-wallet): once luxfi/proto tags include the zap_codec
subpackage, swap zap_codec.go's shim for the canonical
github.com/luxfi/proto/zap_codec import in builder.go and delete the
shim file.
proto/p Wave 2A (#101) rips github.com/luxfi/codec from p/genesis —
genesis.New, Genesis.Bytes and genesis.Parse now take a block.Codec
parameter at the call site (the same genesis-sized codec.Manager
already constructed inline elsewhere in the wallet stack).
Adds pvmGenesisCodec() helper mirroring the existing
newXVMParserCodecs() pattern in this file. Uses math.MaxInt32 budget
because PVM genesis blobs (full set of initial validator stake txs
plus CreateChainTx entries for X/C/D/Q/A/B/T/Z/G/K) can exceed runtime
tx-size limits. block.RegisterTypes seeds the full Apricot/Banff/
Durango/Quasar block + tx type set.
Threads pvmGenesisCodec() through:
- FromConfig: genesis.New + Genesis.Bytes (one codec, two uses)
- VMGenesis: genesis.Parse
- Aliases: genesis.Parse
Also fixes the X-chain genesis serialization path that was broken by
Wave 1A: xvmCodecs.GenesisCodec was used directly without going
through xchaintxs.NewParser, so the canonical XVM tx types
(BaseTx/CreateAsset/Operation/Import/Export) and fx-owned
secp256k1fx.TransferOutput were never registered. The xvm genesis now
flows through NewParser, mirroring how UTXOAssetID already uses it.
builder_test.go's TestFromConfig_ChainSetIsShardDriven gains a single
pvmGenesisCodec() construction and threads it through both pgenesis.Parse
call sites.
go build ./... + go test ./... green.
The proto pin remains at v1.1.0; the workspace go.work consumes the
local proto/ which is on branch chore/proto-p-codec-rip with the new
API. This commit goes live once proto v1.2.0 is tagged.
This is the third decomplect layer:
1. builder/ now a nested module (github.com/luxfi/genesis/builder)
— uses luxfi/utxo, vm, database, proto
2. pkg/genesis/security/ now a nested module
— uses luxfi/consensus for ChainSecurityProfile verification
3. do_transfer_test.go moved from pkg/genesis/ → builder/
— it imports luxfi/utxo/secp256k1fx which was forcing the entire
v1.27.x-era dep cascade onto downstream consumers via test-deps
in the module graph
Result on the root module's MVS-visible graph:
- consensus v1.25.0 → v1.22.84 (matches luxd v1.23.42 family)
- database v1.18.3 → v1.17.44 (matches luxd v1.23.42 exactly)
- validators v1.2.0 → v1.0.0 (matches luxd v1.23.42 exactly)
- threshold v1.6.17 → GONE (no longer in graph at all)
- node v1.27.24 → v1.20.3 (transitive, harmless)
Closes the v1.23.x backport blocker for canonical evmAddr/utxoAddr.
luxd v1.23.43 (genesis bump) can now adopt the canonical genesis
schema without dragging the v1.27.x post-quantum threshold refactor
into a v1.23.x production line.
One-direction module dep:
- github.com/luxfi/genesis (data types — minimal deps)
- github.com/luxfi/genesis/security (verification — uses consensus)
- github.com/luxfi/genesis/builder (tx-building — uses utxo/vm/db)
- github.com/luxfi/genesis/cmd (tools — uses node)
Never the reverse.
Renames Go struct field ETHAddr → EVMAddr across types.go, keys.go,
allocations.go, validator_keys.go, config.go, builder/builder.go,
cmd/genesis, cmd/checkkeys. JSON tags stayed evmAddr already (from
v1.12.6). Result: matched pair EVMAddr+UTXOAddr on both Go and JSON
sides.
Kept ParseETHAddress as the input-parsing function name — it describes
what it parses (an Ethereum-format hex address), not the field it
populates (an EVMAddr).
LLM.md updated to state the canonical pair plainly; no rename history.
Renames Allocation.EVMAddr → ETHAddr (json tag `ethAddr`) and
KeyInfo.EVMAddr → ETHAddr across pkg/genesis, builder, cmd/genesis,
cmd/checkkeys. AllocationJSON drops the legacy `evmAddr` tag entirely
— there is no aliasing, no UnmarshalJSON normalizer, no fallback.
JSON files and Go code share one canonical pair: ethAddr + utxoAddr.
configs/getgenesis_test.go updated to assert against the canonical
field. parseAllocations error message says "invalid eth address"
to match the new identifier.
Tests:
ok github.com/luxfi/genesis/builder
ok github.com/luxfi/genesis/configs
ok github.com/luxfi/genesis/pkg/genesis
Address types now reflect the actual two-address model that exists across
all chains:
- EVMAddr (0x H160) — C-Chain and every other EVM chain
- UTXOAddr (bech32, P-/X- prefix interchangeable) — P-Chain and X-Chain
The old names (ETHAddr / LUXAddr / avaxAddr / xAddr) confused two
orthogonal axes (Ethereum-vs-Lux brand × address-encoding-type) with
each other. UTXO/EVM is the actual technical distinction.
Backward compat:
- JSON: AllocationJSON.UnmarshalJSON accepts evmAddr/ethAddr +
utxoAddr/xAddr/luxAddr/avaxAddr (legacy upstream Avalanche).
- Existing config files: bumped with both new + legacy fields present.
Builds + tests green on luxfi/genesis and luxfi/node/genesis/builder.
Also: each allocation gets 50M LUX on X (initialAmount) + 50M LUX on P
(unlockSchedule locktime=0). For 1000 accounts: 50B on each chain →
100B total per env across P+X. mainnet legacy retained at-is (1.08M
historic blocks reference original asset IDs).
Every primary network now ships all 10 chain shards (X, C, D, Q, A, B, T, Z, G, K).
Each chain spawns at startup with zero post-launch CreateBlockchainTx — chain set
is fully data-driven by which shards the operator ships.
Per-letter EVM chainIds locked at 96369 + 100*Δ_from_C (mainnet base) with the
familiar testnet=-1 / devnet=+1 triple inherited from C-Chain:
C 96369/96368/96370 D 96469/96468/96470 Q 96569/96568/96570
A 96669/96668/96670 B 96769/96768/96770 T 96869/96868/96870
Z 96969/96968/96970 G 97069/97068/97070 K 97169/97168/97170
Localnet uses a parallel 31337 + 110*Δ series.
F-Chain ≡ T-Chain (threshold FHE) and M-Chain ≡ K-Chain (KMS-MPC) wired in via
builder alias lists — letter taxonomy decomplected from canonical VM IDs.
Each shard carries:
- chainId / networkId / timestamp (chain identity at boot)
- vm + description (operator transparency)
- per-VM seed extras (DEX fee config, T-Chain DKG params, B-Chain signer set,
Q-Chain pq scheme pin, K-Chain MPC topology — all empty/default initial state)
Contract locked by configs.TestGetGenesis_AllPrimaryChainsBakedIn — every primary
network must ship all 10 letter-chain shards. Removing a chain on any network is
now a load-bearing test edit, not a silent operator switch.
Mainnet C-Chain invariants preserved: chainId=96369, genesisHash unchanged,
stateRoot=0x2d1cedac…, warp precompile in alloc, warpConfig.blockTimestamp=
1730446786, treasury 2T LUX.
Closes the v1.9.14 CI build failure: pkg/genesis/security_profile_test.go
references consensusconfig.StrictPQ()/ProfileStrictPQ which were only
introduced when the Lux prefix was dropped from profile identifiers.
That symbol first ships in consensus v1.23.25.
Also fixes the gofmt drift on builder/builder.go, cmd/genesis/main.go,
and pkg/genesis/keys.go that was failing the Test job.
Make every primary-network chain (X/C/D/Q/A/B/T/Z/G/K) opt-in via its
own <x>chain.json shard. Absent shard -> empty ConfigOutput field ->
builder skips the CreateChainTx -> daemon doesn't start that chain.
Lux mainnet/testnet/devnet ship full chain set as shards; localnet and
local ship the same canonical X asset shard. P-only sovereign L1 boot
shape (<tenant> etc.) is achieved by shipping pchain.json+network.json
only — no env knob, no special builder branch.
X-Chain genesis was previously hardcoded inside FromConfig as a literal
{Symbol:"LUX",Name:"Lux",Denomination:9}. Now sourced from xchain.json
shard, which keeps the same canonical asset for Lux but lets a sibling
network (lqd / <tenant>) ship its own descriptor or ship none and
boot a P-only primary network.
builder.FromConfig:
- HRP hoisted above the X-Chain conditional (used on both branches).
- X-Chain construction gated on config.XChainGenesis != "".
- Returns ids.Empty for xAssetID when X is absent.
- Single chainEntries table replaces the per-chain switch and the
DefaultChainGenesis stub-data fallback.
- "platform" alias renamed to "protocol" (PChainAliases / VMAliases /
Aliases output) — value is "P-Chain", served by ProtocolVM.
configs.loadAllChainShards / readAllChainShards:
- Single dispatch over primaryChainShardFiles in canonical order
(X,C,D,Q,A,B,T,Z,G,K). Adding a new primary chain is one entry +
one slot, not a new env knob and not a new builder branch.
pkg/genesis/types.go:
- ConfigOutput.XChainGenesis (omitempty) added; threaded through
Config <-> ConfigOutput conversions.
Tests:
- builder/builder_test.go: TestFromConfig_ChainSetIsShardDriven pins
the contract — full mainnet emits 10 chains + non-empty xAssetID;
same config with all chain shards stripped emits 0 chains + empty
xAssetID, validators + UTXOs intact.
- configs/chain_shards_test.go: TestGetGenesis_XChainShardPresent...
asserts every Lux network embeds the canonical {LUX,Lux,9} asset;
AbsentShardEmptyOptIn extended to cover xChainGenesis.