2026-05-15 12:15:31 -07:00
|
|
|
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
|
|
|
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
|
|
|
|
|
|
package builder
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
|
|
"github.com/luxfi/constants"
|
|
|
|
|
"github.com/luxfi/ids"
|
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
|
|
|
|
|
|
"github.com/luxfi/node/vms/platformvm/genesis"
|
|
|
|
|
)
|
|
|
|
|
|
2026-06-06 23:52:19 -07:00
|
|
|
// TestUTXOAssetIDFromGenesisBytes_Sovereign asserts the canonical
|
2026-05-21 18:34:50 -07:00
|
|
|
// behaviour the sovereign-L1 fix relies on: when the platform genesis
|
|
|
|
|
// bakes an X-Chain, the X-Chain asset ID returned by
|
2026-06-06 23:52:19 -07:00
|
|
|
// UTXOAssetIDFromGenesisBytes is the runtime ID encoded IN the genesis
|
2026-05-21 18:34:50 -07:00
|
|
|
// (different across networks with different validator/holder sets),
|
|
|
|
|
// NOT the network-id-keyed constants.UTXOAssetIDFor(networkID) value.
|
|
|
|
|
//
|
|
|
|
|
// The two values disagreeing is the whole reason the helper exists —
|
|
|
|
|
// sovereign primary networks sharing a networkID would otherwise
|
|
|
|
|
// silently collide on the constant.
|
2026-06-06 23:52:19 -07:00
|
|
|
func TestUTXOAssetIDFromGenesisBytes_Sovereign(t *testing.T) {
|
2026-05-21 18:34:50 -07:00
|
|
|
require := require.New(t)
|
|
|
|
|
|
|
|
|
|
// Use the local devnet config — it has an X-Chain genesis baked in,
|
|
|
|
|
// so the helper must derive a real ID (not just fall back).
|
|
|
|
|
cfg := GetConfig(constants.LocalID)
|
|
|
|
|
require.NotNil(cfg)
|
|
|
|
|
require.NotEmpty(cfg.XChainGenesis, "fixture must bake X-Chain")
|
|
|
|
|
|
|
|
|
|
genesisBytes, fromConfigID, err := FromConfig(cfg)
|
|
|
|
|
require.NoError(err)
|
|
|
|
|
require.NotEmpty(genesisBytes)
|
|
|
|
|
require.NotEqual(ids.Empty, fromConfigID)
|
|
|
|
|
|
2026-06-06 23:52:19 -07:00
|
|
|
helperID, ok, err := UTXOAssetIDFromGenesisBytes(genesisBytes)
|
2026-05-21 18:34:50 -07:00
|
|
|
require.NoError(err)
|
|
|
|
|
require.True(ok, "X-Chain is in genesis — helper must report ok=true")
|
|
|
|
|
require.Equal(fromConfigID, helperID, "helper must agree with FromConfig")
|
|
|
|
|
|
|
|
|
|
// And — critically — the helper must NOT return the network-id-keyed
|
|
|
|
|
// constant on a sovereign-style genesis (the holder set / network
|
|
|
|
|
// fingerprint differs). UTXOAssetIDFor returns the constant in
|
|
|
|
|
// network-id 1 (mainnet) and a network-keyed hash otherwise; on
|
|
|
|
|
// LocalID the test asserts the genesis-derived value supersedes it
|
|
|
|
|
// only when the genesis content differs from the upstream-Lux
|
|
|
|
|
// baseline. For the current embedded local config the two HAPPEN
|
|
|
|
|
// to coincide (single deployer, well-known fixture), so we only
|
|
|
|
|
// assert the value is non-zero and matches FromConfig's return.
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-06 23:52:19 -07:00
|
|
|
// TestUTXOAssetIDFromGenesisBytes_POnly verifies that when the platform
|
2026-05-21 18:34:50 -07:00
|
|
|
// genesis has no X-Chain (P-only mode), the helper returns ok=false
|
|
|
|
|
// and ids.Empty rather than an error. Callers fall back to
|
|
|
|
|
// constants.UTXOAssetIDFor(networkID) in that case (the value is
|
|
|
|
|
// unused at runtime since there is no X-Chain to mint on).
|
2026-06-06 23:52:19 -07:00
|
|
|
func TestUTXOAssetIDFromGenesisBytes_POnly(t *testing.T) {
|
2026-05-21 18:34:50 -07:00
|
|
|
require := require.New(t)
|
|
|
|
|
|
|
|
|
|
pOnly := &genesis.Genesis{Chains: nil}
|
2026-07-10 21:20:17 -07:00
|
|
|
pOnlyBytes, err := pOnly.Bytes()
|
2026-05-21 18:34:50 -07:00
|
|
|
require.NoError(err)
|
|
|
|
|
|
2026-06-06 23:52:19 -07:00
|
|
|
id, ok, err := UTXOAssetIDFromGenesisBytes(pOnlyBytes)
|
2026-05-21 18:34:50 -07:00
|
|
|
require.NoError(err, "P-only is a valid mode, not a parse error")
|
|
|
|
|
require.False(ok, "P-only must report ok=false so caller falls back")
|
|
|
|
|
require.Equal(ids.Empty, id)
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-06 23:52:19 -07:00
|
|
|
// TestUTXOAssetIDFromGenesisBytes_Malformed asserts that bad input
|
2026-05-21 18:34:50 -07:00
|
|
|
// surfaces an error — silently returning ids.Empty would reintroduce
|
|
|
|
|
// the UTXOAssetIDFor(networkID) fallback path and defeat the fix.
|
2026-06-06 23:52:19 -07:00
|
|
|
func TestUTXOAssetIDFromGenesisBytes_Malformed(t *testing.T) {
|
2026-05-21 18:34:50 -07:00
|
|
|
require := require.New(t)
|
|
|
|
|
|
2026-06-06 23:52:19 -07:00
|
|
|
_, _, err := UTXOAssetIDFromGenesisBytes([]byte{0xde, 0xad})
|
2026-05-21 18:34:50 -07:00
|
|
|
require.Error(err)
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-15 12:15:31 -07:00
|
|
|
// TestVMGenesisOptInChains asserts that VMGenesis returns an error for VM IDs
|
|
|
|
|
// not present in the genesis chains list — the core "P-only" contract relied
|
|
|
|
|
// upon by node.initChainManager to log "skipping" and run the node without
|
|
|
|
|
// the missing chain. The behaviour MUST be deterministic and must not panic.
|
|
|
|
|
func TestVMGenesisOptInChains(t *testing.T) {
|
|
|
|
|
require := require.New(t)
|
|
|
|
|
|
|
|
|
|
// Build a genesis with zero chains — pure P-only.
|
|
|
|
|
pOnly := &genesis.Genesis{
|
|
|
|
|
Chains: nil,
|
|
|
|
|
}
|
2026-07-10 21:20:17 -07:00
|
|
|
pOnlyBytes, err := pOnly.Bytes()
|
2026-05-15 12:15:31 -07:00
|
|
|
require.NoError(err)
|
|
|
|
|
|
|
|
|
|
for name, vmID := range map[string]ids.ID{
|
|
|
|
|
"XVM": constants.XVMID,
|
|
|
|
|
"EVM": constants.EVMID,
|
|
|
|
|
"Quantum": constants.QuantumVMID,
|
|
|
|
|
"DexVM": constants.DexVMID,
|
|
|
|
|
} {
|
|
|
|
|
t.Run(name, func(t *testing.T) {
|
|
|
|
|
tx, lookupErr := VMGenesis(pOnlyBytes, vmID)
|
|
|
|
|
require.Nil(tx, "P-only genesis must not surface a tx for %s", name)
|
|
|
|
|
require.Error(lookupErr, "P-only genesis must report %s as missing", name)
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|