mirror of
https://github.com/luxfi/node.git
synced 2026-07-27 03:39:39 +00:00
733 lines
23 KiB
Go
733 lines
23 KiB
Go
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package config
|
|
|
|
import (
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/spf13/pflag"
|
|
"github.com/spf13/viper"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
consensusconfig "github.com/luxfi/consensus/config"
|
|
"github.com/luxfi/constants"
|
|
"github.com/luxfi/ids"
|
|
"github.com/luxfi/node/chains"
|
|
"github.com/luxfi/node/genesis/builder"
|
|
"github.com/luxfi/node/nets"
|
|
pchaingenesis "github.com/luxfi/node/vms/platformvm/genesis"
|
|
pchaintxs "github.com/luxfi/node/vms/platformvm/txs"
|
|
)
|
|
|
|
const chainConfigFilenameExtension = ".ex"
|
|
|
|
func TestGetChainConfigsFromFiles(t *testing.T) {
|
|
tests := map[string]struct {
|
|
configs map[string]string
|
|
upgrades map[string]string
|
|
expected map[string]chains.ChainConfig
|
|
}{
|
|
"no chain configs": {
|
|
configs: map[string]string{},
|
|
upgrades: map[string]string{},
|
|
expected: map[string]chains.ChainConfig{},
|
|
},
|
|
"valid chain-id": {
|
|
configs: map[string]string{"yH8D7ThNJkxmtkuv2jgBa4P1Rn3Qpr4pPr7QYNfcdoS6k6HWp": "hello", "2JVSBoinj9C2J33VntvzYtVJNZdN2NKiwwKjcumHUWEb5DbBrm": "world"},
|
|
upgrades: map[string]string{"yH8D7ThNJkxmtkuv2jgBa4P1Rn3Qpr4pPr7QYNfcdoS6k6HWp": "helloUpgrades"},
|
|
expected: func() map[string]chains.ChainConfig {
|
|
m := map[string]chains.ChainConfig{}
|
|
id1, err := ids.FromString("yH8D7ThNJkxmtkuv2jgBa4P1Rn3Qpr4pPr7QYNfcdoS6k6HWp")
|
|
require.NoError(t, err)
|
|
m[id1.String()] = chains.ChainConfig{Config: []byte("hello"), Upgrade: []byte("helloUpgrades")}
|
|
|
|
id2, err := ids.FromString("2JVSBoinj9C2J33VntvzYtVJNZdN2NKiwwKjcumHUWEb5DbBrm")
|
|
require.NoError(t, err)
|
|
m[id2.String()] = chains.ChainConfig{Config: []byte("world"), Upgrade: []byte(nil)}
|
|
|
|
return m
|
|
}(),
|
|
},
|
|
"valid alias": {
|
|
configs: map[string]string{"C": "hello", "X": "world"},
|
|
upgrades: map[string]string{"C": "upgradess"},
|
|
expected: func() map[string]chains.ChainConfig {
|
|
m := map[string]chains.ChainConfig{}
|
|
m["C"] = chains.ChainConfig{Config: []byte("hello"), Upgrade: []byte("upgradess")}
|
|
m["X"] = chains.ChainConfig{Config: []byte("world"), Upgrade: []byte(nil)}
|
|
|
|
return m
|
|
}(),
|
|
},
|
|
}
|
|
|
|
for name, test := range tests {
|
|
t.Run(name, func(t *testing.T) {
|
|
require := require.New(t)
|
|
root := t.TempDir()
|
|
configJSON := fmt.Sprintf(`{%q: %q}`, ChainConfigDirKey, root)
|
|
configFile := setupConfigJSON(t, root, configJSON)
|
|
chainsDir := root
|
|
// Create custom configs
|
|
for key, value := range test.configs {
|
|
chainDir := filepath.Join(chainsDir, key)
|
|
setupFile(t, chainDir, chainConfigFileName+chainConfigFilenameExtension, value)
|
|
}
|
|
for key, value := range test.upgrades {
|
|
chainDir := filepath.Join(chainsDir, key)
|
|
setupFile(t, chainDir, chainUpgradeFileName+chainConfigFilenameExtension, value)
|
|
}
|
|
|
|
v := setupViper(configFile)
|
|
|
|
// Parse config
|
|
require.Equal(root, v.GetString(ChainConfigDirKey))
|
|
chainConfigs, err := getChainConfigs(v)
|
|
require.NoError(err)
|
|
require.Equal(test.expected, chainConfigs)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetChainConfigsDirNotExist(t *testing.T) {
|
|
tests := map[string]struct {
|
|
structure string
|
|
file map[string]string
|
|
expectedErr error
|
|
expected map[string]chains.ChainConfig
|
|
}{
|
|
"cdir not exist": {
|
|
structure: "/",
|
|
file: map[string]string{"config.ex": "noeffect"},
|
|
expectedErr: errCannotReadDirectory,
|
|
expected: nil,
|
|
},
|
|
"cdir is file ": {
|
|
structure: "/",
|
|
file: map[string]string{"cdir": "noeffect"},
|
|
expectedErr: errCannotReadDirectory,
|
|
expected: nil,
|
|
},
|
|
"chain subdir not exist": {
|
|
structure: "/cdir/",
|
|
file: map[string]string{"config.ex": "noeffect"},
|
|
expectedErr: nil,
|
|
expected: map[string]chains.ChainConfig{},
|
|
},
|
|
"full structure": {
|
|
structure: "/cdir/C/",
|
|
file: map[string]string{"config.ex": "hello"},
|
|
expectedErr: nil,
|
|
expected: map[string]chains.ChainConfig{"C": {Config: []byte("hello"), Upgrade: []byte(nil)}},
|
|
},
|
|
}
|
|
|
|
for name, test := range tests {
|
|
t.Run(name, func(t *testing.T) {
|
|
require := require.New(t)
|
|
root := t.TempDir()
|
|
chainConfigDir := filepath.Join(root, "cdir")
|
|
configJSON := fmt.Sprintf(`{%q: %q}`, ChainConfigDirKey, chainConfigDir)
|
|
configFile := setupConfigJSON(t, root, configJSON)
|
|
|
|
dirToCreate := filepath.Join(root, test.structure)
|
|
require.NoError(os.MkdirAll(dirToCreate, 0o700))
|
|
|
|
for key, value := range test.file {
|
|
setupFile(t, dirToCreate, key, value)
|
|
}
|
|
v := setupViper(configFile)
|
|
|
|
// Parse config
|
|
require.Equal(chainConfigDir, v.GetString(ChainConfigDirKey))
|
|
|
|
// don't read with getConfigFromViper since it's very slow.
|
|
chainConfigs, err := getChainConfigs(v)
|
|
require.ErrorIs(err, test.expectedErr)
|
|
require.Equal(test.expected, chainConfigs)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSetChainConfigDefaultDir(t *testing.T) {
|
|
require := require.New(t)
|
|
root := t.TempDir()
|
|
// changes internal package variable, since using defaultDir (under user home) is risky.
|
|
defaultChainConfigDir = filepath.Join(root, "cdir")
|
|
configFilePath := setupConfigJSON(t, root, "{}")
|
|
|
|
v := setupViper(configFilePath)
|
|
require.Equal(defaultChainConfigDir, v.GetString(ChainConfigDirKey))
|
|
|
|
chainsDir := filepath.Join(defaultChainConfigDir, "C")
|
|
setupFile(t, chainsDir, chainConfigFileName+chainConfigFilenameExtension, "helloworld")
|
|
chainConfigs, err := getChainConfigs(v)
|
|
require.NoError(err)
|
|
expected := map[string]chains.ChainConfig{"C": {Config: []byte("helloworld"), Upgrade: []byte(nil)}}
|
|
require.Equal(expected, chainConfigs)
|
|
}
|
|
|
|
func TestGetChainConfigsFromFlags(t *testing.T) {
|
|
tests := map[string]struct {
|
|
fullConfigs map[string]chains.ChainConfig
|
|
expected map[string]chains.ChainConfig
|
|
}{
|
|
"no chain configs": {
|
|
fullConfigs: map[string]chains.ChainConfig{},
|
|
expected: map[string]chains.ChainConfig{},
|
|
},
|
|
"valid chain-id": {
|
|
fullConfigs: func() map[string]chains.ChainConfig {
|
|
m := map[string]chains.ChainConfig{}
|
|
id1, err := ids.FromString("yH8D7ThNJkxmtkuv2jgBa4P1Rn3Qpr4pPr7QYNfcdoS6k6HWp")
|
|
require.NoError(t, err)
|
|
m[id1.String()] = chains.ChainConfig{Config: []byte("hello"), Upgrade: []byte("helloUpgrades")}
|
|
|
|
id2, err := ids.FromString("2JVSBoinj9C2J33VntvzYtVJNZdN2NKiwwKjcumHUWEb5DbBrm")
|
|
require.NoError(t, err)
|
|
m[id2.String()] = chains.ChainConfig{Config: []byte("world"), Upgrade: []byte(nil)}
|
|
|
|
return m
|
|
}(),
|
|
expected: func() map[string]chains.ChainConfig {
|
|
m := map[string]chains.ChainConfig{}
|
|
id1, err := ids.FromString("yH8D7ThNJkxmtkuv2jgBa4P1Rn3Qpr4pPr7QYNfcdoS6k6HWp")
|
|
require.NoError(t, err)
|
|
m[id1.String()] = chains.ChainConfig{Config: []byte("hello"), Upgrade: []byte("helloUpgrades")}
|
|
|
|
id2, err := ids.FromString("2JVSBoinj9C2J33VntvzYtVJNZdN2NKiwwKjcumHUWEb5DbBrm")
|
|
require.NoError(t, err)
|
|
m[id2.String()] = chains.ChainConfig{Config: []byte("world"), Upgrade: []byte(nil)}
|
|
|
|
return m
|
|
}(),
|
|
},
|
|
"valid alias": {
|
|
fullConfigs: map[string]chains.ChainConfig{
|
|
"C": {Config: []byte("hello"), Upgrade: []byte("upgradess")},
|
|
"X": {Config: []byte("world"), Upgrade: []byte(nil)},
|
|
},
|
|
expected: func() map[string]chains.ChainConfig {
|
|
m := map[string]chains.ChainConfig{}
|
|
m["C"] = chains.ChainConfig{Config: []byte("hello"), Upgrade: []byte("upgradess")}
|
|
m["X"] = chains.ChainConfig{Config: []byte("world"), Upgrade: []byte(nil)}
|
|
|
|
return m
|
|
}(),
|
|
},
|
|
}
|
|
|
|
for name, test := range tests {
|
|
t.Run(name, func(t *testing.T) {
|
|
require := require.New(t)
|
|
jsonMaps, err := json.Marshal(test.fullConfigs)
|
|
require.NoError(err)
|
|
encodedFileContent := base64.StdEncoding.EncodeToString(jsonMaps)
|
|
|
|
// build viper config
|
|
v := setupViperFlags()
|
|
v.Set(ChainConfigContentKey, encodedFileContent)
|
|
|
|
// Parse config
|
|
chainConfigs, err := getChainConfigs(v)
|
|
require.NoError(err)
|
|
require.Equal(test.expected, chainConfigs)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetVMAliasesFromFile(t *testing.T) {
|
|
tests := map[string]struct {
|
|
givenJSON string
|
|
expected map[ids.ID][]string
|
|
expectedErr error
|
|
}{
|
|
"wrong vm id": {
|
|
givenJSON: `{"wrongVmId": ["vm1","vm2"]}`,
|
|
expected: nil,
|
|
expectedErr: errUnmarshalling,
|
|
},
|
|
"vm id": {
|
|
givenJSON: `{"2Ctt6eGAeo4MLqTmGa7AdRecuVMPGWEX9wSsCLBYrLhX4a394i": ["vm1","vm2"],
|
|
"Gmt4fuNsGJAd2PX86LBvycGaBpgCYKbuULdCLZs3SEs1Jx1LU": ["vm3", "vm4"] }`,
|
|
expected: func() map[ids.ID][]string {
|
|
m := map[ids.ID][]string{}
|
|
id1, _ := ids.FromString("2Ctt6eGAeo4MLqTmGa7AdRecuVMPGWEX9wSsCLBYrLhX4a394i")
|
|
id2, _ := ids.FromString("Gmt4fuNsGJAd2PX86LBvycGaBpgCYKbuULdCLZs3SEs1Jx1LU")
|
|
m[id1] = []string{"vm1", "vm2"}
|
|
m[id2] = []string{"vm3", "vm4"}
|
|
return m
|
|
}(),
|
|
expectedErr: nil,
|
|
},
|
|
}
|
|
|
|
for name, test := range tests {
|
|
t.Run(name, func(t *testing.T) {
|
|
require := require.New(t)
|
|
root := t.TempDir()
|
|
aliasPath := filepath.Join(root, "aliases.json")
|
|
configJSON := fmt.Sprintf(`{%q: %q}`, VMAliasesFileKey, aliasPath)
|
|
configFilePath := setupConfigJSON(t, root, configJSON)
|
|
setupFile(t, root, "aliases.json", test.givenJSON)
|
|
v := setupViper(configFilePath)
|
|
vmAliases, err := getVMAliases(v)
|
|
require.ErrorIs(err, test.expectedErr)
|
|
require.Equal(test.expected, vmAliases)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetVMAliasesFromFlag(t *testing.T) {
|
|
tests := map[string]struct {
|
|
givenJSON string
|
|
expected map[ids.ID][]string
|
|
expectedErr error
|
|
}{
|
|
"wrong vm id": {
|
|
givenJSON: `{"wrongVmId": ["vm1","vm2"]}`,
|
|
expected: nil,
|
|
expectedErr: errUnmarshalling,
|
|
},
|
|
"vm id": {
|
|
givenJSON: `{"2Ctt6eGAeo4MLqTmGa7AdRecuVMPGWEX9wSsCLBYrLhX4a394i": ["vm1","vm2"],
|
|
"Gmt4fuNsGJAd2PX86LBvycGaBpgCYKbuULdCLZs3SEs1Jx1LU": ["vm3", "vm4"] }`,
|
|
expected: func() map[ids.ID][]string {
|
|
m := map[ids.ID][]string{}
|
|
id1, _ := ids.FromString("2Ctt6eGAeo4MLqTmGa7AdRecuVMPGWEX9wSsCLBYrLhX4a394i")
|
|
id2, _ := ids.FromString("Gmt4fuNsGJAd2PX86LBvycGaBpgCYKbuULdCLZs3SEs1Jx1LU")
|
|
m[id1] = []string{"vm1", "vm2"}
|
|
m[id2] = []string{"vm3", "vm4"}
|
|
return m
|
|
}(),
|
|
expectedErr: nil,
|
|
},
|
|
}
|
|
|
|
for name, test := range tests {
|
|
t.Run(name, func(t *testing.T) {
|
|
require := require.New(t)
|
|
encodedFileContent := base64.StdEncoding.EncodeToString([]byte(test.givenJSON))
|
|
|
|
// build viper config
|
|
v := setupViperFlags()
|
|
v.Set(VMAliasesContentKey, encodedFileContent)
|
|
|
|
vmAliases, err := getVMAliases(v)
|
|
require.ErrorIs(err, test.expectedErr)
|
|
require.Equal(test.expected, vmAliases)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetVMAliasesDefaultDir(t *testing.T) {
|
|
require := require.New(t)
|
|
root := t.TempDir()
|
|
// changes internal package variable, since using defaultDir (under user home) is risky.
|
|
defaultVMAliasFilePath = filepath.Join(root, "aliases.json")
|
|
configFilePath := setupConfigJSON(t, root, "{}")
|
|
|
|
v := setupViper(configFilePath)
|
|
require.Equal(defaultVMAliasFilePath, v.GetString(VMAliasesFileKey))
|
|
|
|
setupFile(t, root, "aliases.json", `{"2Ctt6eGAeo4MLqTmGa7AdRecuVMPGWEX9wSsCLBYrLhX4a394i": ["vm1","vm2"]}`)
|
|
vmAliases, err := getVMAliases(v)
|
|
require.NoError(err)
|
|
|
|
expected := map[ids.ID][]string{}
|
|
id, _ := ids.FromString("2Ctt6eGAeo4MLqTmGa7AdRecuVMPGWEX9wSsCLBYrLhX4a394i")
|
|
expected[id] = []string{"vm1", "vm2"}
|
|
require.Equal(expected, vmAliases)
|
|
}
|
|
|
|
func TestGetVMAliasesDirNotExists(t *testing.T) {
|
|
require := require.New(t)
|
|
root := t.TempDir()
|
|
aliasPath := "/not/exists"
|
|
// set it explicitly
|
|
configJSON := fmt.Sprintf(`{%q: %q}`, VMAliasesFileKey, aliasPath)
|
|
configFilePath := setupConfigJSON(t, root, configJSON)
|
|
v := setupViper(configFilePath)
|
|
vmAliases, err := getVMAliases(v)
|
|
require.ErrorIs(err, errFileDoesNotExist)
|
|
require.Nil(vmAliases)
|
|
|
|
// do not set it explicitly
|
|
configJSON = "{}"
|
|
configFilePath = setupConfigJSON(t, root, configJSON)
|
|
v = setupViper(configFilePath)
|
|
vmAliases, err = getVMAliases(v)
|
|
require.Nil(vmAliases)
|
|
require.NoError(err)
|
|
}
|
|
|
|
func TestGetNetConfigsFromFile(t *testing.T) {
|
|
netID, err := ids.FromString("2Ctt6eGAeo4MLqTmGa7AdRecuVMPGWEX9wSsCLBYrLhX4a394i")
|
|
require.NoError(t, err)
|
|
|
|
defaultConfigs := map[ids.ID]nets.Config{
|
|
netID: getDefaultNetConfig(setupViperFlags()),
|
|
}
|
|
|
|
tests := map[string]struct {
|
|
fileName string
|
|
givenJSON string
|
|
testF func(*require.Assertions, map[ids.ID]nets.Config)
|
|
expectedErr error
|
|
}{
|
|
"wrong config": {
|
|
fileName: "2Ctt6eGAeo4MLqTmGa7AdRecuVMPGWEX9wSsCLBYrLhX4a394i.json",
|
|
givenJSON: `thisisnotjson`,
|
|
testF: func(require *require.Assertions, given map[ids.ID]nets.Config) {
|
|
require.Nil(given)
|
|
},
|
|
expectedErr: errUnmarshalling,
|
|
},
|
|
"chain is not tracked": {
|
|
fileName: "Gmt4fuNsGJAd2PX86LBvycGaBpgCYKbuULdCLZs3SEs1Jx1LU.json",
|
|
givenJSON: `{"validatorOnly": true}`,
|
|
testF: func(require *require.Assertions, given map[ids.ID]nets.Config) {
|
|
require.Equal(defaultConfigs, given)
|
|
},
|
|
expectedErr: nil,
|
|
},
|
|
"default config when incorrect extension used": {
|
|
fileName: "2Ctt6eGAeo4MLqTmGa7AdRecuVMPGWEX9wSsCLBYrLhX4a394i.yaml",
|
|
givenJSON: `{"validatorOnly": true}`,
|
|
testF: func(require *require.Assertions, given map[ids.ID]nets.Config) {
|
|
require.Equal(defaultConfigs, given)
|
|
},
|
|
expectedErr: nil,
|
|
},
|
|
"invalid consensus parameters": {
|
|
fileName: "2Ctt6eGAeo4MLqTmGa7AdRecuVMPGWEX9wSsCLBYrLhX4a394i.json",
|
|
givenJSON: `{"consensusParameters":{"k": 111, "alphaPreference":1234} }`,
|
|
testF: func(require *require.Assertions, given map[ids.ID]nets.Config) {
|
|
require.Nil(given)
|
|
},
|
|
expectedErr: consensusconfig.ErrParametersInvalid,
|
|
},
|
|
"correct config": {
|
|
fileName: "2Ctt6eGAeo4MLqTmGa7AdRecuVMPGWEX9wSsCLBYrLhX4a394i.json",
|
|
givenJSON: `{"validatorOnly": true, "consensusParameters":{"k":20, "alphaPreference":15, "alphaConfidence":15} }`,
|
|
testF: func(require *require.Assertions, given map[ids.ID]nets.Config) {
|
|
id, _ := ids.FromString("2Ctt6eGAeo4MLqTmGa7AdRecuVMPGWEX9wSsCLBYrLhX4a394i")
|
|
config, ok := given[id]
|
|
require.True(ok)
|
|
|
|
require.True(config.ValidatorOnly)
|
|
require.Equal(15, config.ConsensusParameters.AlphaConfidence)
|
|
require.Equal(15, config.ConsensusParameters.AlphaPreference)
|
|
require.Equal(20, config.ConsensusParameters.K)
|
|
},
|
|
expectedErr: nil,
|
|
},
|
|
}
|
|
|
|
for name, test := range tests {
|
|
t.Run(name, func(t *testing.T) {
|
|
require := require.New(t)
|
|
|
|
root := t.TempDir()
|
|
chainPath := filepath.Join(root, "chains")
|
|
|
|
configJSON := fmt.Sprintf(`{%q: %q}`, NetConfigDirKey, chainPath)
|
|
configFilePath := setupConfigJSON(t, root, configJSON)
|
|
|
|
setupFile(t, chainPath, test.fileName, test.givenJSON)
|
|
|
|
v := setupViper(configFilePath)
|
|
chainConfigs, err := getNetConfigs(v, []ids.ID{netID})
|
|
require.ErrorIs(err, test.expectedErr)
|
|
if test.expectedErr != nil {
|
|
return
|
|
}
|
|
test.testF(require, chainConfigs)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetNetConfigsFromFlags(t *testing.T) {
|
|
netID, err := ids.FromString("2Ctt6eGAeo4MLqTmGa7AdRecuVMPGWEX9wSsCLBYrLhX4a394i")
|
|
require.NoError(t, err)
|
|
|
|
defaultConfigs := map[ids.ID]nets.Config{
|
|
netID: getDefaultNetConfig(setupViperFlags()),
|
|
}
|
|
|
|
tests := map[string]struct {
|
|
givenJSON string
|
|
testF func(*require.Assertions, map[ids.ID]nets.Config)
|
|
expectedErr error
|
|
}{
|
|
"default config used when no config provided": {
|
|
givenJSON: `{}`,
|
|
testF: func(require *require.Assertions, given map[ids.ID]nets.Config) {
|
|
require.Equal(defaultConfigs, given)
|
|
},
|
|
expectedErr: nil,
|
|
},
|
|
"entry with no config": {
|
|
givenJSON: `{"2Ctt6eGAeo4MLqTmGa7AdRecuVMPGWEX9wSsCLBYrLhX4a394i":{"consensusParameters":{"k":20,"alphaPreference":15,"alphaConfidence":15}}}`,
|
|
testF: func(require *require.Assertions, given map[ids.ID]nets.Config) {
|
|
require.Len(given, 1)
|
|
id, _ := ids.FromString("2Ctt6eGAeo4MLqTmGa7AdRecuVMPGWEX9wSsCLBYrLhX4a394i")
|
|
config, ok := given[id]
|
|
require.True(ok)
|
|
// should have our specified params
|
|
require.Equal(20, config.ConsensusParameters.K)
|
|
require.Equal(15, config.ConsensusParameters.AlphaPreference)
|
|
require.Equal(15, config.ConsensusParameters.AlphaConfidence)
|
|
},
|
|
expectedErr: nil,
|
|
},
|
|
"default config used when chain is not tracked": {
|
|
givenJSON: `{"Gmt4fuNsGJAd2PX86LBvycGaBpgCYKbuULdCLZs3SEs1Jx1LU":{"validatorOnly":true}}`,
|
|
testF: func(require *require.Assertions, given map[ids.ID]nets.Config) {
|
|
require.Equal(defaultConfigs, given)
|
|
},
|
|
expectedErr: nil,
|
|
},
|
|
"invalid consensus parameters": {
|
|
givenJSON: `{
|
|
"2Ctt6eGAeo4MLqTmGa7AdRecuVMPGWEX9wSsCLBYrLhX4a394i": {
|
|
"consensusParameters": {
|
|
"k": 111,
|
|
"alphaPreference": 1234
|
|
}
|
|
}
|
|
}`,
|
|
testF: func(require *require.Assertions, given map[ids.ID]nets.Config) {
|
|
require.Empty(given)
|
|
},
|
|
expectedErr: consensusconfig.ErrParametersInvalid,
|
|
},
|
|
"correct config": {
|
|
givenJSON: `{
|
|
"2Ctt6eGAeo4MLqTmGa7AdRecuVMPGWEX9wSsCLBYrLhX4a394i": {
|
|
"consensusParameters": {
|
|
"k": 30,
|
|
"alphaPreference": 16,
|
|
"alphaConfidence": 20
|
|
},
|
|
"validatorOnly": true
|
|
}
|
|
}`,
|
|
testF: func(require *require.Assertions, given map[ids.ID]nets.Config) {
|
|
id, _ := ids.FromString("2Ctt6eGAeo4MLqTmGa7AdRecuVMPGWEX9wSsCLBYrLhX4a394i")
|
|
config, ok := given[id]
|
|
require.True(ok)
|
|
require.True(config.ValidatorOnly)
|
|
require.Equal(16, config.ConsensusParameters.AlphaPreference)
|
|
require.Equal(20, config.ConsensusParameters.AlphaConfidence)
|
|
require.Equal(30, config.ConsensusParameters.K)
|
|
// must still respect defaults (MainnetParameters.MaxOutstandingItems = 1024)
|
|
require.Equal(1024, config.ConsensusParameters.MaxOutstandingItems)
|
|
},
|
|
expectedErr: nil,
|
|
},
|
|
}
|
|
|
|
for name, test := range tests {
|
|
t.Run(name, func(t *testing.T) {
|
|
require := require.New(t)
|
|
|
|
encodedFileContent := base64.StdEncoding.EncodeToString([]byte(test.givenJSON))
|
|
|
|
// build viper config
|
|
v := setupViperFlags()
|
|
v.Set(NetConfigContentKey, encodedFileContent)
|
|
|
|
chainConfigs, err := getNetConfigs(v, []ids.ID{netID})
|
|
require.ErrorIs(err, test.expectedErr)
|
|
if test.expectedErr != nil {
|
|
return
|
|
}
|
|
test.testF(require, chainConfigs)
|
|
})
|
|
}
|
|
}
|
|
|
|
// setups config json file and writes content
|
|
func setupConfigJSON(t *testing.T, rootPath string, value string) string {
|
|
configFilePath := filepath.Join(rootPath, "config.json")
|
|
require.NoError(t, os.WriteFile(configFilePath, []byte(value), 0o600))
|
|
return configFilePath
|
|
}
|
|
|
|
// setups file creates necessary path and writes value to it.
|
|
func setupFile(t *testing.T, path string, fileName string, value string) {
|
|
require := require.New(t)
|
|
|
|
require.NoError(os.MkdirAll(path, 0o700))
|
|
filePath := filepath.Join(path, fileName)
|
|
require.NoError(os.WriteFile(filePath, []byte(value), 0o600))
|
|
}
|
|
|
|
func setupViperFlags() *viper.Viper {
|
|
v := viper.New()
|
|
fs := BuildFlagSet()
|
|
pflag.Parse()
|
|
if err := v.BindPFlags(fs); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
return v
|
|
}
|
|
|
|
func setupViper(configFilePath string) *viper.Viper {
|
|
v := setupViperFlags()
|
|
v.SetConfigFile(configFilePath)
|
|
if err := v.ReadInConfig(); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
return v
|
|
}
|
|
|
|
func TestSkipBootstrapConfig(t *testing.T) {
|
|
v := viper.New()
|
|
|
|
// Test default values
|
|
v.SetDefault(SkipBootstrapKey, false)
|
|
v.SetDefault(EnableAutominingKey, false)
|
|
|
|
config, err := getBootstrapConfig(v, 1)
|
|
require.NoError(t, err)
|
|
require.False(t, config.SkipBootstrap)
|
|
require.False(t, config.EnableAutomining)
|
|
|
|
// Test with skip bootstrap enabled
|
|
v.Set(SkipBootstrapKey, true)
|
|
config, err = getBootstrapConfig(v, 1)
|
|
require.NoError(t, err)
|
|
require.True(t, config.SkipBootstrap)
|
|
require.False(t, config.EnableAutomining)
|
|
|
|
// Test with automining enabled
|
|
v.Set(EnableAutominingKey, true)
|
|
config, err = getBootstrapConfig(v, 1)
|
|
require.NoError(t, err)
|
|
require.True(t, config.SkipBootstrap)
|
|
require.True(t, config.EnableAutomining)
|
|
|
|
// Test with both disabled
|
|
v.Set(SkipBootstrapKey, false)
|
|
v.Set(EnableAutominingKey, false)
|
|
config, err = getBootstrapConfig(v, 1)
|
|
require.NoError(t, err)
|
|
require.False(t, config.SkipBootstrap)
|
|
require.False(t, config.EnableAutomining)
|
|
}
|
|
|
|
func TestDevModeFlags(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
skipBootstrap bool
|
|
enableAutomining bool
|
|
expected bool
|
|
}{
|
|
{
|
|
name: "both enabled",
|
|
skipBootstrap: true,
|
|
enableAutomining: true,
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "only skip bootstrap",
|
|
skipBootstrap: true,
|
|
enableAutomining: false,
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "only automining",
|
|
skipBootstrap: false,
|
|
enableAutomining: true,
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "both disabled",
|
|
skipBootstrap: false,
|
|
enableAutomining: false,
|
|
expected: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
v := viper.New()
|
|
v.Set(SkipBootstrapKey, tt.skipBootstrap)
|
|
v.Set(EnableAutominingKey, tt.enableAutomining)
|
|
|
|
config, err := getBootstrapConfig(v, 1)
|
|
require.NoError(t, err)
|
|
require.Equal(t, tt.skipBootstrap, config.SkipBootstrap)
|
|
require.Equal(t, tt.enableAutomining, config.EnableAutomining)
|
|
|
|
// Check that at least one dev mode flag is set
|
|
devModeEnabled := config.SkipBootstrap || config.EnableAutomining
|
|
require.Equal(t, tt.expected, devModeEnabled)
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestResolveXAssetID_FromSovereignGenesis covers the canonical
|
|
// behaviour the sovereign-L1 fix relies on: when the loaded platform
|
|
// genesis bakes an X-Chain, resolveXAssetID returns the runtime asset
|
|
// ID encoded IN the genesis (matches what FromConfig produces, matches
|
|
// what the running X-Chain reports via platform.getStakingAssetID).
|
|
//
|
|
// Critically: NOT constants.UTXOAssetIDFor(networkID). That value is
|
|
// network-id-keyed and would silently collide between two sovereign L1s
|
|
// sharing a primary-network ID.
|
|
func TestResolveXAssetID_FromSovereignGenesis(t *testing.T) {
|
|
require := require.New(t)
|
|
|
|
cfg := builder.GetConfig(constants.LocalID)
|
|
require.NotNil(cfg)
|
|
require.NotEmpty(cfg.XChainGenesis, "fixture must bake X-Chain")
|
|
|
|
genesisBytes, expectedID, err := builder.FromConfig(cfg)
|
|
require.NoError(err)
|
|
require.NotEqual(ids.Empty, expectedID)
|
|
|
|
gotID, err := resolveXAssetID(constants.LocalID, genesisBytes)
|
|
require.NoError(err)
|
|
require.Equal(expectedID, gotID,
|
|
"resolveXAssetID must agree with FromConfig on the genesis-derived asset ID")
|
|
}
|
|
|
|
// TestResolveXAssetID_POnlyFallback verifies that when the platform
|
|
// genesis bakes no X-Chain (P-only mode), resolveXAssetID falls back
|
|
// to constants.UTXOAssetIDFor(networkID). That value is unused at
|
|
// runtime (no X-Chain to mint on) but keeps the existing nodeConfig
|
|
// shape consistent for downstream consumers.
|
|
func TestResolveXAssetID_POnlyFallback(t *testing.T) {
|
|
require := require.New(t)
|
|
|
|
pOnly := &pchaingenesis.Genesis{Chains: nil}
|
|
pOnlyBytes, err := pchaingenesis.Codec.Marshal(pchaintxs.CodecVersion, pOnly)
|
|
require.NoError(err)
|
|
|
|
gotID, err := resolveXAssetID(42, pOnlyBytes)
|
|
require.NoError(err)
|
|
require.Equal(constants.UTXOAssetIDFor(42), gotID,
|
|
"P-only must fall through to UTXOAssetIDFor(networkID)")
|
|
}
|
|
|
|
// TestResolveXAssetID_Malformed asserts that bad genesis bytes surface
|
|
// an error rather than silently returning ids.Empty (which would
|
|
// reintroduce the UTXOAssetIDFor fallback and defeat the fix on
|
|
// sovereign L1s where the fallback value is wrong).
|
|
func TestResolveXAssetID_Malformed(t *testing.T) {
|
|
require := require.New(t)
|
|
|
|
_, err := resolveXAssetID(1, []byte{0xff, 0xfe, 0xfd, 0xfc})
|
|
require.Error(err)
|
|
}
|