mirror of
https://github.com/luxfi/node.git
synced 2026-07-27 03:06:23 +00:00
genesis: install the M-Chain plugin under the vmID genesis actually declares
The build installed M-Chain's plugin binary as tGVBwRxpmD2aFdg3iYjgRvrCe8Jcmq9UNKxyHMus2NZ8WcD8t — the CB58 of the retired `thresholdvm` identifier — while the genesis builder declares the chain with constants.MPCVMID (qCURact1n41FcoNBch8iMVBwc9AWie48D118ZNJ5tBdWrvryS). The plugin registry resolves a CreateChainTx's vmID to an implementation by filename, so the two never met: M-Chain was declared in genesis and no node could start it. The Dockerfile comment had already been renamed to "mpcvm" without the CB58 being recomputed, which is why it read as correct. Fixes all five sites (plugin build target, the build-verify list, the runtime COPY, the comment, and publish_plugin_set.sh) and adds genesis/builder/mchain_test.go, which pins the vmID literally and asserts M-Chain is present in the height-0 chain set for mainnet, testnet and local. A vmID is an immutable one-way door once a chain is created with it, so it is now covered by a test rather than by five copies of a string. Bumps luxfi/genesis to v1.16.3, where mchain.json states its quorum as "policy": "7-of-10" instead of a bare mpcThreshold that reads as the signer count to an operator and as the polynomial degree to a library. Co-authored-by: Hanzo Dev <dev@hanzo.ai>
This commit is contained in:
+4
-4
@@ -322,7 +322,7 @@ RUN --mount=type=cache,target=/root/.cache/go-build \
|
||||
# oraclevm -> r5m1ujrmXxVcQetG3CQfuDLHp2RHKh6vCDaFgBRQfUcTZh7eS
|
||||
# quantumvm -> ry9Sg8rZdT26iEKvJDmC2wkESs4SDKgZEhk5BgLSwg1EpcNug
|
||||
# relayvm -> sP6dLqrrBR9w3soP18fbJ3YzZecZdD7DDdfH2cFhhLq7Hy9bz
|
||||
# mpcvm -> tGVBwRxpmD2aFdg3iYjgRvrCe8Jcmq9UNKxyHMus2NZ8WcD8t
|
||||
# mpcvm -> qCURact1n41FcoNBch8iMVBwc9AWie48D118ZNJ5tBdWrvryS
|
||||
# zkvm -> vv3qPfyTVXZ5ArRZA9Jh4hbYDTBe43f7sgQg4CHfNg1rnnvX9
|
||||
|
||||
# MUST track node's go.mod luxfi/chains (the D-Chain dexvm + 10 VM plugins).
|
||||
@@ -366,7 +366,7 @@ RUN --mount=type=cache,target=/root/.cache/go-build \
|
||||
( cd /tmp/chains/relayvm && CGO_ENABLED=0 GOFLAGS=-mod=mod go build -ldflags="-s -w" \
|
||||
-o /luxd/build/plugins/sP6dLqrrBR9w3soP18fbJ3YzZecZdD7DDdfH2cFhhLq7Hy9bz ./cmd/plugin ) || echo "WARN: relayvm plugin build skipped" ; \
|
||||
( cd /tmp/chains/mpcvm && CGO_ENABLED=0 GOFLAGS=-mod=mod go build -ldflags="-s -w" \
|
||||
-o /luxd/build/plugins/tGVBwRxpmD2aFdg3iYjgRvrCe8Jcmq9UNKxyHMus2NZ8WcD8t ./cmd/plugin ) || echo "WARN: mpcvm plugin build skipped" ; \
|
||||
-o /luxd/build/plugins/qCURact1n41FcoNBch8iMVBwc9AWie48D118ZNJ5tBdWrvryS ./cmd/plugin ) || echo "WARN: mpcvm plugin build skipped" ; \
|
||||
( cd /tmp/chains/zkvm && CGO_ENABLED=0 GOFLAGS=-mod=mod go build -ldflags="-s -w" \
|
||||
-o /luxd/build/plugins/vv3qPfyTVXZ5ArRZA9Jh4hbYDTBe43f7sgQg4CHfNg1rnnvX9 ./cmd/plugin ) || echo "WARN: zkvm plugin build skipped" ; \
|
||||
( chmod +x /luxd/build/plugins/* 2>/dev/null || true ) && \
|
||||
@@ -379,7 +379,7 @@ RUN --mount=type=cache,target=/root/.cache/go-build \
|
||||
r5m1ujrmXxVcQetG3CQfuDLHp2RHKh6vCDaFgBRQfUcTZh7eS \
|
||||
ry9Sg8rZdT26iEKvJDmC2wkESs4SDKgZEhk5BgLSwg1EpcNug \
|
||||
sP6dLqrrBR9w3soP18fbJ3YzZecZdD7DDdfH2cFhhLq7Hy9bz \
|
||||
tGVBwRxpmD2aFdg3iYjgRvrCe8Jcmq9UNKxyHMus2NZ8WcD8t \
|
||||
qCURact1n41FcoNBch8iMVBwc9AWie48D118ZNJ5tBdWrvryS \
|
||||
vv3qPfyTVXZ5ArRZA9Jh4hbYDTBe43f7sgQg4CHfNg1rnnvX9 ; do \
|
||||
test -s /luxd/build/plugins/$p \
|
||||
|| { echo "FATAL: required chain-VM plugin $p missing/empty — its build failed above (see the matching WARN line); the runtime-stage hard COPY would otherwise fail cryptically. Surface & fix the real Go build error, or remove the plugin from BOTH the build list and the runtime COPY."; exit 1; } ; \
|
||||
@@ -489,7 +489,7 @@ COPY --from=builder \
|
||||
/luxd/build/plugins/r5m1ujrmXxVcQetG3CQfuDLHp2RHKh6vCDaFgBRQfUcTZh7eS \
|
||||
/luxd/build/plugins/ry9Sg8rZdT26iEKvJDmC2wkESs4SDKgZEhk5BgLSwg1EpcNug \
|
||||
/luxd/build/plugins/sP6dLqrrBR9w3soP18fbJ3YzZecZdD7DDdfH2cFhhLq7Hy9bz \
|
||||
/luxd/build/plugins/tGVBwRxpmD2aFdg3iYjgRvrCe8Jcmq9UNKxyHMus2NZ8WcD8t \
|
||||
/luxd/build/plugins/qCURact1n41FcoNBch8iMVBwc9AWie48D118ZNJ5tBdWrvryS \
|
||||
/luxd/build/plugins/vv3qPfyTVXZ5ArRZA9Jh4hbYDTBe43f7sgQg4CHfNg1rnnvX9 \
|
||||
/luxd/build/plugins/
|
||||
WORKDIR /luxd/build
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
// Copyright (C) 2019-2026, Lux Industries Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package builder
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/luxfi/constants"
|
||||
"github.com/luxfi/node/vms/platformvm/genesis"
|
||||
pchaintxs "github.com/luxfi/node/vms/platformvm/txs"
|
||||
)
|
||||
|
||||
// parsePolicy reads the operator form "3-of-5" into (K, N).
|
||||
//
|
||||
// The canonical parser is luxfi/threshold/pkg/quorum, but the node's genesis
|
||||
// builder is a boot-critical path and does not take a dependency on a
|
||||
// threshold-crypto library to read three integers. What this test pins is the
|
||||
// FORM — that the genesis blob states a quorum in a spelling that cannot be
|
||||
// confused with a polynomial degree — and the form is checkable here.
|
||||
func parsePolicy(t *testing.T, s string) (k, n int) {
|
||||
t.Helper()
|
||||
_, err := fmt.Sscanf(s, "%d-of-%d", &k, &n)
|
||||
require.NoErrorf(t, err, "policy %q is not in the operator form \"3-of-5\"", s)
|
||||
require.Greaterf(t, k, 1, "policy %q: k=1 is not a threshold policy", s)
|
||||
require.LessOrEqualf(t, k, n, "policy %q can never be satisfied", s)
|
||||
return k, n
|
||||
}
|
||||
|
||||
// canonicalMPCVMID is M-Chain's vmID in the encoding the node's plugin registry
|
||||
// resolves against. Pinned literally: the plugin binary's FILENAME must equal
|
||||
// it, and a change that leaves the two out of step produces a chain that is
|
||||
// declared in genesis but that no node can start.
|
||||
//
|
||||
// The predecessor value, tGVBwRxpmD2aFdg3iYjgRvrCe8Jcmq9UNKxyHMus2NZ8WcD8t, is
|
||||
// the retired `thresholdvm` identifier. It was what the Dockerfile installed
|
||||
// the plugin under while genesis declared MPCVMID, so the two never met.
|
||||
const canonicalMPCVMID = "qCURact1n41FcoNBch8iMVBwc9AWie48D118ZNJ5tBdWrvryS"
|
||||
|
||||
// M-Chain must be a GENESIS chain — in the P-Chain's chain set at height 0,
|
||||
// tracked by every validator from boot — not a chain created later by a
|
||||
// CreateChainTx someone has to remember to submit.
|
||||
//
|
||||
// The difference matters for custody specifically. A post-genesis chain exists
|
||||
// only from the height its tx landed, so a node syncing from genesis has a
|
||||
// window with no custody state, and the chain's very existence depends on an
|
||||
// operator action that can be forgotten or fumbled. Bridged funds should not
|
||||
// depend on that.
|
||||
func TestMChainIsAGenesisChain(t *testing.T) {
|
||||
for _, networkID := range []uint32{
|
||||
constants.MainnetID,
|
||||
constants.TestnetID,
|
||||
constants.LocalID,
|
||||
} {
|
||||
cfg := GetConfig(networkID)
|
||||
require.NotNilf(t, cfg, "network %d has no genesis config", networkID)
|
||||
require.NotEmptyf(t, cfg.MChainGenesis,
|
||||
"network %d carries no mchain.json, so the builder skips M-Chain entirely", networkID)
|
||||
|
||||
raw, _, err := FromConfig(cfg)
|
||||
require.NoErrorf(t, err, "network %d genesis build", networkID)
|
||||
|
||||
parsed, err := genesis.Parse(raw)
|
||||
require.NoErrorf(t, err, "network %d genesis parse", networkID)
|
||||
|
||||
// Genesis chains are CreateChainTx entries executed at height 0 — the
|
||||
// chain exists from the first block, without anyone submitting a tx.
|
||||
var found *pchaintxs.CreateChainTx
|
||||
for _, c := range parsed.Chains {
|
||||
u, ok := c.Unsigned.(*pchaintxs.CreateChainTx)
|
||||
require.True(t, ok, "a genesis chain entry must be a CreateChainTx")
|
||||
if u.VMID() == constants.MPCVMID {
|
||||
found = u
|
||||
break
|
||||
}
|
||||
}
|
||||
require.NotNilf(t, found,
|
||||
"network %d: no genesis chain with vmID %s (M-Chain)", networkID, constants.MPCVMID)
|
||||
require.Equal(t, "M-Chain", found.BlockchainName())
|
||||
require.Equalf(t, []byte(cfg.MChainGenesis), found.GenesisData(),
|
||||
"network %d: the VM must receive exactly the mchain.json bytes", networkID)
|
||||
require.Equalf(t, canonicalMPCVMID, found.VMID().String(),
|
||||
"network %d: genesis declares a vmID the plugin registry will look up by filename", networkID)
|
||||
}
|
||||
}
|
||||
|
||||
// The genesis blob decides how many custodians must cooperate to move bridged
|
||||
// funds, so it must state that in a form nothing can misread as a polynomial
|
||||
// degree. A bare `"threshold": 3` reads as the signer count to an operator and
|
||||
// as the degree to every threshold library, and those differ by one.
|
||||
func TestMChainGenesisPolicyIsUnambiguousAndDeployable(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
networkID uint32
|
||||
want string
|
||||
}{
|
||||
{constants.MainnetID, "7-of-10"},
|
||||
{constants.TestnetID, "3-of-5"},
|
||||
{constants.LocalID, "3-of-5"},
|
||||
} {
|
||||
cfg := GetConfig(tc.networkID)
|
||||
require.NotNil(t, cfg)
|
||||
|
||||
var blob struct {
|
||||
Policy string `json:"policy"`
|
||||
VM string `json:"vm"`
|
||||
}
|
||||
require.NoErrorf(t, json.Unmarshal([]byte(cfg.MChainGenesis), &blob),
|
||||
"network %d mchain.json must decode, policy included", tc.networkID)
|
||||
|
||||
require.Equalf(t, tc.want, blob.Policy, "network %d policy", tc.networkID)
|
||||
k, n := parsePolicy(t, blob.Policy)
|
||||
require.Greaterf(t, 2*k, n,
|
||||
"network %d: %s admits two disjoint quorums, so two halves of the committee could authorise contradictory releases",
|
||||
tc.networkID, blob.Policy)
|
||||
require.Equalf(t, "mpcvm", blob.VM,
|
||||
"network %d must name the current VM, not the retired ThresholdVM", tc.networkID)
|
||||
}
|
||||
}
|
||||
|
||||
// The ambiguous fields must be gone, not merely ignored. Leaving them in place
|
||||
// means the next reader has two numbers to choose between, and the whole point
|
||||
// of the policy field is that there is exactly one.
|
||||
func TestMChainGenesisHasNoAmbiguousThresholdFields(t *testing.T) {
|
||||
for _, networkID := range []uint32{constants.MainnetID, constants.TestnetID, constants.LocalID} {
|
||||
cfg := GetConfig(networkID)
|
||||
require.NotNil(t, cfg)
|
||||
|
||||
var blob map[string]any
|
||||
require.NoError(t, json.Unmarshal([]byte(cfg.MChainGenesis), &blob))
|
||||
for _, dead := range []string{"mpcThreshold", "mpcParties", "threshold", "totalParties"} {
|
||||
require.NotContainsf(t, blob, dead,
|
||||
"network %d mchain.json still carries %q; the policy field is the only quorum statement",
|
||||
networkID, dead)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -126,7 +126,7 @@ require (
|
||||
github.com/luxfi/constants v1.6.2
|
||||
github.com/luxfi/container v0.2.1
|
||||
github.com/luxfi/filesystem v0.0.1
|
||||
github.com/luxfi/genesis v1.16.2
|
||||
github.com/luxfi/genesis v1.16.3
|
||||
github.com/luxfi/genesis/pkg/genesis/security v1.13.8
|
||||
github.com/luxfi/geth v1.20.1
|
||||
github.com/luxfi/go-bip39 v1.2.0
|
||||
@@ -196,6 +196,7 @@ require (
|
||||
github.com/luxfi/dkg v0.3.5 // indirect
|
||||
github.com/luxfi/lattice/v7 v7.1.4 // indirect
|
||||
github.com/luxfi/lens v0.2.1 // indirect
|
||||
github.com/luxfi/light v1.0.0 // indirect
|
||||
github.com/luxfi/magnetar v1.2.3 // indirect
|
||||
github.com/luxfi/mdns v0.1.1 // indirect
|
||||
github.com/luxfi/mlwe v0.3.0 // indirect
|
||||
|
||||
@@ -329,16 +329,14 @@ github.com/luxfi/crypto/ipa v1.2.4 h1:6xfwhI9/HrcDkF3Ti5/NxsNQIWbwYDJmRSNIHRQ/xf
|
||||
github.com/luxfi/crypto/ipa v1.2.4/go.mod h1:43J6f6rcfUMrZt4cQectMOZb6Ps+fAEj8ZTPC3Kk+gE=
|
||||
github.com/luxfi/database v1.21.1 h1:GNnoWVa82l+n2dK7x2aG8LR2NEToq6ZCRX0sQjmK0OM=
|
||||
github.com/luxfi/database v1.21.1/go.mod h1:Gc7Z2OPrrcYLnAL8B1trOnguXauOlSDV5tkviLN6Xec=
|
||||
github.com/luxfi/dex v1.14.0 h1:4PtorVbRmbD73S6YWPvgp5GRCb9jeuaedl7mo1CbzCI=
|
||||
github.com/luxfi/dex v1.14.0/go.mod h1:wYWQmwospvdKBWQ6OJMXb8I+kfgEtE46R1rD8H7vHCQ=
|
||||
github.com/luxfi/dkg v0.3.5 h1:s2L2mMQaz+n9m0b0ghvoV5VZNxiwb2z4WrGugvK0udY=
|
||||
github.com/luxfi/dkg v0.3.5/go.mod h1:M+WH7GFRN+YUD851Rlnumdp0Md98kplNN8pVx65U8I8=
|
||||
github.com/luxfi/filesystem v0.0.1 h1:VZ6xMFKaAPBW/ddlMsDnI2G0VU1lV5rYaVcW5d+KwEY=
|
||||
github.com/luxfi/filesystem v0.0.1/go.mod h1:OQVSU6XNwqrr1AI+MqkID2taHUclx7NYmmr3svgttec=
|
||||
github.com/luxfi/formatting v1.1.1 h1:MJhVXIPh1dbysvYEjtaEA/Z0FUTiI7n0DwOF54FS08c=
|
||||
github.com/luxfi/formatting v1.1.1/go.mod h1:zhBWp6fLZduhpiAdPgVDdPVOyhw4FvwRUksF6+xKQCE=
|
||||
github.com/luxfi/genesis v1.16.2 h1:OLQg5+ln8qgORBYnJuQsZxar8AfZlsXg5g/f95AraPI=
|
||||
github.com/luxfi/genesis v1.16.2/go.mod h1:piaSqJY80eVpgov8DUWQXll9IdlCroWgvhnwC7/3lTA=
|
||||
github.com/luxfi/genesis v1.16.3 h1:P9/ixEOTC9yVvhhcRT3femRMMVIQxKINet1X1GwE4tQ=
|
||||
github.com/luxfi/genesis v1.16.3/go.mod h1:0F6hV6GfwDSmIWsueB7/vqPZFxyS7A4Jo3p1QU87fFc=
|
||||
github.com/luxfi/genesis/pkg/genesis/security v1.13.8 h1:9ier0p55ErSpoXHRJpZ04WV5HwwMB1uDrU7PHGBKG2U=
|
||||
github.com/luxfi/genesis/pkg/genesis/security v1.13.8/go.mod h1:DzU+GYUFv12ja4Vc46bWKNBBmNYbcow3u/DASx4wpfI=
|
||||
github.com/luxfi/geth v1.20.1 h1:QUGQr4AKvADjwMi7t8a0OfoyxShgEcI9pwie1jFYfm0=
|
||||
@@ -357,6 +355,8 @@ github.com/luxfi/lattice/v7 v7.1.4 h1:hQR02M6cHTAV5+joOPi9gb9Gm+z/hKJnhJF4IlciIJ
|
||||
github.com/luxfi/lattice/v7 v7.1.4/go.mod h1:DmIQFi3mJiehVsR235l1NKYEU0JhU649OX5p7gMEW2c=
|
||||
github.com/luxfi/lens v0.2.1 h1:5Qd0GdjbM+XUVgwDbZ452tKkR7yeE8QnBTHHaH8fJNY=
|
||||
github.com/luxfi/lens v0.2.1/go.mod h1:6FIhC8weEE5RbNMF3SaE+XPSB9cr6FmjypYBoHkz4JQ=
|
||||
github.com/luxfi/light v1.0.0 h1:zTJgp5M0xX8rIwRjYDQgOGhLas3rgXyhkszrTX+ne3s=
|
||||
github.com/luxfi/light v1.0.0/go.mod h1:1G0kgjEe/srlBMCIrFq4IvhnrMuHKFG18CRUWfw1z30=
|
||||
github.com/luxfi/log v1.4.3 h1:xkUKRWvQ4ZwvlUC2e0/RTtHYZOYSMvSQ9W9lbjwBmiI=
|
||||
github.com/luxfi/log v1.4.3/go.mod h1:myIkufyiQomSQH34K981kbz6cG4WUoerRUh7F4XhlQI=
|
||||
github.com/luxfi/magnetar v1.2.3 h1:n4UrJZLK+mhDDZr1HLl2H/KgA6o6v62r5oiC61R7awE=
|
||||
|
||||
@@ -60,7 +60,7 @@ PLUGINS=(
|
||||
r5m1ujrmXxVcQetG3CQfuDLHp2RHKh6vCDaFgBRQfUcTZh7eS # oraclevm
|
||||
ry9Sg8rZdT26iEKvJDmC2wkESs4SDKgZEhk5BgLSwg1EpcNug # quantumvm
|
||||
sP6dLqrrBR9w3soP18fbJ3YzZecZdD7DDdfH2cFhhLq7Hy9bz # relayvm
|
||||
tGVBwRxpmD2aFdg3iYjgRvrCe8Jcmq9UNKxyHMus2NZ8WcD8t # mpcvm
|
||||
qCURact1n41FcoNBch8iMVBwc9AWie48D118ZNJ5tBdWrvryS # mpcvm
|
||||
vv3qPfyTVXZ5ArRZA9Jh4hbYDTBe43f7sgQg4CHfNg1rnnvX9 # zkvm
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user