diff --git a/builder/builder.go b/builder/builder.go index 70a9b81..44d3bab 100644 --- a/builder/builder.go +++ b/builder/builder.go @@ -56,21 +56,24 @@ var ( AChainAliases = []string{"A", "ai", "aivm"} // BChainAliases are the default aliases for the B-Chain (Bridge) BChainAliases = []string{"B", "bridge", "bridgevm"} - // TChainAliases are the default aliases for the T-Chain (Threshold-FHE). - // "F" / "fhe" alias the threshold-FHE chain since this is the - // encrypted-compute chain in conceptual letter-chain taxonomy - // (LP-134 / "F-Chain" in user-facing docs). The on-disk shard - // stays tchain.json — letter is decomplected from product name. - TChainAliases = []string{"T", "threshold", "thresholdvm", "F", "fhe", "fhevm"} + // FChainAliases are the default aliases for the F-Chain (threshold-FHE). + // LP-134: F-Chain runs the ThresholdVM substrate in FHE mode (confidential + // compute / off-EVM threshold DECRYPT). It supersedes the retired T-Chain; + // the legacy "T"/"threshold" labels now belong only to teleportvm + // (LP-6332). On-disk shard: fchain.json. + FChainAliases = []string{"F", "fhe", "fhevm"} // ZChainAliases are the default aliases for the Z-Chain (ZK) ZChainAliases = []string{"Z", "zk", "zkvm"} // GChainAliases are the default aliases for the G-Chain (Graph) GChainAliases = []string{"G", "graph", "graphvm"} - // KChainAliases are the default aliases for the K-Chain (KMS / MPC). - // "M" / "mpc" alias the KMS chain since this is the MPC-topology - // chain in conceptual letter-chain taxonomy (LP-134 / "M-Chain" - // in user-facing docs). The on-disk shard stays kchain.json. - KChainAliases = []string{"K", "kms", "kmsvm", "M", "mpc", "mpcvm"} + // KChainAliases are the default aliases for the K-Chain (KMS). + // LP-134: "K stays keyvm" — the threshold-MPC labels M/mpc/mpcvm moved off + // K onto the M-Chain (MChainAliases). K-Chain is pure KMS key management. + KChainAliases = []string{"K", "key", "keyvm", "kms", "kmsvm"} + // MChainAliases are the default aliases for the M-Chain (threshold-MPC). + // LP-134: M-Chain runs the ThresholdVM substrate in MPC mode (CGGMP21 / + // FROST threshold SIGNING for bridge custody). On-disk shard: mchain.json. + MChainAliases = []string{"M", "mpc", "mpcvm"} // VMAliases are the default aliases for VMs VMAliases = map[ids.ID][]string{ @@ -576,10 +579,15 @@ func FromConfig(config *genesiscfg.Config) ([]byte, ids.ID, error) { // chains exist is a function of which shards the operator shipped, // not a runtime knob. // - // Order is fixed (X→C→D→Q→A→B→T→Z→G→K) and preserved across + // Order is fixed (X→C→D→Q→A→B→F→Z→G→K→M) and preserved across // presence/absence so byte-identical genesis holds when the same // shard set is supplied. Append-only — reordering shifts the // P-Chain genesis byte layout. + // + // LP-134: F-Chain and M-Chain both run the ThresholdVM substrate (F in + // FHE mode, M in MPC mode) — they share constants.ThresholdVMID and are + // disambiguated by BlockchainName. F-Chain occupies the retired T-Chain's + // slot (same VM); M-Chain is appended, keeping Z/G/K byte-stable. chainEntries := []struct { GenesisData []byte VMID ids.ID @@ -593,10 +601,11 @@ func FromConfig(config *genesiscfg.Config) ([]byte, ids.ID, error) { {GenesisData: []byte(config.QChainGenesis), VMID: constants.QuantumVMID, Name: "Q-Chain"}, {GenesisData: []byte(config.AChainGenesis), VMID: constants.AIVMID, Name: "A-Chain"}, {GenesisData: []byte(config.BChainGenesis), VMID: constants.BridgeVMID, Name: "B-Chain"}, - {GenesisData: []byte(config.TChainGenesis), VMID: constants.ThresholdVMID, Name: "T-Chain"}, + {GenesisData: []byte(config.FChainGenesis), VMID: constants.ThresholdVMID, Name: "F-Chain"}, {GenesisData: []byte(config.ZChainGenesis), VMID: constants.ZKVMID, Name: "Z-Chain"}, {GenesisData: []byte(config.GChainGenesis), VMID: constants.GraphVMID, Name: "G-Chain"}, {GenesisData: []byte(config.KChainGenesis), VMID: constants.KeyVMID, Name: "K-Chain"}, + {GenesisData: []byte(config.MChainGenesis), VMID: constants.ThresholdVMID, Name: "M-Chain"}, } chains := []genesis.Chain{} for _, e := range chainEntries { @@ -787,12 +796,25 @@ func Aliases(genesisBytes []byte) (map[string][]string, map[ids.ID][]string, err } chainAliases[chainID] = BChainAliases case constants.ThresholdVMID: - apiAliases[endpoint] = []string{ - "T", "threshold", "thresholdvm", - path.Join(constants.ChainAliasPrefix, "T"), - path.Join(constants.ChainAliasPrefix, "threshold"), + // LP-134: F-Chain (FHE mode) and M-Chain (MPC mode) both run the + // ThresholdVM substrate, so they share constants.ThresholdVMID. + // They are distinct chains — disambiguate by the CreateChainTx's + // BlockchainName (set from the chain entry Name in FromConfig). + if uChain.BlockchainName == "M-Chain" { + apiAliases[endpoint] = []string{ + "M", "mpc", "mpcvm", + path.Join(constants.ChainAliasPrefix, "M"), + path.Join(constants.ChainAliasPrefix, "mpc"), + } + chainAliases[chainID] = MChainAliases + } else { + apiAliases[endpoint] = []string{ + "F", "fhe", "fhevm", + path.Join(constants.ChainAliasPrefix, "F"), + path.Join(constants.ChainAliasPrefix, "fhe"), + } + chainAliases[chainID] = FChainAliases } - chainAliases[chainID] = TChainAliases case constants.ZKVMID: apiAliases[endpoint] = []string{ "Z", "zk", "zkvm", @@ -809,9 +831,9 @@ func Aliases(genesisBytes []byte) (map[string][]string, map[ids.ID][]string, err chainAliases[chainID] = GChainAliases case constants.KeyVMID: apiAliases[endpoint] = []string{ - "K", "kms", "kmsvm", + "K", "key", "keyvm", "kms", path.Join(constants.ChainAliasPrefix, "K"), - path.Join(constants.ChainAliasPrefix, "kms"), + path.Join(constants.ChainAliasPrefix, "key"), } chainAliases[chainID] = KChainAliases } diff --git a/builder/builder_test.go b/builder/builder_test.go index e4fc614..f3cf5f4 100644 --- a/builder/builder_test.go +++ b/builder/builder_test.go @@ -208,7 +208,7 @@ func TestFromConfig_ChainSetIsShardDriven(t *testing.T) { full, err := pgenesis.Parse(pgc, bytes) require.NoError(t, err) - require.Len(t, full.Chains, 10, "mainnet emits all 10 primary chains") + require.Len(t, full.Chains, 11, "mainnet emits all 11 primary chains (LP-134: X,C,D,Q,A,B,F,Z,G,K,M)") // Strip every chain shard. Allocations + validators stay intact — // the P-Chain itself is the foundation, not a CreateChainTx entry. @@ -218,10 +218,11 @@ func TestFromConfig_ChainSetIsShardDriven(t *testing.T) { cfg.QChainGenesis = "" cfg.AChainGenesis = "" cfg.BChainGenesis = "" - cfg.TChainGenesis = "" + cfg.FChainGenesis = "" cfg.ZChainGenesis = "" cfg.GChainGenesis = "" cfg.KChainGenesis = "" + cfg.MChainGenesis = "" bytes, utxoAssetID, err = FromConfig(cfg) require.NoError(t, err, "P-only build must succeed") diff --git a/builder/wire_test.go b/builder/wire_test.go index fe5082d..2f5137d 100644 --- a/builder/wire_test.go +++ b/builder/wire_test.go @@ -39,7 +39,7 @@ func TestWire_PVMGenesisRoundtrip(t *testing.T) { gen, err := pgenesis.Parse(pgc, bytes1) require.NoError(t, err) require.NotNil(t, gen) - require.Equal(t, 10, len(gen.Chains), "mainnet ships all 10 primary chains") + require.Equal(t, 11, len(gen.Chains), "mainnet ships all 11 primary chains (LP-134: T split into F+M)") // Re-marshal. bytes2, err := gen.Bytes(pgc) diff --git a/cmd/bootstrap-chain/main.go b/cmd/bootstrap-chain/main.go index ea282c5..427c703 100644 --- a/cmd/bootstrap-chain/main.go +++ b/cmd/bootstrap-chain/main.go @@ -9,9 +9,12 @@ // 3. IssueAddChainValidatorTx — adds every primary network validator // 4. Probes eth_blockNumber and info.isBootstrapped — fails if either is bad // -// Designed to bootstrap the four canonical Lux devnet chains (hanzo, zoo, -// pars, spc) in one pass, but the list is data-driven so it can bootstrap -// any subset. +// Creates lux-native chains on a luxd network. The chain list is data-driven +// (--track-chain-ids). Brand names (hanzo/zoo/pars/spc/osage) are REFUSED: +// per the brand_sovereign_node_one_way law luxd runs the LUX primary network +// ONLY, and each brand runs its own sovereign node (zood/hanzod/parsd/…). The +// historical "bootstrap the four brand L2s into luxd" purpose is retired +// alongside cmd/createchaintx. // // Idempotency: before any P-chain spend, the tool queries // platform.getBlockchains and skips any chain whose alias already exists. @@ -22,9 +25,9 @@ // by CreateChainTx. Three IDs, three roles, never aliased: // // - `networkID` — identifies a validator network. Comes in two scopes: -// * primary networkID (uint32: 1=mainnet, 2=testnet, 3=local, 1337=dev) -// * per-chain networkID (ids.ID 32 bytes) — the CreateNetworkTx ID -// that owns one or more chains. +// - primary networkID (uint32: 1=mainnet, 2=testnet, 3=local, 1337=dev) +// - per-chain networkID (ids.ID 32 bytes) — the CreateNetworkTx ID +// that owns one or more chains. // - `chainID` — the blockchain's own globally unique ID (ids.ID 32 bytes). // - `evmChainID` — EIP-155 chain ID (uint64). EVM JSON-RPC only. // @@ -36,7 +39,7 @@ // --bip44-idx=5 \ // --network-label=devnet \ // --configs-dir=/path/to/genesis/configs \ -// --track-chain-ids=hanzo,zoo,pars,spc \ +// --track-chain-ids= \ // --output=/dev/stdout package main @@ -117,11 +120,17 @@ func main() { hrp := flag.String("hrp", "dev", "P-chain bech32 HRP: test|dev") bipIdx := flag.Uint("bip44-idx", 5, "BIP44 derivation index at m/44'/9000'/0'/0/") configsDir := flag.String("configs-dir", "", "directory containing -/genesis.json files (required)") - // --track-chain-ids mirrors luxd's existing --track-chain-ids flag. One - // concept, one flag — this is the declared list of chains this network - // serves. The tool reads it, queries the P-chain, and idempotently - // creates the missing ones. - trackChainIDs := flag.String("track-chain-ids", "hanzo,zoo,pars,spc", "comma-separated brand names (matches luxd --track-chain-ids). Each name resolves a (brand,env) → configs/-/genesis.json and a content-hashed CreateChainTx name.") + // --track-chain-ids is the declared list of non-brand chains to create on + // the target luxd network. The tool reads it, queries the P-chain, and + // idempotently creates the missing ones. + // + // DEFAULT IS EMPTY by design. The historical default "hanzo,zoo,pars,spc" + // created brand L2 EVMs INSIDE luxd — the dead WAY-1 retired by the + // brand_sovereign_node_one_way law: luxd runs the LUX primary network + // ONLY, and each brand (hanzo/zoo/pars/spc/osage) runs its OWN sovereign + // node (zood/hanzod/parsd/…). Brand names are now hard-refused below; this + // flag exists only for legitimate lux-native chain creation. + trackChainIDs := flag.String("track-chain-ids", "", "comma-separated chain ids to create on the target luxd network. Brand names (hanzo/zoo/pars/spc/osage) are REFUSED — brands run their own sovereign node, never inside luxd.") vmIDStr := flag.String("vm-id", defaultEVMID, "EVM VM ID present in luxd's --plugin-dir") // --chain-name-override pins an explicit CreateChainTx name for a single // brand (format: =, repeatable). Bypasses the content-hash @@ -195,6 +204,19 @@ func main() { log.Fatal("--uri and --configs-dir are required") } + // brand_sovereign_node_one_way law: luxd runs the LUX primary network + // ONLY. Brand L2 EVMs are NEVER created inside luxd — each brand runs its + // own sovereign node (zood/hanzod/parsd/…) and the unified DEX is the + // shared 0x9999 V4 precompile compiled into every brand EVM, not a chain + // hosted on luxd. Refuse brand names so this tool can never re-create the + // phantom brand chains that decayed into dangling chain-aliases. + for _, name := range strings.Split(*trackChainIDs, ",") { + switch strings.ToLower(strings.TrimSpace(name)) { + case "hanzo", "zoo", "pars", "spc", "osage": + log.Fatalf("refusing to bootstrap brand chain %q inside luxd: brands run their own sovereign node (zood/hanzod/parsd/…), not luxd — see brand_sovereign_node_one_way", strings.TrimSpace(name)) + } + } + vmID, err := ids.FromString(*vmIDStr) if err != nil { log.Fatalf("invalid --vm-id: %v", err) diff --git a/configs/chain_shards_test.go b/configs/chain_shards_test.go index f1f15c3..1b878dc 100644 --- a/configs/chain_shards_test.go +++ b/configs/chain_shards_test.go @@ -143,12 +143,14 @@ func networkIDFromName(t *testing.T, name string) uint32 { } // TestGetGenesis_AllPrimaryChainsBakedIn locks the contract that every -// primary network (mainnet, testnet, devnet, localnet) ships ALL 10 +// primary network (mainnet, testnet, devnet, localnet) ships ALL 11 // primary-network chain shards baked into its genesis. This means the // chains spawn at network startup with NO post-launch CreateBlockchainTx — // chain set is fully data-driven by which shards are committed. // -// The 10 primary-network chains, in canonical order: +// The 11 primary-network chains, in canonical order (LP-134: the legacy +// T-Chain/ThresholdVM is retired and split into F-Chain + M-Chain, both +// served by the same ThresholdVM substrate in FHE / MPC mode respectively): // // X XVM exchange / UTXO asset // C EVM contracts @@ -156,10 +158,11 @@ func networkIDFromName(t *testing.T, name string) uint32 { // Q QuantumVM post-quantum consensus (Quasar / ML-DSA / ML-KEM) // A AIVM AI verification / attestation // B BridgeVM cross-chain bridge (MPC signing) -// T ThresholdVM threshold FHE encrypted compute (LP-?? F-chain semantic) +// F ThresholdVM threshold-FHE confidential compute (LP-134; FHE mode, T's slot) // Z ZKVM zero-knowledge proofs / private state // G GraphVM graph database -// K KeyVM KMS — MPC topology (LP-?? M-chain semantic) +// K KeyVM KMS — key management +// M ThresholdVM threshold-MPC bridge-custody signing (LP-134; MPC mode) // // P-Chain is the primary network itself and carries the validator set + chain // registry; it has no shard slot because it isn't a CreateChainTx entry. @@ -177,10 +180,11 @@ func TestGetGenesis_AllPrimaryChainsBakedIn(t *testing.T) { "qChainGenesis", "aChainGenesis", "bChainGenesis", - "tChainGenesis", + "fChainGenesis", "zChainGenesis", "gChainGenesis", "kChainGenesis", + "mChainGenesis", } for _, name := range []string{"mainnet", "testnet", "devnet", "localnet"} { t.Run(name, func(t *testing.T) { @@ -214,11 +218,16 @@ func TestGetGenesis_AllPrimaryChainsBakedIn(t *testing.T) { // for the D/Q/A/... shards below; C is the one cell pinned by the // canonical map rather than derived from the arithmetic. // +// LP-134: the legacy T-Chain (idx 5, 96869-series) is retired. F-Chain and +// M-Chain take the next free indices 9 and 10 — fresh IDs, T's are not +// recycled. (localnet base = 31337 + 110*idx.) +// // This produces: // // C(96369/96368/96367), 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) +// A(96669/96668/96670), B(96769/96768/96770), F(97269/97268/97270), +// Z(96969/96968/96970), G(97069/97068/97070), K(97169/97168/97170), +// M(97369/97368/97370) // // Each ID is unique across {network × letter} so a misrouted tx cannot // be replayed against the wrong chain. @@ -226,19 +235,19 @@ func TestPrimaryChainShards_PerChainCanonicalChainID(t *testing.T) { want := map[string]map[string]int{ "mainnet": { "d": 96469, "q": 96569, "a": 96669, "b": 96769, - "t": 96869, "z": 96969, "g": 97069, "k": 97169, + "f": 97269, "z": 96969, "g": 97069, "k": 97169, "m": 97369, }, "testnet": { "d": 96468, "q": 96568, "a": 96668, "b": 96768, - "t": 96868, "z": 96968, "g": 97068, "k": 97168, + "f": 97268, "z": 96968, "g": 97068, "k": 97168, "m": 97368, }, "devnet": { "d": 96470, "q": 96570, "a": 96670, "b": 96770, - "t": 96870, "z": 96970, "g": 97070, "k": 97170, + "f": 97270, "z": 96970, "g": 97070, "k": 97170, "m": 97370, }, "localnet": { "d": 31447, "q": 31557, "a": 31667, "b": 31777, - "t": 31887, "z": 31997, "g": 32107, "k": 32217, + "f": 32327, "z": 31997, "g": 32107, "k": 32217, "m": 32437, }, } for net, byLetter := range want { diff --git a/configs/configs.go b/configs/configs.go index 8ae8215..4ba4c29 100644 --- a/configs/configs.go +++ b/configs/configs.go @@ -201,7 +201,7 @@ func loadEmbeddedGenesisWithDynamic(networkName string, dynamicPChain *genesis.P // Runtime gate is luxd's --track-chains, not bake-time env knobs. // // To run a P-only network (a regulated securities L1, etc.), ship a config tree - // that omits every {x,c,d,q,a,b,t,z,g,k}chain.json. No knob, no + // that omits every {x,c,d,q,a,b,f,z,g,k,m}chain.json. No knob, no // hack — chain set is purely data-driven by which shards are // present. chainShards, err := loadAllChainShards(networkName) @@ -237,10 +237,11 @@ func loadEmbeddedGenesisWithDynamic(networkName string, dynamicPChain *genesis.P QChainGenesis: chainShards.Q, AChainGenesis: chainShards.A, BChainGenesis: chainShards.B, - TChainGenesis: chainShards.T, + FChainGenesis: chainShards.F, ZChainGenesis: chainShards.Z, GChainGenesis: chainShards.G, KChainGenesis: chainShards.K, + MChainGenesis: chainShards.M, SecurityProfile: securityProfile, CertPolicy: certPolicy, Message: network.Message, @@ -255,13 +256,19 @@ func loadEmbeddedGenesisWithDynamic(networkName string, dynamicPChain *genesis.P // in the builder's chains slice — the operator's filesystem is the // declarative source of truth for which primary-network chains exist. type chainSet struct { - X, C, D, Q, A, B, T, Z, G, K string + X, C, D, Q, A, B, F, Z, G, K, M string } // primaryChainShardFiles lists every primary-network chain shard // filename in canonical order. Order matters: builder.FromConfig // preserves it when assembling the chains slice, so changing this // list shifts the P-Chain genesis byte layout. Append-only. +// +// LP-134: the legacy tchain.json (ThresholdVM) is retired; the threshold +// substrate is split into fchain.json (F-Chain, threshold-FHE) — which +// takes T's vacated slot since it runs the same ThresholdVM — and +// mchain.json (M-Chain, threshold-MPC bridge custody), appended at the +// end. Z/G/K positions are unchanged so their genesis layout is stable. var primaryChainShardFiles = [...]string{ "xchain.json", "cchain.json", @@ -269,10 +276,11 @@ var primaryChainShardFiles = [...]string{ "qchain.json", "achain.json", "bchain.json", - "tchain.json", + "fchain.json", "zchain.json", "gchain.json", "kchain.json", + "mchain.json", } // chainSetSlots returns the address-of-field slots for s in the @@ -280,7 +288,7 @@ var primaryChainShardFiles = [...]string{ // FS loaders to bind shard files to ConfigOutput fields without // repeating the per-chain switch. func (s *chainSet) slots() []*string { - return []*string{&s.X, &s.C, &s.D, &s.Q, &s.A, &s.B, &s.T, &s.Z, &s.G, &s.K} + return []*string{&s.X, &s.C, &s.D, &s.Q, &s.A, &s.B, &s.F, &s.Z, &s.G, &s.K, &s.M} } // loadAllChainShards reads every primary-network chain shard from the @@ -535,10 +543,11 @@ func buildCanonicalGenesisFromSplitFiles(networkName string) ([]byte, error) { QChainGenesis: chainShards.Q, AChainGenesis: chainShards.A, BChainGenesis: chainShards.B, - TChainGenesis: chainShards.T, + FChainGenesis: chainShards.F, ZChainGenesis: chainShards.Z, GChainGenesis: chainShards.G, KChainGenesis: chainShards.K, + MChainGenesis: chainShards.M, Message: network.Message, } @@ -642,10 +651,11 @@ func buildGenesisFromDir(dir string) ([]byte, error) { QChainGenesis: chainShards.Q, AChainGenesis: chainShards.A, BChainGenesis: chainShards.B, - TChainGenesis: chainShards.T, + FChainGenesis: chainShards.F, ZChainGenesis: chainShards.Z, GChainGenesis: chainShards.G, KChainGenesis: chainShards.K, + MChainGenesis: chainShards.M, SecurityProfile: securityProfile, Message: network.Message, } diff --git a/configs/devnet/fchain.json b/configs/devnet/fchain.json new file mode 100644 index 0000000..87d7769 --- /dev/null +++ b/configs/devnet/fchain.json @@ -0,0 +1,21 @@ +{ + "aliases": [ + "F", + "fhe", + "fhevm" + ], + "description": "Threshold FHE confidential compute — off-EVM threshold DECRYPT (TFHE, ring N=1024 q=12289)", + "dkgParties": 10, + "dkgThreshold": 7, + "fhePrecompilePrefix": "0x07", + "message": "Lux F-Chain Genesis", + "mode": "fhe", + "name": "F-Chain", + "networkId": 3, + "ringDegreeN": 1024, + "ringModulusQ": 12289, + "timestamp": 1730531602, + "version": 1, + "vm": "ThresholdVM", + "chainId": 97270 +} diff --git a/configs/devnet/kchain.json b/configs/devnet/kchain.json index 677e768..96b6b73 100644 --- a/configs/devnet/kchain.json +++ b/configs/devnet/kchain.json @@ -1,17 +1,16 @@ { "aliases": [ - "M", - "mpc", - "mpcvm" + "K", + "key", + "keyvm", + "kms" ], - "description": "KMS \u2014 MPC key management + threshold sigs", + "description": "KMS — key management (ML-KEM / ML-DSA / BLS)", "message": "Lux K-Chain Genesis", - "mpcParties": 10, - "mpcThreshold": 7, "name": "K-Chain", "networkId": 3, "timestamp": 1730531602, "version": 1, "vm": "KeyVM", "chainId": 97170 -} \ No newline at end of file +} diff --git a/configs/devnet/mchain.json b/configs/devnet/mchain.json new file mode 100644 index 0000000..56c46f0 --- /dev/null +++ b/configs/devnet/mchain.json @@ -0,0 +1,22 @@ +{ + "aliases": [ + "M", + "mpc", + "mpcvm" + ], + "description": "Threshold MPC signing — off-EVM CGGMP21/FROST ceremony for bridge custody of external wallets", + "message": "Lux M-Chain Genesis", + "mode": "mpc", + "mpcParties": 10, + "mpcThreshold": 7, + "mpcVerifyPrecompiles": [ + "0x0800000000000000000000000000000000000002", + "0x0800000000000000000000000000000000000003" + ], + "name": "M-Chain", + "networkId": 3, + "timestamp": 1730531602, + "version": 1, + "vm": "ThresholdVM", + "chainId": 97370 +} diff --git a/configs/devnet/tchain.json b/configs/devnet/tchain.json deleted file mode 100644 index e0999be..0000000 --- a/configs/devnet/tchain.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "aliases": [ - "F", - "fhe", - "fhevm" - ], - "description": "Threshold FHE encrypted compute (ring N=1024 q=12289)", - "dkgParties": 10, - "dkgThreshold": 7, - "message": "Lux T-Chain Genesis", - "name": "T-Chain", - "networkId": 3, - "ringDegreeN": 1024, - "ringModulusQ": 12289, - "timestamp": 1730531602, - "version": 1, - "vm": "ThresholdVM", - "chainId": 96870 -} \ No newline at end of file diff --git a/configs/localnet/fchain.json b/configs/localnet/fchain.json new file mode 100644 index 0000000..2347172 --- /dev/null +++ b/configs/localnet/fchain.json @@ -0,0 +1,21 @@ +{ + "aliases": [ + "F", + "fhe", + "fhevm" + ], + "description": "Threshold FHE confidential compute — off-EVM threshold DECRYPT (TFHE, ring N=1024 q=12289)", + "dkgParties": 10, + "dkgThreshold": 7, + "fhePrecompilePrefix": "0x07", + "message": "Lux F-Chain Genesis", + "mode": "fhe", + "name": "F-Chain", + "networkId": 1337, + "ringDegreeN": 1024, + "ringModulusQ": 12289, + "timestamp": 1735689600, + "version": 1, + "vm": "ThresholdVM", + "chainId": 32327 +} diff --git a/configs/localnet/kchain.json b/configs/localnet/kchain.json index 824c9ec..2291fb0 100644 --- a/configs/localnet/kchain.json +++ b/configs/localnet/kchain.json @@ -1,17 +1,16 @@ { "aliases": [ - "M", - "mpc", - "mpcvm" + "K", + "key", + "keyvm", + "kms" ], - "description": "KMS \u2014 MPC key management + threshold sigs", + "description": "KMS — key management (ML-KEM / ML-DSA / BLS)", "message": "Lux K-Chain Genesis", - "mpcParties": 10, - "mpcThreshold": 7, "name": "K-Chain", "networkId": 1337, "timestamp": 1735689600, "version": 1, "vm": "KeyVM", "chainId": 32217 -} \ No newline at end of file +} diff --git a/configs/localnet/mchain.json b/configs/localnet/mchain.json new file mode 100644 index 0000000..e5aa624 --- /dev/null +++ b/configs/localnet/mchain.json @@ -0,0 +1,22 @@ +{ + "aliases": [ + "M", + "mpc", + "mpcvm" + ], + "description": "Threshold MPC signing — off-EVM CGGMP21/FROST ceremony for bridge custody of external wallets", + "message": "Lux M-Chain Genesis", + "mode": "mpc", + "mpcParties": 10, + "mpcThreshold": 7, + "mpcVerifyPrecompiles": [ + "0x0800000000000000000000000000000000000002", + "0x0800000000000000000000000000000000000003" + ], + "name": "M-Chain", + "networkId": 1337, + "timestamp": 1735689600, + "version": 1, + "vm": "ThresholdVM", + "chainId": 32437 +} diff --git a/configs/localnet/tchain.json b/configs/localnet/tchain.json deleted file mode 100644 index 20c37b7..0000000 --- a/configs/localnet/tchain.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "aliases": [ - "F", - "fhe", - "fhevm" - ], - "description": "Threshold FHE encrypted compute (ring N=1024 q=12289)", - "dkgParties": 10, - "dkgThreshold": 7, - "message": "Lux T-Chain Genesis", - "name": "T-Chain", - "networkId": 1337, - "ringDegreeN": 1024, - "ringModulusQ": 12289, - "timestamp": 1735689600, - "version": 1, - "vm": "ThresholdVM", - "chainId": 31887 -} \ No newline at end of file diff --git a/configs/lp134_split_test.go b/configs/lp134_split_test.go new file mode 100644 index 0000000..4a5ff44 --- /dev/null +++ b/configs/lp134_split_test.go @@ -0,0 +1,238 @@ +// Copyright (C) 2019-2026, Lux Industries, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package configs + +import ( + "encoding/json" + "os" + "path/filepath" + "strings" + "testing" +) + +// LP-134 chain-level decomplect invariants. +// +// The legacy T-Chain (ThresholdVM serving BOTH threshold-FHE and +// threshold-MPC) is split into two orthogonal chains — F-Chain (FHE only, +// confidential DECRYPT) and M-Chain (MPC only, CGGMP21/FROST bridge-custody +// SIGNING) — plus a K-Chain (KeyVM/KMS) cleaned of the MPC conflation +// ("K stays keyvm"). Both F and M run the same ThresholdVM substrate but are +// distinct chains with disjoint purposes, params, precompile families, and +// aliases. These tests fail closed if any concern leaks across the split: +// - no MPC field/alias/precompile-family on F-Chain, +// - no FHE field/alias/precompile-family on M-Chain, +// - no alias claimed by two of {F, M, K}, +// - the retired tchain.json is gone (forward-only). + +var lp134Nets = []string{"mainnet", "testnet", "devnet", "localnet"} + +func lp134ShardPath(net, letter string) string { + return filepath.Join("..", "configs", net, letter+"chain.json") +} + +func lp134ReadShard(t *testing.T, net, letter string) map[string]any { + t.Helper() + data, err := os.ReadFile(lp134ShardPath(net, letter)) + if err != nil { + t.Fatalf("%s/%schain.json: %v", net, letter, err) + } + var m map[string]any + if err := json.Unmarshal(data, &m); err != nil { + t.Fatalf("%s/%schain.json parse: %v", net, letter, err) + } + return m +} + +func lp134ShardText(t *testing.T, net, letter string) string { + t.Helper() + data, err := os.ReadFile(lp134ShardPath(net, letter)) + if err != nil { + t.Fatalf("%s/%schain.json: %v", net, letter, err) + } + return string(data) +} + +func lp134AliasSet(t *testing.T, m map[string]any) map[string]bool { + t.Helper() + raw, ok := m["aliases"].([]any) + if !ok { + t.Fatalf("aliases missing or wrong type: %T", m["aliases"]) + } + out := make(map[string]bool, len(raw)) + for _, a := range raw { + s, _ := a.(string) + out[strings.ToLower(s)] = true + } + return out +} + +// TestLP134_TChainRetired: the legacy tchain.json is gone on every net +// (forward-only split — F-Chain + M-Chain supersede it). +func TestLP134_TChainRetired(t *testing.T) { + for _, net := range lp134Nets { + if _, err := os.Stat(lp134ShardPath(net, "t")); !os.IsNotExist(err) { + t.Fatalf("%s/tchain.json must be retired (LP-134 forward-only split); stat err=%v", net, err) + } + } +} + +// TestLP134_FChainIsFHEOnly: F-Chain carries FHE concepts only — ThresholdVM +// in FHE mode, FHE ring params, the 0x07 FHE precompile family, and NO MPC +// field, alias, or 0x08 (threshold-signature) precompile-family reference. +func TestLP134_FChainIsFHEOnly(t *testing.T) { + for _, net := range lp134Nets { + t.Run(net, func(t *testing.T) { + m := lp134ReadShard(t, net, "f") + if got, _ := m["vm"].(string); got != "ThresholdVM" { + t.Fatalf("F-Chain vm = %q, want ThresholdVM", got) + } + if got, _ := m["mode"].(string); got != "fhe" { + t.Fatalf("F-Chain mode = %q, want fhe", got) + } + if m["ringDegreeN"] == nil || m["ringModulusQ"] == nil { + t.Fatalf("F-Chain must carry the FHE ring params (ringDegreeN/ringModulusQ)") + } + if got, _ := m["fhePrecompilePrefix"].(string); got != "0x07" { + t.Fatalf("F-Chain fhePrecompilePrefix = %q, want 0x07", got) + } + // Decomplect: no MPC concepts on F-Chain. + for _, banned := range []string{"mpcParties", "mpcThreshold", "mpcVerifyPrecompiles"} { + if m[banned] != nil { + t.Fatalf("F-Chain must not carry MPC field %q (LP-134 decomplect)", banned) + } + } + if strings.Contains(lp134ShardText(t, net, "f"), "0x08") { + t.Fatalf("F-Chain must not reference the 0x08 threshold-signature precompile family") + } + al := lp134AliasSet(t, m) + for _, want := range []string{"f", "fhe", "fhevm"} { + if !al[want] { + t.Fatalf("F-Chain aliases must include %q", want) + } + } + for _, banned := range []string{"m", "mpc", "mpcvm", "t", "threshold", "thresholdvm"} { + if al[banned] { + t.Fatalf("F-Chain must not claim alias %q", banned) + } + } + }) + } +} + +// TestLP134_MChainIsMPCOnly: M-Chain carries MPC concepts only — ThresholdVM +// in MPC mode, MPC params, the CGGMP21 (0x0800..03) + FROST (0x0800..02) +// verify precompiles, and NO FHE field, alias, or 0x07 precompile-family ref. +func TestLP134_MChainIsMPCOnly(t *testing.T) { + const ( + frostAddr = "0x0800000000000000000000000000000000000002" + cggmp21Addr = "0x0800000000000000000000000000000000000003" + ) + for _, net := range lp134Nets { + t.Run(net, func(t *testing.T) { + m := lp134ReadShard(t, net, "m") + if got, _ := m["vm"].(string); got != "ThresholdVM" { + t.Fatalf("M-Chain vm = %q, want ThresholdVM", got) + } + if got, _ := m["mode"].(string); got != "mpc" { + t.Fatalf("M-Chain mode = %q, want mpc", got) + } + if m["mpcParties"] == nil || m["mpcThreshold"] == nil { + t.Fatalf("M-Chain must carry the MPC params (mpcParties/mpcThreshold)") + } + // EVM verify surface: exactly the two threshold-signature precompiles. + pcs, ok := m["mpcVerifyPrecompiles"].([]any) + if !ok || len(pcs) != 2 { + t.Fatalf("M-Chain must declare its 2 MPC verify precompiles, got %v", m["mpcVerifyPrecompiles"]) + } + seen := map[string]bool{frostAddr: false, cggmp21Addr: false} + for _, p := range pcs { + ps, _ := p.(string) + ps = strings.ToLower(ps) + if _, known := seen[ps]; !known { + t.Fatalf("M-Chain unexpected precompile %q (want only FROST/CGGMP21)", ps) + } + seen[ps] = true + } + for addr, ok := range seen { + if !ok { + t.Fatalf("M-Chain missing MPC verify precompile %s", addr) + } + } + // Decomplect: no FHE concepts on M-Chain. + for _, banned := range []string{"ringDegreeN", "ringModulusQ", "dkgParties", "dkgThreshold", "fhePrecompilePrefix"} { + if m[banned] != nil { + t.Fatalf("M-Chain must not carry FHE field %q (LP-134 decomplect)", banned) + } + } + if strings.Contains(lp134ShardText(t, net, "m"), "0x07") { + t.Fatalf("M-Chain must not reference the 0x07 FHE precompile family") + } + al := lp134AliasSet(t, m) + for _, want := range []string{"m", "mpc", "mpcvm"} { + if !al[want] { + t.Fatalf("M-Chain aliases must include %q", want) + } + } + for _, banned := range []string{"f", "fhe", "fhevm", "t", "threshold", "thresholdvm"} { + if al[banned] { + t.Fatalf("M-Chain must not claim alias %q", banned) + } + } + }) + } +} + +// TestLP134_KChainIsKMSOnly: K-Chain is pure KeyVM/KMS — the M/mpc aliases and +// MPC params moved to M-Chain ("K stays keyvm" per LP-134). +func TestLP134_KChainIsKMSOnly(t *testing.T) { + for _, net := range lp134Nets { + t.Run(net, func(t *testing.T) { + m := lp134ReadShard(t, net, "k") + if got, _ := m["vm"].(string); got != "KeyVM" { + t.Fatalf("K-Chain vm = %q, want KeyVM", got) + } + for _, banned := range []string{"mpcParties", "mpcThreshold", "mpcVerifyPrecompiles", "mode", "ringDegreeN"} { + if m[banned] != nil { + t.Fatalf("K-Chain must not carry %q (LP-134: MPC/FHE concerns live on M/F)", banned) + } + } + al := lp134AliasSet(t, m) + if !al["k"] { + t.Fatalf("K-Chain must claim alias K") + } + for _, banned := range []string{"m", "mpc", "mpcvm", "f", "fhe", "fhevm"} { + if al[banned] { + t.Fatalf("K-Chain must not claim alias %q (LP-134 decomplect)", banned) + } + } + }) + } +} + +// TestLP134_NoAliasCollision: the threshold-family chains {F, M, K} claim +// mutually-disjoint alias sets — the core decomplect invariant (a single alias +// resolving to two chains is the exact bug LP-134 removes). +func TestLP134_NoAliasCollision(t *testing.T) { + for _, net := range lp134Nets { + t.Run(net, func(t *testing.T) { + f := lp134AliasSet(t, lp134ReadShard(t, net, "f")) + mm := lp134AliasSet(t, lp134ReadShard(t, net, "m")) + k := lp134AliasSet(t, lp134ReadShard(t, net, "k")) + for _, pair := range []struct { + a, b map[string]bool + an, bn string + }{ + {f, mm, "F", "M"}, + {f, k, "F", "K"}, + {mm, k, "M", "K"}, + } { + for alias := range pair.a { + if pair.b[alias] { + t.Fatalf("%s-Chain alias %q collides with %s-Chain (LP-134: aliases must be disjoint)", pair.an, alias, pair.bn) + } + } + } + }) + } +} diff --git a/configs/mainnet/fchain.json b/configs/mainnet/fchain.json new file mode 100644 index 0000000..7f5a61e --- /dev/null +++ b/configs/mainnet/fchain.json @@ -0,0 +1,21 @@ +{ + "aliases": [ + "F", + "fhe", + "fhevm" + ], + "description": "Threshold FHE confidential compute — off-EVM threshold DECRYPT (TFHE, ring N=1024 q=12289)", + "dkgParties": 10, + "dkgThreshold": 7, + "fhePrecompilePrefix": "0x07", + "message": "Lux F-Chain Genesis", + "mode": "fhe", + "name": "F-Chain", + "networkId": 1, + "ringDegreeN": 1024, + "ringModulusQ": 12289, + "timestamp": 1730446786, + "version": 1, + "vm": "ThresholdVM", + "chainId": 97269 +} diff --git a/configs/mainnet/kchain.json b/configs/mainnet/kchain.json index b340e10..4546758 100644 --- a/configs/mainnet/kchain.json +++ b/configs/mainnet/kchain.json @@ -1,17 +1,16 @@ { "aliases": [ - "M", - "mpc", - "mpcvm" + "K", + "key", + "keyvm", + "kms" ], - "description": "KMS \u2014 MPC key management + threshold sigs", + "description": "KMS — key management (ML-KEM / ML-DSA / BLS)", "message": "Lux K-Chain Genesis", - "mpcParties": 10, - "mpcThreshold": 7, "name": "K-Chain", "networkId": 1, "timestamp": 1730446786, "version": 1, "vm": "KeyVM", "chainId": 97169 -} \ No newline at end of file +} diff --git a/configs/mainnet/mchain.json b/configs/mainnet/mchain.json new file mode 100644 index 0000000..aea21d4 --- /dev/null +++ b/configs/mainnet/mchain.json @@ -0,0 +1,22 @@ +{ + "aliases": [ + "M", + "mpc", + "mpcvm" + ], + "description": "Threshold MPC signing — off-EVM CGGMP21/FROST ceremony for bridge custody of external wallets", + "message": "Lux M-Chain Genesis", + "mode": "mpc", + "mpcParties": 10, + "mpcThreshold": 7, + "mpcVerifyPrecompiles": [ + "0x0800000000000000000000000000000000000002", + "0x0800000000000000000000000000000000000003" + ], + "name": "M-Chain", + "networkId": 1, + "timestamp": 1730446786, + "version": 1, + "vm": "ThresholdVM", + "chainId": 97369 +} diff --git a/configs/mainnet/tchain.json b/configs/mainnet/tchain.json deleted file mode 100644 index c422d0e..0000000 --- a/configs/mainnet/tchain.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "aliases": [ - "F", - "fhe", - "fhevm" - ], - "description": "Threshold FHE encrypted compute (ring N=1024 q=12289)", - "dkgParties": 10, - "dkgThreshold": 7, - "message": "Lux T-Chain Genesis", - "name": "T-Chain", - "networkId": 1, - "ringDegreeN": 1024, - "ringModulusQ": 12289, - "timestamp": 1730446786, - "version": 1, - "vm": "ThresholdVM", - "chainId": 96869 -} \ No newline at end of file diff --git a/configs/testnet/fchain.json b/configs/testnet/fchain.json new file mode 100644 index 0000000..c219197 --- /dev/null +++ b/configs/testnet/fchain.json @@ -0,0 +1,21 @@ +{ + "aliases": [ + "F", + "fhe", + "fhevm" + ], + "description": "Threshold FHE confidential compute — off-EVM threshold DECRYPT (TFHE, ring N=1024 q=12289)", + "dkgParties": 10, + "dkgThreshold": 7, + "fhePrecompilePrefix": "0x07", + "message": "Lux F-Chain Genesis", + "mode": "fhe", + "name": "F-Chain", + "networkId": 2, + "ringDegreeN": 1024, + "ringModulusQ": 12289, + "timestamp": 1730531602, + "version": 1, + "vm": "ThresholdVM", + "chainId": 97268 +} diff --git a/configs/testnet/kchain.json b/configs/testnet/kchain.json index 88511f5..81ef66d 100644 --- a/configs/testnet/kchain.json +++ b/configs/testnet/kchain.json @@ -1,17 +1,16 @@ { "aliases": [ - "M", - "mpc", - "mpcvm" + "K", + "key", + "keyvm", + "kms" ], - "description": "KMS \u2014 MPC key management + threshold sigs", + "description": "KMS — key management (ML-KEM / ML-DSA / BLS)", "message": "Lux K-Chain Genesis", - "mpcParties": 10, - "mpcThreshold": 7, "name": "K-Chain", "networkId": 2, "timestamp": 1730531602, "version": 1, "vm": "KeyVM", "chainId": 97168 -} \ No newline at end of file +} diff --git a/configs/testnet/mchain.json b/configs/testnet/mchain.json new file mode 100644 index 0000000..4fd8ab5 --- /dev/null +++ b/configs/testnet/mchain.json @@ -0,0 +1,22 @@ +{ + "aliases": [ + "M", + "mpc", + "mpcvm" + ], + "description": "Threshold MPC signing — off-EVM CGGMP21/FROST ceremony for bridge custody of external wallets", + "message": "Lux M-Chain Genesis", + "mode": "mpc", + "mpcParties": 10, + "mpcThreshold": 7, + "mpcVerifyPrecompiles": [ + "0x0800000000000000000000000000000000000002", + "0x0800000000000000000000000000000000000003" + ], + "name": "M-Chain", + "networkId": 2, + "timestamp": 1730531602, + "version": 1, + "vm": "ThresholdVM", + "chainId": 97368 +} diff --git a/configs/testnet/tchain.json b/configs/testnet/tchain.json deleted file mode 100644 index 9f702e9..0000000 --- a/configs/testnet/tchain.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "aliases": [ - "F", - "fhe", - "fhevm" - ], - "description": "Threshold FHE encrypted compute (ring N=1024 q=12289)", - "dkgParties": 10, - "dkgThreshold": 7, - "message": "Lux T-Chain Genesis", - "name": "T-Chain", - "networkId": 2, - "ringDegreeN": 1024, - "ringModulusQ": 12289, - "timestamp": 1730531602, - "version": 1, - "vm": "ThresholdVM", - "chainId": 96868 -} \ No newline at end of file diff --git a/go.sum b/go.sum index a1e4ac1..8a9f2b4 100644 --- a/go.sum +++ b/go.sum @@ -79,11 +79,11 @@ github.com/luxfi/cache v1.2.1 h1:kAzOS55/hmYeNKR+0HAKv4ma48Y6JjkI8UQeqdZ8bfI= github.com/luxfi/cache v1.2.1/go.mod h1:co7JTxZZHpKT31Yh01LFp5aZOxmoUg157FhBLQdQHVU= github.com/luxfi/codec v1.1.4 h1:Yl8ZalMNkqo7cD6R9AjczAajkLOmsjyZ9+DASVYHrvg= github.com/luxfi/codec v1.1.4/go.mod h1:oGQ3j6E8c2P0pL0irYtWkrB1hmDUFIE0puXHK4gV5KI= -github.com/luxfi/constants v1.5.7 h1:a1tCMdxd+pClPMIPOaI9vcYGNy6cQIc2rubac2Trc0k= +github.com/luxfi/constants v1.5.7 h1:wGGcFAvFuKNyAiWpubcMuHFtpkSMZo+pdETQWH9Xc1o= github.com/luxfi/constants v1.5.7/go.mod h1:z+Wc7skybZAA+xuBWNcmtv402S/BFqixL+FiSQXPg8U= github.com/luxfi/container v0.0.4 h1:BXhF82WyfqVP5mjlNcr7tP0Fcnvl0Ap1rkiu+rq5XuM= github.com/luxfi/container v0.0.4/go.mod h1:Z3SpmMF5d4t77MM0nHYXURpn+EMVaeu1fhbd/3BGaek= -github.com/luxfi/crypto v1.19.15 h1:Tf+iPRXv08Rlj9k7iNtYy0e1tJWIgRcY0pTR4rQax6w= +github.com/luxfi/crypto v1.19.15 h1:pypmy1GhlMIIk+/PXe5pIv2uzHcEyB2xij7F2IcuP5U= github.com/luxfi/crypto v1.19.15/go.mod h1:INjdZtke85k8hX/QAmTMAY8bbZ4gzGZQLqURg3xf6Gk= github.com/luxfi/geth v1.16.98 h1:w187TtKuGStf3tm2bshuHVKBv2Frjx0lT54kQVXyNHA= github.com/luxfi/geth v1.16.98/go.mod h1:6kEzSExdk9CPQDPXALt6P3HfQqBq7KF1Jrrv9gBpxbU= diff --git a/pkg/genesis/chain_mapping.go b/pkg/genesis/chain_mapping.go index 80301d3..4de45b9 100644 --- a/pkg/genesis/chain_mapping.go +++ b/pkg/genesis/chain_mapping.go @@ -14,17 +14,21 @@ import ( type ChainRole string const ( - RoleProtocol ChainRole = "P" // Protocol chain — validator set, chain registry, ordering - RoleExchange ChainRole = "X" // Exchange/asset transfer chain - RoleContract ChainRole = "C" // Contract/EVM chain - RoleQuantum ChainRole = "Q" // Quantum-resistant chain - RoleAttest ChainRole = "A" // Attestation chain - RoleBridge ChainRole = "B" // Bridge chain - RoleThreshold ChainRole = "T" // Threshold/MPC chain - RoleZK ChainRole = "Z" // Zero-knowledge chain - RoleGraph ChainRole = "G" // Graph/data chain (future) - RoleIdentity ChainRole = "I" // Identity chain (future) - RoleKMS ChainRole = "K" // KMS chain (future) + RoleProtocol ChainRole = "P" // Protocol chain — validator set, chain registry, ordering + RoleExchange ChainRole = "X" // Exchange/asset transfer chain + RoleContract ChainRole = "C" // Contract/EVM chain + RoleQuantum ChainRole = "Q" // Quantum-resistant chain + RoleAttest ChainRole = "A" // Attestation chain + RoleBridge ChainRole = "B" // Bridge chain + // LP-134: the retired RoleThreshold ("T") is split into RoleFHE ("F") and + // RoleMPC ("M"). The ThresholdVM substrate serves both — F-Chain in FHE + // mode (confidential compute) and M-Chain in MPC mode (bridge custody). + RoleFHE ChainRole = "F" // Threshold-FHE chain (confidential compute) + RoleMPC ChainRole = "M" // Threshold-MPC chain (bridge custody signing) + RoleZK ChainRole = "Z" // Zero-knowledge chain + RoleGraph ChainRole = "G" // Graph/data chain (future) + RoleIdentity ChainRole = "I" // Identity chain (future) + RoleKMS ChainRole = "K" // KMS chain (future) ) // ChainConfig represents a single chain's configuration @@ -228,12 +232,28 @@ func DefaultMainnetMapping() *ChainMapping { Name: "Bridge Chain", Aliases: []string{"B", "bridge"}, }, - RoleThreshold: { - ChainID: ids.TChainID, - VMID: ids.Empty, // Threshold VM ID - Role: RoleThreshold, - Name: "Threshold Chain", - Aliases: []string{"T", "threshold", "mpc"}, + RoleFHE: { + // LP-134: F-Chain runs ThresholdVM in FHE mode. The canonical + // native chain ID awaits luxfi/ids.FChainID — the ids native- + // chain set (P/C/X/Q/A/B/T/Z/G/I/K/D) is closed, so adding F/M + // is a separate luxfi/ids change. The active genesis path pins + // the EVM chainId in fchain.json (97269/97268/97270/32327). + ChainID: ids.Empty, + VMID: ids.Empty, // ThresholdVM (FHE mode) + Role: RoleFHE, + Name: "F-Chain", + Aliases: []string{"F", "fhe", "fhevm"}, + }, + RoleMPC: { + // LP-134: M-Chain runs ThresholdVM in MPC mode (CGGMP21 / FROST + // bridge custody). Canonical native chain ID awaits + // luxfi/ids.MChainID; the active genesis path pins the EVM + // chainId in mchain.json (97369/97368/97370/32437). + ChainID: ids.Empty, + VMID: ids.Empty, // ThresholdVM (MPC mode) + Role: RoleMPC, + Name: "M-Chain", + Aliases: []string{"M", "mpc", "mpcvm"}, }, RoleZK: { ChainID: ids.ZChainID, diff --git a/pkg/genesis/config.go b/pkg/genesis/config.go index e64ffdd..fc152bf 100644 --- a/pkg/genesis/config.go +++ b/pkg/genesis/config.go @@ -104,10 +104,11 @@ func GetConfigFile(filepath string) (*Config, error) { QChainGenesis: output.QChainGenesis, AChainGenesis: output.AChainGenesis, BChainGenesis: output.BChainGenesis, - TChainGenesis: output.TChainGenesis, + FChainGenesis: output.FChainGenesis, ZChainGenesis: output.ZChainGenesis, GChainGenesis: output.GChainGenesis, KChainGenesis: output.KChainGenesis, + MChainGenesis: output.MChainGenesis, SecurityProfile: output.SecurityProfile, CertPolicy: output.CertPolicy, Message: output.Message, @@ -247,10 +248,11 @@ var optionalChainShards = []struct { {"qchain.json", func(c *Config) *string { return &c.QChainGenesis }}, {"achain.json", func(c *Config) *string { return &c.AChainGenesis }}, {"bchain.json", func(c *Config) *string { return &c.BChainGenesis }}, - {"tchain.json", func(c *Config) *string { return &c.TChainGenesis }}, + {"fchain.json", func(c *Config) *string { return &c.FChainGenesis }}, {"zchain.json", func(c *Config) *string { return &c.ZChainGenesis }}, {"gchain.json", func(c *Config) *string { return &c.GChainGenesis }}, {"kchain.json", func(c *Config) *string { return &c.KChainGenesis }}, + {"mchain.json", func(c *Config) *string { return &c.MChainGenesis }}, } // readOptionalShard reads filename from dir, returning the file contents on @@ -390,10 +392,11 @@ func ParseConfigOutput(output *ConfigOutput, networkID uint32) (*Config, error) QChainGenesis: output.QChainGenesis, AChainGenesis: output.AChainGenesis, BChainGenesis: output.BChainGenesis, - TChainGenesis: output.TChainGenesis, + FChainGenesis: output.FChainGenesis, ZChainGenesis: output.ZChainGenesis, GChainGenesis: output.GChainGenesis, KChainGenesis: output.KChainGenesis, + MChainGenesis: output.MChainGenesis, Chains: output.Chains, SecurityProfile: output.SecurityProfile, CertPolicy: output.CertPolicy, diff --git a/pkg/genesis/types.go b/pkg/genesis/types.go index c64dc06..82d3b06 100644 --- a/pkg/genesis/types.go +++ b/pkg/genesis/types.go @@ -26,14 +26,15 @@ type Config struct { QChainGenesis string `json:"qChainGenesis,omitempty"` // Q-Chain: Quantum VM genesis AChainGenesis string `json:"aChainGenesis,omitempty"` // A-Chain: Attestation/AI VM genesis BChainGenesis string `json:"bChainGenesis,omitempty"` // B-Chain: Bridge VM genesis - TChainGenesis string `json:"tChainGenesis,omitempty"` // T-Chain: Threshold VM genesis + FChainGenesis string `json:"fChainGenesis,omitempty"` // F-Chain: Threshold-FHE VM genesis (LP-134; supersedes T-Chain) ZChainGenesis string `json:"zChainGenesis,omitempty"` // Z-Chain: ZK VM genesis GChainGenesis string `json:"gChainGenesis,omitempty"` // G-Chain: Graph VM genesis KChainGenesis string `json:"kChainGenesis,omitempty"` // K-Chain: KMS VM genesis + MChainGenesis string `json:"mChainGenesis,omitempty"` // M-Chain: Threshold-MPC VM genesis (LP-134; supersedes T-Chain) Message string `json:"message"` // Chains defines additional chains to include in genesis beyond the - // built-in alphabet chains (C, D, Q, B, T, Z, G, K). Each entry becomes + // built-in alphabet chains (C, D, Q, A, B, F, Z, G, K, M). Each entry becomes // a CreateChainTx in the P-Chain genesis. This allows chains to be // embedded directly for automatic bootstrap. Chains []ChainEntry `json:"chains,omitempty"` @@ -73,7 +74,8 @@ type Config struct { // // EVMAddr is the 20-byte H160 EVM address (C-Chain and other EVM chains). // UTXOAddr is the 20-byte ShortID used by both P-Chain and X-Chain UTXOs -// (same bytes; bech32 prefix differentiates the chain). +// +// (same bytes; bech32 prefix differentiates the chain). type Allocation struct { EVMAddr ids.ShortID `json:"evmAddr"` UTXOAddr ids.ShortID `json:"utxoAddr"` @@ -201,7 +203,6 @@ type StakerJSON struct { EndTime uint64 `json:"endTime,omitempty"` } - // CChainConfig is the C-Chain genesis configuration type CChainConfig struct { Config CChainParams `json:"config"` @@ -308,10 +309,11 @@ type ConfigOutput struct { QChainGenesis string `json:"qChainGenesis,omitempty"` // Q-Chain: Quantum VM genesis AChainGenesis string `json:"aChainGenesis,omitempty"` // A-Chain: Attestation/AI VM genesis BChainGenesis string `json:"bChainGenesis,omitempty"` // B-Chain: Bridge VM genesis - TChainGenesis string `json:"tChainGenesis,omitempty"` // T-Chain: Threshold VM genesis + FChainGenesis string `json:"fChainGenesis,omitempty"` // F-Chain: Threshold-FHE VM genesis (LP-134; supersedes T-Chain) ZChainGenesis string `json:"zChainGenesis,omitempty"` // Z-Chain: ZK VM genesis GChainGenesis string `json:"gChainGenesis,omitempty"` // G-Chain: Graph VM genesis KChainGenesis string `json:"kChainGenesis,omitempty"` // K-Chain: KMS VM genesis + MChainGenesis string `json:"mChainGenesis,omitempty"` // M-Chain: Threshold-MPC VM genesis (LP-134; supersedes T-Chain) Chains []ChainEntry `json:"chains,omitempty"` // Additional genesis chains SecurityProfile *SecurityProfile `json:"securityProfile,omitempty"` CertPolicy *CertPolicy `json:"certPolicy,omitempty"` @@ -370,10 +372,11 @@ func (c *Config) ToJSON(hrp string) *ConfigOutput { QChainGenesis: c.QChainGenesis, AChainGenesis: c.AChainGenesis, BChainGenesis: c.BChainGenesis, - TChainGenesis: c.TChainGenesis, + FChainGenesis: c.FChainGenesis, ZChainGenesis: c.ZChainGenesis, GChainGenesis: c.GChainGenesis, KChainGenesis: c.KChainGenesis, + MChainGenesis: c.MChainGenesis, SecurityProfile: c.SecurityProfile, CertPolicy: c.CertPolicy, Message: c.Message, @@ -385,4 +388,3 @@ func (c *Config) MarshalJSON() ([]byte, error) { hrp := constants.GetHRP(c.NetworkID) return json.Marshal(c.ToJSON(hrp)) } -