Refactor subnet→chain terminology and update genesis to v1.5.6

Major changes:
- Rename SubnetSpec → ChainSpec/ParticipantSpec throughout codebase
- Rename BlockchainSpec → ChainSpec for full chain definitions
- Update all protobuf definitions with new naming
- Fix duplicate variable declarations from sed replacements
- Update copyright headers to BSD-3-Clause with SPDX identifiers
- Update github.com/luxfi/genesis to v1.5.6 (BLS signer keys)

The genesis v1.5.6 update adds:
- Initial stakers with BLS publicKey and proofOfPossession
- Initial allocations for local network testing
- Fixes "failed to load database state: not found" error

Build fixes:
- Fix api/mocks/client.go CChainAPI return type
- Fix local/node_test.go with build tag for integration tests
- Fix parameter name collisions (chainConfigs → pChainConfigs)
This commit is contained in:
Zach Kelling
2025-12-19 07:11:06 -08:00
parent 55113bf301
commit 2f9accfb10
68 changed files with 3470 additions and 4512 deletions
+65
View File
@@ -0,0 +1,65 @@
name: Deploy Documentation
on:
push:
branches: [main]
paths:
- 'docs/**'
- '.github/workflows/docs.yml'
workflow_dispatch:
permissions:
contents: read
pages: write
id-token: write
concurrency:
group: "pages"
cancel-in-progress: false
jobs:
build:
runs-on: ubuntu-latest
defaults:
run:
working-directory: docs
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 10
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'pnpm'
cache-dependency-path: docs/pnpm-lock.yaml
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build
run: pnpm build
- name: Setup Pages
uses: actions/configure-pages@v5
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: docs/out
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
needs: build
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
+1 -1
View File
@@ -88,7 +88,7 @@ func (c APIClient) InfoAPI() *info.Client {
return c.info
}
func (c APIClient) HealthAPI() *health.Client {
func (c APIClient) HealthAPI() HealthClient {
return c.health
}
+9 -1
View File
@@ -11,11 +11,19 @@ import (
"github.com/luxfi/node/api/health"
"github.com/luxfi/node/api/info"
"github.com/luxfi/node/indexer"
"github.com/luxfi/node/utils/rpc"
"github.com/luxfi/node/vms/exchangevm"
"github.com/luxfi/node/vms/platformvm"
// evmclient "github.com/luxfi/evm/plugin/evm/client"
)
// HealthClient is the interface for health API client operations
type HealthClient interface {
Readiness(ctx context.Context, tags []string, options ...rpc.Option) (*health.APIReply, error)
Health(ctx context.Context, tags []string, options ...rpc.Option) (*health.APIReply, error)
Liveness(ctx context.Context, tags []string, options ...rpc.Option) (*health.APIReply, error)
}
// EthClient is the interface for ethereum client operations
type EthClient interface {
// Balance operations
@@ -63,7 +71,7 @@ type Client interface {
CChainAPI() interface{} // evmclient.Client
CChainEthAPI() EthClient // ethclient websocket wrapper that adds mutexed calls, and lazy conn init (on first call)
InfoAPI() *info.Client
HealthAPI() *health.Client
HealthAPI() HealthClient
AdminAPI() *admin.Client
PChainIndexAPI() *indexer.Client
CChainIndexAPI() *indexer.Client
+8 -14
View File
@@ -8,10 +8,6 @@ import (
exchangevm "github.com/luxfi/node/vms/exchangevm"
evmclient "github.com/luxfi/evm/plugin/evm/client"
health "github.com/luxfi/node/api/health"
indexer "github.com/luxfi/node/indexer"
info "github.com/luxfi/node/api/info"
@@ -43,16 +39,14 @@ func (_m *Client) AdminAPI() *admin.Client {
}
// CChainAPI provides a mock function with given fields:
func (_m *Client) CChainAPI() evmclient.Client {
func (_m *Client) CChainAPI() interface{} {
ret := _m.Called()
var r0 evmclient.Client
if rf, ok := ret.Get(0).(func() evmclient.Client); ok {
var r0 interface{}
if rf, ok := ret.Get(0).(func() interface{}); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(evmclient.Client)
}
r0 = ret.Get(0)
}
return r0
@@ -91,15 +85,15 @@ func (_m *Client) CChainIndexAPI() *indexer.Client {
}
// HealthAPI provides a mock function with given fields:
func (_m *Client) HealthAPI() *health.Client {
func (_m *Client) HealthAPI() api.HealthClient {
ret := _m.Called()
var r0 *health.Client
if rf, ok := ret.Get(0).(func() *health.Client); ok {
var r0 api.HealthClient
if rf, ok := ret.Get(0).(func() api.HealthClient); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*health.Client)
r0 = ret.Get(0).(api.HealthClient)
}
}
+51 -49
View File
@@ -1,5 +1,5 @@
// Copyright (C) 2021-2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// SPDX-License-Identifier: BSD-3-Clause
// Package client implements client.
package client
@@ -31,11 +31,13 @@ type Client interface {
Ping(ctx context.Context) (*rpcpb.PingResponse, error)
RPCVersion(ctx context.Context) (*rpcpb.RPCVersionResponse, error)
Start(ctx context.Context, execPath string, opts ...OpOption) (*rpcpb.StartResponse, error)
CreateBlockchains(ctx context.Context, blockchainSpecs []*rpcpb.BlockchainSpec) (*rpcpb.CreateBlockchainsResponse, error)
CreateSubnets(ctx context.Context, subnetSpecs []*rpcpb.SubnetSpec) (*rpcpb.CreateSubnetsResponse, error)
TransformElasticSubnets(ctx context.Context, elasticSubnetSpecs []*rpcpb.ElasticSubnetSpec) (*rpcpb.TransformElasticSubnetsResponse, error)
// CreateChains creates chains with VMs and genesis (uses rpcpb.CreateBlockchains)
CreateChains(ctx context.Context, chainSpecs []*rpcpb.BlockchainSpec) (*rpcpb.CreateBlockchainsResponse, error)
// CreateParticipantGroups creates participant groups (uses rpcpb.CreateChains)
CreateParticipantGroups(ctx context.Context, participantsSpecs []*rpcpb.ChainSpec) (*rpcpb.CreateChainsResponse, error)
TransformElasticChains(ctx context.Context, elasticChainSpecs []*rpcpb.ElasticChainSpec) (*rpcpb.TransformElasticChainsResponse, error)
AddPermissionlessValidator(ctx context.Context, validatorSpec []*rpcpb.PermissionlessValidatorSpec) (*rpcpb.AddPermissionlessValidatorResponse, error)
RemoveSubnetValidator(ctx context.Context, validatorSpec []*rpcpb.RemoveSubnetValidatorSpec) (*rpcpb.RemoveSubnetValidatorResponse, error)
RemoveChainValidator(ctx context.Context, validatorSpec []*rpcpb.RemoveChainValidatorSpec) (*rpcpb.RemoveChainValidatorResponse, error)
Health(ctx context.Context) (*rpcpb.HealthResponse, error)
WaitForHealthy(ctx context.Context) (*rpcpb.WaitForHealthyResponse, error)
URIs(ctx context.Context) ([]string, error)
@@ -116,10 +118,10 @@ func (c *client) Start(ctx context.Context, execPath string, opts ...OpOption) (
NumNodes: &ret.numNodes,
ChainConfigs: ret.chainConfigs,
UpgradeConfigs: ret.upgradeConfigs,
SubnetConfigs: ret.subnetConfigs,
ChainConfigFiles: ret.chainConfigs,
}
if ret.trackSubnets != "" {
req.WhitelistedSubnets = &ret.trackSubnets
if ret.trackChains != "" {
req.WhitelistedChains = &ret.trackChains
}
if ret.rootDataDir != "" {
req.RootDataDir = &ret.rootDataDir
@@ -127,8 +129,8 @@ func (c *client) Start(ctx context.Context, execPath string, opts ...OpOption) (
if ret.pluginDir != "" {
req.PluginDir = ret.pluginDir
}
if len(ret.blockchainSpecs) > 0 {
req.BlockchainSpecs = ret.blockchainSpecs
if len(ret.chainSpecs) > 0 {
req.BlockchainSpecs = ret.chainSpecs
}
if ret.globalNodeConfig != "" {
req.GlobalNodeConfig = &ret.globalNodeConfig
@@ -143,31 +145,31 @@ func (c *client) Start(ctx context.Context, execPath string, opts ...OpOption) (
return c.controlc.Start(ctx, req)
}
func (c *client) CreateBlockchains(ctx context.Context, blockchainSpecs []*rpcpb.BlockchainSpec) (*rpcpb.CreateBlockchainsResponse, error) {
func (c *client) CreateChains(ctx context.Context, chainSpecs []*rpcpb.BlockchainSpec) (*rpcpb.CreateBlockchainsResponse, error) {
req := &rpcpb.CreateBlockchainsRequest{
BlockchainSpecs: blockchainSpecs,
BlockchainSpecs: chainSpecs,
}
c.log.Info("create blockchains")
c.log.Info("create chains")
return c.controlc.CreateBlockchains(ctx, req)
}
func (c *client) CreateSubnets(ctx context.Context, subnetSpecs []*rpcpb.SubnetSpec) (*rpcpb.CreateSubnetsResponse, error) {
req := &rpcpb.CreateSubnetsRequest{
SubnetSpecs: subnetSpecs,
func (c *client) CreateParticipantGroups(ctx context.Context, participantsSpecs []*rpcpb.ChainSpec) (*rpcpb.CreateChainsResponse, error) {
req := &rpcpb.CreateChainsRequest{
ChainSpecs: participantsSpecs,
}
c.log.Info("create subnets")
return c.controlc.CreateSubnets(ctx, req)
c.log.Info("create participant groups")
return c.controlc.CreateChains(ctx, req)
}
func (c *client) TransformElasticSubnets(ctx context.Context, elasticSubnetSpecs []*rpcpb.ElasticSubnetSpec) (*rpcpb.TransformElasticSubnetsResponse, error) {
req := &rpcpb.TransformElasticSubnetsRequest{
ElasticSubnetSpec: elasticSubnetSpecs,
func (c *client) TransformElasticChains(ctx context.Context, elasticChainSpecs []*rpcpb.ElasticChainSpec) (*rpcpb.TransformElasticChainsResponse, error) {
req := &rpcpb.TransformElasticChainsRequest{
ElasticChainSpec: elasticChainSpecs,
}
c.log.Info("transform subnets")
return c.controlc.TransformElasticSubnets(ctx, req)
c.log.Info("transform chains")
return c.controlc.TransformElasticChains(ctx, req)
}
func (c *client) AddPermissionlessValidator(ctx context.Context, validatorSpec []*rpcpb.PermissionlessValidatorSpec) (*rpcpb.AddPermissionlessValidatorResponse, error) {
@@ -175,17 +177,17 @@ func (c *client) AddPermissionlessValidator(ctx context.Context, validatorSpec [
ValidatorSpec: validatorSpec,
}
c.log.Info("add permissionless validators to elastic subnets")
c.log.Info("add permissionless validators to elastic chains")
return c.controlc.AddPermissionlessValidator(ctx, req)
}
func (c *client) RemoveSubnetValidator(ctx context.Context, validatorSpec []*rpcpb.RemoveSubnetValidatorSpec) (*rpcpb.RemoveSubnetValidatorResponse, error) {
req := &rpcpb.RemoveSubnetValidatorRequest{
func (c *client) RemoveChainValidator(ctx context.Context, validatorSpec []*rpcpb.RemoveChainValidatorSpec) (*rpcpb.RemoveChainValidatorResponse, error) {
req := &rpcpb.RemoveChainValidatorRequest{
ValidatorSpec: validatorSpec,
}
c.log.Info("remove subnet validator")
return c.controlc.RemoveSubnetValidator(ctx, req)
c.log.Info("remove chain validator")
return c.controlc.RemoveChainValidator(ctx, req)
}
func (c *client) Health(ctx context.Context) (*rpcpb.HealthResponse, error) {
@@ -274,7 +276,7 @@ func (c *client) AddNode(ctx context.Context, name string, execPath string, opts
NodeConfig: &ret.globalNodeConfig,
ChainConfigs: ret.chainConfigs,
UpgradeConfigs: ret.upgradeConfigs,
SubnetConfigs: ret.subnetConfigs,
ChainConfigFiles: ret.chainConfigs,
}
if ret.pluginDir != "" {
@@ -311,12 +313,12 @@ func (c *client) RestartNode(ctx context.Context, name string, opts ...OpOption)
if ret.pluginDir != "" {
req.PluginDir = ret.pluginDir
}
if ret.trackSubnets != "" {
req.WhitelistedSubnets = &ret.trackSubnets
if ret.trackChains != "" {
req.WhitelistedChains = &ret.trackChains
}
req.ChainConfigs = ret.chainConfigs
req.UpgradeConfigs = ret.upgradeConfigs
req.SubnetConfigs = ret.subnetConfigs
req.ChainConfigFiles = ret.chainConfigs
c.log.Info("restart node", zap.String("name", name))
return c.controlc.RestartNode(ctx, req)
@@ -350,7 +352,7 @@ func (c *client) LoadSnapshot(ctx context.Context, snapshotName string, opts ...
SnapshotName: snapshotName,
ChainConfigs: ret.chainConfigs,
UpgradeConfigs: ret.upgradeConfigs,
SubnetConfigs: ret.subnetConfigs,
ChainConfigFiles: ret.chainConfigs,
}
if ret.execPath != "" {
req.ExecPath = &ret.execPath
@@ -392,16 +394,16 @@ func (c *client) Close() error {
type Op struct {
numNodes uint32
execPath string
trackSubnets string
trackChains string
globalNodeConfig string
rootDataDir string
pluginDir string
blockchainSpecs []*rpcpb.BlockchainSpec
chainSpecs []*rpcpb.BlockchainSpec
customNodeConfigs map[string]string
numSubnets uint32
numChains uint32
chainConfigs map[string]string
upgradeConfigs map[string]string
subnetConfigs map[string]string
pChainConfigs map[string]string
reassignPortsIfUsed bool
dynamicPorts bool
}
@@ -432,15 +434,15 @@ func WithExecPath(execPath string) OpOption {
}
}
func WithWhitelistedSubnets(trackSubnets string) OpOption {
func WithWhitelistedChains(trackChains string) OpOption {
return func(op *Op) {
op.trackSubnets = trackSubnets
op.trackChains = trackChains
}
}
func WithTrackSubnets(trackSubnets string) OpOption {
func WithTrackChains(trackChains string) OpOption {
return func(op *Op) {
op.trackSubnets = trackSubnets
op.trackChains = trackChains
}
}
@@ -456,10 +458,10 @@ func WithPluginDir(pluginDir string) OpOption {
}
}
// Slice of BlockchainSpec
func WithBlockchainSpecs(blockchainSpecs []*rpcpb.BlockchainSpec) OpOption {
// WithChainSpecs sets the chain specifications for creating chains with VMs
func WithChainSpecs(chainSpecs []*rpcpb.BlockchainSpec) OpOption {
return func(op *Op) {
op.blockchainSpecs = blockchainSpecs
op.chainSpecs = chainSpecs
}
}
@@ -477,10 +479,10 @@ func WithUpgradeConfigs(upgradeConfigs map[string]string) OpOption {
}
}
// Map from subnet id to its configuration json contents.
func WithSubnetConfigs(subnetConfigs map[string]string) OpOption {
// Map from chain id to its configuration json contents.
func WithChainConfigFiles(chainConfigs map[string]string) OpOption {
return func(op *Op) {
op.subnetConfigs = subnetConfigs
op.chainConfigs = chainConfigs
}
}
@@ -491,9 +493,9 @@ func WithCustomNodeConfigs(customNodeConfigs map[string]string) OpOption {
}
}
func WithNumSubnets(numSubnets uint32) OpOption {
func WithNumChains(numChains uint32) OpOption {
return func(op *Op) {
op.numSubnets = numSubnets
op.numChains = numChains
}
}
+88 -88
View File
@@ -1,5 +1,5 @@
// Copyright (C) 2021-2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// SPDX-License-Identifier: BSD-3-Clause
package control
@@ -35,7 +35,7 @@ const clientRootDirPrefix = "client"
var (
logLevel string
logDir string
trackSubnets string
trackChains string
endpoint string
dialTimeout time.Duration
requestTimeout time.Duration
@@ -59,11 +59,11 @@ func NewCommand() *cobra.Command {
cmd.AddCommand(
newRPCVersionCommand(),
newStartCommand(),
newCreateBlockchainsCommand(),
newCreateSubnetsCommand(),
newTransformElasticSubnetsCommand(),
newCreateChainsCommand(),
newCreateParticipantGroupsCommand(),
newTransformElasticChainsCommand(),
newAddPermissionlessValidatorCommand(),
newRemoveSubnetValidatorCommand(),
newRemoveChainValidatorCommand(),
newHealthCommand(),
newWaitForHealthyCommand(),
newURIsCommand(),
@@ -92,12 +92,12 @@ var (
pluginDir string
globalNodeConfig string
addNodeConfig string
blockchainSpecsStr string
chainSpecsStr string
customNodeConfigs string
rootDataDir string
chainConfigs string
upgradeConfigs string
subnetConfigs string
pChainConfigs string
reassignPortsIfUsed bool
dynamicPorts bool
)
@@ -191,7 +191,7 @@ func newStartCommand() *cobra.Command {
"[optional] root data directory to store logs and configurations",
)
cmd.PersistentFlags().StringVar(
&blockchainSpecsStr,
&chainSpecsStr,
"blockchain-specs",
"",
"[optional] JSON string of array of [(VM name, genesis file path)]",
@@ -209,10 +209,10 @@ func newStartCommand() *cobra.Command {
"[optional] custom node configs as JSON string of map, for each node individually. Common entries override `global-node-config`, but can be combined. Invalidates `number-of-nodes` (provide all node configs if used).",
)
cmd.PersistentFlags().StringVar(
&trackSubnets,
"whitelisted-subnets",
&trackChains,
"whitelisted-chains",
"",
"[optional] whitelisted subnets (comma-separated)",
"[optional] whitelisted chains (comma-separated)",
)
cmd.PersistentFlags().StringVar(
&chainConfigs,
@@ -227,10 +227,10 @@ func newStartCommand() *cobra.Command {
"[optional] JSON string of map from chain id to its upgrade file contents",
)
cmd.PersistentFlags().StringVar(
&subnetConfigs,
"subnet-configs",
&chainConfigs,
"chain-configs",
"",
"[optional] JSON string of map from subnet id to its config file contents",
"[optional] JSON string of map from chain id to its config file contents",
)
cmd.PersistentFlags().BoolVar(
&reassignPortsIfUsed,
@@ -260,7 +260,7 @@ func startFunc(*cobra.Command, []string) error {
opts := []client.OpOption{
client.WithNumNodes(numNodes),
client.WithPluginDir(pluginDir),
client.WithTrackSubnets(trackSubnets),
client.WithTrackChains(trackChains),
client.WithRootDataDir(rootDataDir),
client.WithReassignPortsIfUsed(reassignPortsIfUsed),
client.WithDynamicPorts(dynamicPorts),
@@ -284,12 +284,12 @@ func startFunc(*cobra.Command, []string) error {
opts = append(opts, client.WithCustomNodeConfigs(nodeConfigs))
}
if blockchainSpecsStr != "" {
blockchainSpecs := []*rpcpb.BlockchainSpec{}
if err := json.Unmarshal([]byte(blockchainSpecsStr), &blockchainSpecs); err != nil {
if chainSpecsStr != "" {
chainSpecs := []*rpcpb.BlockchainSpec{}
if err := json.Unmarshal([]byte(chainSpecsStr), &chainSpecs); err != nil {
return err
}
opts = append(opts, client.WithBlockchainSpecs(blockchainSpecs))
opts = append(opts, client.WithChainSpecs(chainSpecs))
}
if chainConfigs != "" {
@@ -306,12 +306,12 @@ func startFunc(*cobra.Command, []string) error {
}
opts = append(opts, client.WithUpgradeConfigs(upgradeConfigsMap))
}
if subnetConfigs != "" {
subnetConfigsMap := make(map[string]string)
if err := json.Unmarshal([]byte(subnetConfigs), &subnetConfigsMap); err != nil {
if chainConfigs != "" {
chainConfigsMap := make(map[string]string)
if err := json.Unmarshal([]byte(chainConfigs), &chainConfigsMap); err != nil {
return err
}
opts = append(opts, client.WithSubnetConfigs(subnetConfigsMap))
opts = append(opts, client.WithChainConfigFiles(chainConfigsMap))
}
ctx := getAsyncContext()
@@ -329,35 +329,35 @@ func startFunc(*cobra.Command, []string) error {
return nil
}
func newCreateBlockchainsCommand() *cobra.Command {
func newCreateChainsCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "create-blockchains blockchain-specs [options]",
Short: "Create blockchains.",
RunE: createBlockchainsFunc,
RunE: createChainsFunc,
Args: cobra.ExactArgs(1),
}
return cmd
}
func createBlockchainsFunc(_ *cobra.Command, args []string) error {
func createChainsFunc(_ *cobra.Command, args []string) error {
cli, err := newClient()
if err != nil {
return err
}
defer cli.Close()
blockchainSpecsStr := args[0]
chainSpecsStr := args[0]
blockchainSpecs := []*rpcpb.BlockchainSpec{}
if err := json.Unmarshal([]byte(blockchainSpecsStr), &blockchainSpecs); err != nil {
chainSpecs := []*rpcpb.BlockchainSpec{}
if err := json.Unmarshal([]byte(chainSpecsStr), &chainSpecs); err != nil {
return err
}
ctx := getAsyncContext()
info, err := cli.CreateBlockchains(
info, err := cli.CreateChains(
ctx,
blockchainSpecs,
chainSpecs,
)
if err != nil {
return err
@@ -367,49 +367,49 @@ func createBlockchainsFunc(_ *cobra.Command, args []string) error {
return nil
}
func newCreateSubnetsCommand() *cobra.Command {
func newCreateParticipantGroupsCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "create-subnets [options]",
Short: "Create subnets.",
RunE: createSubnetsFunc,
Use: "create-participant-groups [options]",
Short: "Create participant groups.",
RunE: createParticipantGroupsFunc,
Args: cobra.ExactArgs(1),
}
return cmd
}
func createSubnetsFunc(_ *cobra.Command, args []string) error {
func createParticipantGroupsFunc(_ *cobra.Command, args []string) error {
cli, err := newClient()
if err != nil {
return err
}
defer cli.Close()
subnetSpecsStr := args[0]
participantsSpecsStr := args[0]
subnetSpecs := []*rpcpb.SubnetSpec{}
if err := json.Unmarshal([]byte(subnetSpecsStr), &subnetSpecs); err != nil {
participantsSpecs := []*rpcpb.ChainSpec{}
if err := json.Unmarshal([]byte(participantsSpecsStr), &participantsSpecs); err != nil {
return err
}
ctx := getAsyncContext()
info, err := cli.CreateSubnets(
info, err := cli.CreateParticipantGroups(
ctx,
subnetSpecs,
participantsSpecs,
)
if err != nil {
return err
}
ux.Print(log, luxlog.Green.Wrap("create-subnets response: %+v"), info)
ux.Print(log, luxlog.Green.Wrap("create-chains response: %+v"), info)
return nil
}
func newTransformElasticSubnetsCommand() *cobra.Command {
func newTransformElasticChainsCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "elastic-subnets elastic_subnets_specs [options]",
Short: "Transform subnets to elastic subnets.",
RunE: transformElasticSubnetsFunc,
Use: "elastic-chains elastic_chains_specs [options]",
Short: "Transform chains to elastic chains.",
RunE: transformElasticChainsFunc,
Args: cobra.ExactArgs(1),
}
return cmd
@@ -418,48 +418,48 @@ func newTransformElasticSubnetsCommand() *cobra.Command {
func newAddPermissionlessValidatorCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "add-permissionless-validator permissionlessValidatorSpecs [options]",
Short: "Add permissionless validator to elastic subnets.",
Short: "Add permissionless validator to elastic chains.",
RunE: addPermissionlessValidatorFunc,
Args: cobra.ExactArgs(1),
}
return cmd
}
func newRemoveSubnetValidatorCommand() *cobra.Command {
func newRemoveChainValidatorCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "remove-subnet-validator removeValidatorSpec [options]",
Short: "Remove subnet validator",
RunE: removeSubnetValidatorFunc,
Use: "remove-chain-validator removeValidatorSpec [options]",
Short: "Remove chain validator",
RunE: removeChainValidatorFunc,
Args: cobra.ExactArgs(1),
}
return cmd
}
func transformElasticSubnetsFunc(_ *cobra.Command, args []string) error {
func transformElasticChainsFunc(_ *cobra.Command, args []string) error {
cli, err := newClient()
if err != nil {
return err
}
defer cli.Close()
elasticSubnetSpecsStr := args[0]
elasticParticipantsSpecsStr := args[0]
elasticSubnetSpecs := []*rpcpb.ElasticSubnetSpec{}
if err := json.Unmarshal([]byte(elasticSubnetSpecsStr), &elasticSubnetSpecs); err != nil {
elasticParticipantsSpecs := []*rpcpb.ElasticChainSpec{}
if err := json.Unmarshal([]byte(elasticParticipantsSpecsStr), &elasticParticipantsSpecs); err != nil {
return err
}
ctx := getAsyncContext()
info, err := cli.TransformElasticSubnets(
info, err := cli.TransformElasticChains(
ctx,
elasticSubnetSpecs,
elasticParticipantsSpecs,
)
if err != nil {
return err
}
ux.Print(log, luxlog.Green.Wrap("elastic-subnets response: %+v"), info)
ux.Print(log, luxlog.Green.Wrap("elastic-chains response: %+v"), info)
return nil
}
@@ -491,7 +491,7 @@ func addPermissionlessValidatorFunc(_ *cobra.Command, args []string) error {
return nil
}
func removeSubnetValidatorFunc(_ *cobra.Command, args []string) error {
func removeChainValidatorFunc(_ *cobra.Command, args []string) error {
cli, err := newClient()
if err != nil {
return err
@@ -500,14 +500,14 @@ func removeSubnetValidatorFunc(_ *cobra.Command, args []string) error {
validatorSpecStr := args[0]
validatorSpec := []*rpcpb.RemoveSubnetValidatorSpec{}
validatorSpec := []*rpcpb.RemoveChainValidatorSpec{}
if err := json.Unmarshal([]byte(validatorSpecStr), &validatorSpec); err != nil {
return err
}
ctx := getAsyncContext()
info, err := cli.RemoveSubnetValidator(
info, err := cli.RemoveChainValidator(
ctx,
validatorSpec,
)
@@ -515,7 +515,7 @@ func removeSubnetValidatorFunc(_ *cobra.Command, args []string) error {
return err
}
ux.Print(log, luxlog.Green.Wrap("remove-subnet-validator response: %+v"), info)
ux.Print(log, luxlog.Green.Wrap("remove-chain-validator response: %+v"), info)
return nil
}
@@ -812,10 +812,10 @@ func newAddNodeCommand() *cobra.Command {
"[optional] JSON string of map from chain id to its upgrade file contents",
)
cmd.PersistentFlags().StringVar(
&subnetConfigs,
"subnet-configs",
&chainConfigs,
"chain-configs",
"",
"[optional] JSON string of map from subnet id to its config file contents",
"[optional] JSON string of map from chain id to its config file contents",
)
return cmd
}
@@ -857,12 +857,12 @@ func addNodeFunc(_ *cobra.Command, args []string) error {
}
opts = append(opts, client.WithUpgradeConfigs(upgradeConfigsMap))
}
if subnetConfigs != "" {
subnetConfigsMap := make(map[string]string)
if err := json.Unmarshal([]byte(subnetConfigs), &subnetConfigsMap); err != nil {
if chainConfigs != "" {
chainConfigsMap := make(map[string]string)
if err := json.Unmarshal([]byte(chainConfigs), &chainConfigsMap); err != nil {
return err
}
opts = append(opts, client.WithSubnetConfigs(subnetConfigsMap))
opts = append(opts, client.WithChainConfigFiles(chainConfigsMap))
}
ctx, cancel := context.WithTimeout(context.Background(), requestTimeout)
@@ -895,10 +895,10 @@ func newRestartNodeCommand() *cobra.Command {
"node binary path",
)
cmd.PersistentFlags().StringVar(
&trackSubnets,
"whitelisted-subnets",
&trackChains,
"whitelisted-chains",
"",
"[optional] whitelisted subnets (comma-separated)",
"[optional] whitelisted chains (comma-separated)",
)
cmd.PersistentFlags().StringVar(
&pluginDir,
@@ -919,10 +919,10 @@ func newRestartNodeCommand() *cobra.Command {
"[optional] JSON string of map from chain id to its upgrade file contents",
)
cmd.PersistentFlags().StringVar(
&subnetConfigs,
"subnet-configs",
&chainConfigs,
"chain-configs",
"",
"[optional] JSON string of map from subnet id to its config file contents",
"[optional] JSON string of map from chain id to its config file contents",
)
return cmd
}
@@ -939,7 +939,7 @@ func restartNodeFunc(_ *cobra.Command, args []string) error {
opts := []client.OpOption{
client.WithExecPath(luxdBinPath),
client.WithPluginDir(pluginDir),
client.WithTrackSubnets(trackSubnets),
client.WithTrackChains(trackChains),
}
if chainConfigs != "" {
@@ -956,12 +956,12 @@ func restartNodeFunc(_ *cobra.Command, args []string) error {
}
opts = append(opts, client.WithUpgradeConfigs(upgradeConfigsMap))
}
if subnetConfigs != "" {
subnetConfigsMap := make(map[string]string)
if err := json.Unmarshal([]byte(subnetConfigs), &subnetConfigsMap); err != nil {
if chainConfigs != "" {
chainConfigsMap := make(map[string]string)
if err := json.Unmarshal([]byte(chainConfigs), &chainConfigsMap); err != nil {
return err
}
opts = append(opts, client.WithSubnetConfigs(subnetConfigsMap))
opts = append(opts, client.WithChainConfigFiles(chainConfigsMap))
}
ctx, cancel := context.WithTimeout(context.Background(), requestTimeout)
@@ -1172,10 +1172,10 @@ func newLoadSnapshotCommand() *cobra.Command {
"[optional] JSON string of map from chain id to its upgrade file contents",
)
cmd.PersistentFlags().StringVar(
&subnetConfigs,
"subnet-configs",
&chainConfigs,
"chain-configs",
"",
"[optional] JSON string of map from subnet id to its config file contents",
"[optional] JSON string of map from chain id to its config file contents",
)
cmd.PersistentFlags().StringVar(
&globalNodeConfig,
@@ -1220,12 +1220,12 @@ func loadSnapshotFunc(_ *cobra.Command, args []string) error {
}
opts = append(opts, client.WithUpgradeConfigs(upgradeConfigsMap))
}
if subnetConfigs != "" {
subnetConfigsMap := make(map[string]string)
if err := json.Unmarshal([]byte(subnetConfigs), &subnetConfigsMap); err != nil {
if chainConfigs != "" {
chainConfigsMap := make(map[string]string)
if err := json.Unmarshal([]byte(chainConfigs), &chainConfigsMap); err != nil {
return err
}
opts = append(opts, client.WithSubnetConfigs(subnetConfigsMap))
opts = append(opts, client.WithChainConfigFiles(chainConfigsMap))
}
if globalNodeConfig != "" {
+1 -1
View File
@@ -1,5 +1,5 @@
// Copyright (C) 2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// SPDX-License-Identifier: BSD-3-Clause
package cmd
+17 -17
View File
@@ -24,7 +24,7 @@ var multinetCmd = &cobra.Command{
Short: "Manage multiple heterogeneous networks in parallel",
Long: `Run and validate multiple networks simultaneously with shared BadgerDB
for cross-chain ACID transactions. Supports both primary networks (mainnet/testnet)
and their subnets (L1s/L2s).`,
and their chains (L1s/L2s).`,
}
// startCmd starts multiple networks
@@ -40,7 +40,7 @@ shared database for cross-chain transactions.`,
var configureCmd = &cobra.Command{
Use: "configure",
Short: "Configure networks for parallel validation",
Long: `Set up mainnet, testnet, and their subnets for parallel validation.`,
Long: `Set up mainnet, testnet, and their chains for parallel validation.`,
RunE: runConfigure,
}
@@ -99,7 +99,7 @@ func runStartMulti(cmd *cobra.Command, args []string) error {
fmt.Println("\nNetwork endpoints:")
fmt.Println(" Lux Mainnet: http://localhost:9630")
fmt.Println(" Lux Testnet: http://localhost:9620")
fmt.Println("\nSubnets are accessible through their parent networks.")
fmt.Println("\nChains are accessible through their parent networks.")
fmt.Println("\nPress Ctrl+C to stop all networks...")
// Wait for interrupt
@@ -159,27 +159,27 @@ func loadDefaultNetworks(manager *multinet.MultiNetworkManager) error {
},
}
// Zoo Subnet on Mainnet
// Zoo Chain on Mainnet
zooMainnetConfig := multinet.NetworkConfig{
NetworkID: 200200,
Name: "Zoo Network (Mainnet Subnet)",
Type: multinet.NetworkTypeSubnet,
Name: "Zoo Network (Mainnet Chain)",
Type: multinet.NetworkTypeChain,
ParentID: 96369,
HTTPPort: 0, // Uses parent network's port
StakingPort: 0, // Uses parent network's port
DataDir: "/tmp/multinetwork/mainnet/subnets/zoo",
DataDir: "/tmp/multinetwork/mainnet/chains/zoo",
Validators: 5,
}
// Hanzo Subnet on Mainnet
// Hanzo Chain on Mainnet
hanzoMainnetConfig := multinet.NetworkConfig{
NetworkID: 36963,
Name: "Hanzo Network (Mainnet Subnet)",
Type: multinet.NetworkTypeSubnet,
Name: "Hanzo Network (Mainnet Chain)",
Type: multinet.NetworkTypeChain,
ParentID: 96369,
HTTPPort: 0,
StakingPort: 0,
DataDir: "/tmp/multinetwork/mainnet/subnets/hanzo",
DataDir: "/tmp/multinetwork/mainnet/chains/hanzo",
Validators: 5,
}
@@ -245,17 +245,17 @@ func runConfigure(cmd *cobra.Command, args []string) error {
{
NetworkID: 200200,
Name: "Zoo Network",
Type: multinet.NetworkTypeSubnet,
Type: multinet.NetworkTypeChain,
ParentID: 96369,
DataDir: "/tmp/multinetwork/mainnet/subnets/zoo",
DataDir: "/tmp/multinetwork/mainnet/chains/zoo",
Validators: 5,
},
{
NetworkID: 36963,
Name: "Hanzo Network",
Type: multinet.NetworkTypeSubnet,
Type: multinet.NetworkTypeChain,
ParentID: 96369,
DataDir: "/tmp/multinetwork/mainnet/subnets/hanzo",
DataDir: "/tmp/multinetwork/mainnet/chains/hanzo",
Validators: 5,
},
}
@@ -278,8 +278,8 @@ func runConfigure(cmd *cobra.Command, args []string) error {
fmt.Println("\nConfiguration includes:")
fmt.Println(" • Lux Mainnet (96369) - Primary Network")
fmt.Println(" • Lux Testnet (96368) - Primary Network")
fmt.Println(" • Zoo Network (200200) - Subnet on Mainnet")
fmt.Println(" • Hanzo Network (36963) - Subnet on Mainnet")
fmt.Println(" • Zoo Network (200200) - Chain on Mainnet")
fmt.Println(" • Hanzo Network (36963) - Chain on Mainnet")
return nil
}
+1 -1
View File
@@ -1,5 +1,5 @@
// Copyright (C) 2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// SPDX-License-Identifier: BSD-3-Clause
package commands
+1 -1
View File
@@ -1,5 +1,5 @@
// Copyright (C) 2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// SPDX-License-Identifier: BSD-3-Clause
package commands
+1 -1
View File
@@ -1,5 +1,5 @@
// Copyright (C) 2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// SPDX-License-Identifier: BSD-3-Clause
package commands
+1 -1
View File
@@ -1,5 +1,5 @@
// Copyright (C) 2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// SPDX-License-Identifier: BSD-3-Clause
package commands
+1 -1
View File
@@ -1,5 +1,5 @@
// Copyright (C) 2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// SPDX-License-Identifier: BSD-3-Clause
package commands
+1 -1
View File
@@ -1,5 +1,5 @@
// Copyright (C) 2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// SPDX-License-Identifier: BSD-3-Clause
package commands
+1 -1
View File
@@ -1,5 +1,5 @@
// Copyright (C) 2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// SPDX-License-Identifier: BSD-3-Clause
package main
+1 -1
View File
@@ -1,5 +1,5 @@
// Copyright (C) 2021-2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// SPDX-License-Identifier: BSD-3-Clause
package ping
+1 -1
View File
@@ -1,5 +1,5 @@
// Copyright (C) 2021-2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// SPDX-License-Identifier: BSD-3-Clause
package cmd
+1 -1
View File
@@ -1,5 +1,5 @@
// Copyright (C) 2021-2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// SPDX-License-Identifier: BSD-3-Clause
package server
+1 -1
View File
@@ -114,7 +114,7 @@ func getNetworkConfig(network string) multinet.NetworkConfig {
Validators: 3,
}
default:
// Could be a subnet name
// Could be a chain name
return multinet.NetworkConfig{}
}
}
+1 -1
View File
@@ -1,5 +1,5 @@
// Copyright (C) 2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// SPDX-License-Identifier: BSD-3-Clause
package lux
+1 -1
View File
@@ -1,5 +1,5 @@
// Copyright (C) 2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// SPDX-License-Identifier: BSD-3-Clause
package engines
+1 -1
View File
@@ -1,5 +1,5 @@
// Copyright (C) 2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// SPDX-License-Identifier: BSD-3-Clause
package engines
+1 -1
View File
@@ -1,5 +1,5 @@
// Copyright (C) 2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// SPDX-License-Identifier: BSD-3-Clause
package geth
+1 -1
View File
@@ -1,5 +1,5 @@
// Copyright (C) 2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// SPDX-License-Identifier: BSD-3-Clause
package lux
+1 -1
View File
@@ -1,5 +1,5 @@
// Copyright (C) 2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// SPDX-License-Identifier: BSD-3-Clause
package engines
+1 -1
View File
@@ -1,5 +1,5 @@
// Copyright (C) 2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// SPDX-License-Identifier: BSD-3-Clause
package engines
+6 -6
View File
@@ -1,5 +1,5 @@
// Copyright (C) 2021-2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// SPDX-License-Identifier: BSD-3-Clause
package main
@@ -126,22 +126,22 @@ func run(log luxlog.Logger, binaryPath string) error {
}
log.Info("All nodes healthy")
// Create DEX blockchain on a new subnet
// Create DEX blockchain on a new chain
log.Info("Creating DEX blockchain...")
createCtx, createCancel := context.WithTimeout(context.Background(), createBlockchainTimeout)
defer createCancel()
// DEX blockchain specification
dexBlockchainSpec := []network.BlockchainSpec{
dexChainSpec := []network.ChainSpec{
{
VMName: "dexvm", // Matches constants.DexVMName
Genesis: dexGenesisJSON(), // DEX genesis configuration
BlockchainAlias: "dex", // Alias for easier access
// SubnetID not specified = creates new subnet with all nodes as validators
Alias: "dex", // Alias for easier access
// ChainID not specified = creates new chain with all nodes as validators
},
}
chainIDs, err := nw.CreateBlockchains(createCtx, dexBlockchainSpec)
chainIDs, err := nw.CreateChains(createCtx, dexChainSpec)
if err != nil {
return fmt.Errorf("failed to create DEX blockchain: %w", err)
}
+207
View File
@@ -0,0 +1,207 @@
// Copyright (C) 2021-2025, Lux Industries Inc. All rights reserved.
// SPDX-License-Identifier: BSD-3-Clause
package main
import (
"context"
"fmt"
"os"
"os/signal"
"syscall"
"time"
"github.com/luxfi/netrunner/local"
"github.com/luxfi/netrunner/network"
"github.com/luxfi/node/config"
luxlog "github.com/luxfi/log"
"github.com/luxfi/log/level"
"go.uber.org/zap"
)
const (
healthyTimeout = 3 * time.Minute
createBlockchainTimeout = 5 * time.Minute
luxdBinaryPath = "/Users/z/go/bin/luxd"
pluginDir = "/Users/z/.luxd/plugins"
)
// L2Chain defines configuration for deploying an L2 chain
type L2Chain struct {
Name string
ChainID uint64
VMName string
Alias string
GenesisPath string
}
// L2 Chain configurations
var l2Chains = []L2Chain{
{
Name: "Zoo",
ChainID: 200200,
VMName: "subnetevm",
Alias: "zoo",
GenesisPath: "/Users/z/work/lux/genesis/chains/zoo/genesis.json",
},
{
Name: "SPC",
ChainID: 36911,
VMName: "subnetevm",
Alias: "spc",
GenesisPath: "/Users/z/work/lux/genesis/chains/spc/genesis.json",
},
{
Name: "Hanzo AI",
ChainID: 36963,
VMName: "subnetevm",
Alias: "hanzo",
GenesisPath: "/Users/z/work/lux/genesis/chains/ai/genesis.json",
},
}
func shutdownOnSignal(
log luxlog.Logger,
n network.Network,
signalChan chan os.Signal,
closedOnShutdownChan chan struct{},
) {
sig := <-signalChan
log.Info("got OS signal", zap.Stringer("signal", sig))
if err := n.Stop(context.Background()); err != nil {
log.Info("error stopping network", zap.Error(err))
}
signal.Reset()
close(signalChan)
close(closedOnShutdownChan)
}
func main() {
logFactory := luxlog.NewFactoryWithConfig(luxlog.Config{
DisplayLevel: level.Info,
LogLevel: level.Debug,
})
log, err := logFactory.Make("l2chains")
if err != nil {
fmt.Println(err)
os.Exit(1)
}
if err := run(log); err != nil {
log.Fatal("fatal error", zap.Error(err))
os.Exit(1)
}
}
func run(log luxlog.Logger) error {
// Create mainnet config for 5-node network
log.Info("Creating mainnet configuration...")
netConfig, err := local.NewMainnetConfig(luxdBinaryPath, 5)
if err != nil {
return fmt.Errorf("failed to create mainnet config: %w", err)
}
// Add plugin directory and allow private IPs (required for local testing)
// Also enable output redirection to see errors
for i := range netConfig.NodeConfigs {
netConfig.NodeConfigs[i].Flags[config.PluginDirKey] = pluginDir
netConfig.NodeConfigs[i].Flags[config.NetworkAllowPrivateIPsKey] = true
netConfig.NodeConfigs[i].RedirectStdout = true
netConfig.NodeConfigs[i].RedirectStderr = true
}
// Create network
log.Info("Starting 5-node mainnet network...")
nw, err := local.NewNetwork(log, netConfig, "", "", true)
if err != nil {
return fmt.Errorf("failed to create network: %w", err)
}
defer func() {
if err := nw.Stop(context.Background()); err != nil {
log.Info("error stopping network", zap.Error(err))
}
}()
// Setup signal handler
signalsChan := make(chan os.Signal, 1)
signal.Notify(signalsChan, syscall.SIGINT)
signal.Notify(signalsChan, syscall.SIGTERM)
closedOnShutdownCh := make(chan struct{})
go func() {
shutdownOnSignal(log, nw, signalsChan, closedOnShutdownCh)
}()
// Wait until the nodes in the network are ready
ctx, cancel := context.WithTimeout(context.Background(), healthyTimeout)
defer cancel()
log.Info("Waiting for all nodes to report healthy...")
if err := nw.Healthy(ctx); err != nil {
return fmt.Errorf("network failed to become healthy: %w", err)
}
log.Info("All nodes healthy")
// Get network info
nodes, err := nw.GetAllNodes()
if err != nil {
return fmt.Errorf("failed to get nodes: %w", err)
}
log.Info("Network ready", zap.Int("nodes", len(nodes)))
// Deploy L2 chains
for _, chain := range l2Chains {
log.Info("Deploying L2 chain...",
zap.String("name", chain.Name),
zap.Uint64("chainId", chain.ChainID),
zap.String("alias", chain.Alias),
)
genesis, err := os.ReadFile(chain.GenesisPath)
if err != nil {
log.Error("Failed to read genesis file",
zap.String("chain", chain.Name),
zap.String("path", chain.GenesisPath),
zap.Error(err),
)
continue
}
createCtx, createCancel := context.WithTimeout(context.Background(), createBlockchainTimeout)
chainSpec := []network.ChainSpec{
{
VMName: chain.VMName,
Genesis: genesis,
Alias: chain.Alias,
BlockchainName: chain.Alias, // Use unique name for each chain
},
}
chainIDs, err := nw.CreateChains(createCtx, chainSpec)
createCancel()
if err != nil {
log.Error("Failed to create chain",
zap.String("chain", chain.Name),
zap.String("error", err.Error()),
)
continue
}
log.Info("L2 chain deployed successfully",
zap.String("name", chain.Name),
zap.String("blockchain-id", chainIDs[0].String()),
zap.String("alias", chain.Alias),
)
}
// Print connection info
log.Info("L2 chains deployed. Connect to any node:")
for name, node := range nodes {
log.Info("Node available",
zap.String("name", name),
zap.String("url", fmt.Sprintf("http://%s:%d", node.GetURL(), node.GetAPIPort())),
)
}
log.Info("Network running. Press CTRL+C to exit...")
<-closedOnShutdownCh
return nil
}
+1 -2
View File
@@ -10,8 +10,7 @@ require (
github.com/luxfi/consensus v1.22.26
github.com/luxfi/constants v1.2.3
github.com/luxfi/crypto v1.17.22
github.com/luxfi/evm v0.8.1
github.com/luxfi/genesis v1.5.5
github.com/luxfi/genesis v1.5.6
github.com/luxfi/geth v1.16.52
github.com/luxfi/ids v1.2.4
github.com/luxfi/log v1.1.26
+2 -4
View File
@@ -293,10 +293,8 @@ github.com/luxfi/crypto v1.17.22 h1:aYR0R4xTOqVy9q6mEFMrg7Oru7itveniWR8f4U5vea4=
github.com/luxfi/crypto v1.17.22/go.mod h1:joL4xiznieeh61fs18bm/dF/HVrydzIPHDQk7HdZz9A=
github.com/luxfi/database v1.2.11 h1:xpw+WdIRtiVSa64RAM0BLPgHO1n40YArxTE1cJW6A5Q=
github.com/luxfi/database v1.2.11/go.mod h1:jXWOHWgIrgE7W2fSJp0lqGNV9ghRx6Gzev2v51c4GLQ=
github.com/luxfi/evm v0.8.1 h1:DmQC5UETgunf4e0HGHkeDJCFwU2dOflcHLDICvszN7g=
github.com/luxfi/evm v0.8.1/go.mod h1:ufY7D5sJ9mPUsj1ijuTXUU/ymiMoPoEI1mkjde3H1aA=
github.com/luxfi/genesis v1.5.5 h1:vYICVQZnnEa0MN6wpEw1NNT84pJWMjIuqqD7MO6ta+M=
github.com/luxfi/genesis v1.5.5/go.mod h1:mG7B/SExse3i/nVU42tcZ2J97HVv3iyCIX41X3JyYM4=
github.com/luxfi/genesis v1.5.6 h1:HySJ7YE016miZ+7d3XDVMJRa1GtBK3vOgrxIBp7PN80=
github.com/luxfi/genesis v1.5.6/go.mod h1:mG7B/SExse3i/nVU42tcZ2J97HVv3iyCIX41X3JyYM4=
github.com/luxfi/geth v1.16.52 h1:nkRjlVNlPgtSSOROS95PRXB1CyKGYLr3AstYb41TMtA=
github.com/luxfi/geth v1.16.52/go.mod h1:OJHnLi5CDtLY+QS4c7iN880zVUNcQXteJPgWpoQPj/8=
github.com/luxfi/go-bip32 v1.0.1 h1:UaPUSu6ScZ1wobQJJO+vePAN3OcUHYmq5DEc/uOU+SY=
+254 -279
View File
File diff suppressed because it is too large Load Diff
+15 -9
View File
@@ -1,5 +1,5 @@
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// SPDX-License-Identifier: BSD-3-Clause
package local
@@ -213,14 +213,20 @@ func NewConfigForNetwork(binaryPath string, numNodes uint32, networkID uint32) (
}
}
// Set initialStakedFunds to the first validator key address
// This address must exist in allocations with proper unlock schedule
// The genesis package uses this to split staked funds among validators
firstValidatorAddr, err := FormatAddress("P", hrp, validatorKeys[0].ShortID)
if err != nil {
return network.Config{}, fmt.Errorf("couldn't format first validator address: %w", err)
// Set initialStakedFunds to the SECOND validator key address (not first).
// The first key is kept OUT of initialStakedFunds so its allocation becomes
// regular P-chain UTXOs, which are needed for wallet operations (chain creation, etc).
// The second key's allocation will go to stakers for their stake weight.
if len(validatorKeys) > 1 {
secondValidatorAddr, err := FormatAddress("P", hrp, validatorKeys[1].ShortID)
if err != nil {
return network.Config{}, fmt.Errorf("couldn't format second validator address: %w", err)
}
genesis["initialStakedFunds"] = []string{secondValidatorAddr}
} else {
// Single node case: use empty initialStakedFunds
genesis["initialStakedFunds"] = []string{}
}
genesis["initialStakedFunds"] = []string{firstValidatorAddr}
// Update start time to now
genesis["startTime"] = uint64(now)
@@ -247,7 +253,7 @@ func NewConfigForNetwork(binaryPath string, numNodes uint32, networkID uint32) (
IsBeacon: true,
ChainConfigFiles: map[string]string{},
UpgradeConfigFiles: map[string]string{},
SubnetConfigFiles: map[string]string{},
PChainConfigFiles: map[string]string{},
}
}
+9 -9
View File
@@ -126,12 +126,12 @@ func writeFiles(networkID uint32, genesis []byte, nodeRootDir string, nodeConfig
return nil, err
}
flags[config.ChainConfigDirKey] = chainConfigDir
// subnet configs dir
subnetConfigDir := filepath.Join(nodeRootDir, subnetConfigSubDir)
if err := os.MkdirAll(subnetConfigDir, 0o750); err != nil {
// P-chain configs dir
pChainConfigDir := filepath.Join(nodeRootDir, pChainConfigSubDir)
if err := os.MkdirAll(pChainConfigDir, 0o750); err != nil {
return nil, err
}
flags[config.NetConfigDirKey] = subnetConfigDir
flags[config.NetConfigDirKey] = pChainConfigDir
// chain configs
for chainAlias, chainConfigFile := range nodeConfig.ChainConfigFiles {
chainConfigPath := filepath.Join(chainConfigDir, chainAlias, configFileName)
@@ -146,11 +146,11 @@ func writeFiles(networkID uint32, genesis []byte, nodeRootDir string, nodeConfig
return nil, fmt.Errorf("couldn't write file at %q: %w", chainUpgradePath, err)
}
}
// subnet configs
for subnetID, subnetConfigFile := range nodeConfig.SubnetConfigFiles {
subnetConfigPath := filepath.Join(subnetConfigDir, subnetID+".json")
if err := createFileAndWrite(subnetConfigPath, []byte(subnetConfigFile)); err != nil {
return nil, fmt.Errorf("couldn't write file at %q: %w", subnetConfigPath, err)
// P-chain configs
for chainID, pChainConfigFile := range nodeConfig.PChainConfigFiles {
pChainConfigPath := filepath.Join(pChainConfigDir, chainID+".json")
if err := createFileAndWrite(pChainConfigPath, []byte(pChainConfigFile)); err != nil {
return nil, fmt.Errorf("couldn't write file at %q: %w", pChainConfigPath, err)
}
}
return flags, nil
+37 -9
View File
@@ -1,5 +1,5 @@
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// SPDX-License-Identifier: BSD-3-Clause
package local
@@ -130,13 +130,24 @@ const (
Jan1_2020 uint64 = 1577836800
)
// GenerateVestingSchedule creates an unlock schedule with 1% per year for 100 years
// starting from Jan 1, 2020. This means ~5-6% is already unlocked as of Dec 2025.
// ImmediateUnlockLUX is 5% of 1B = 50M LUX for immediate spending (fees, transactions)
const ImmediateUnlockLUX uint64 = OneBillionLUX * 5 / 100
// GenerateVestingSchedule creates an unlock schedule with:
// - 5% immediately available (locktime=0) for transaction fees
// - 1% per year for 95 years starting from Jan 1, 2020
// This ensures the wallet has spendable funds for chain creation and other operations.
func GenerateVestingSchedule() []map[string]interface{} {
schedule := make([]map[string]interface{}, 100)
for year := 0; year < 100; year++ {
// First entry: immediately available funds (locktime=0)
schedule := make([]map[string]interface{}, 96) // 1 immediate + 95 vested
schedule[0] = map[string]interface{}{
"amount": ImmediateUnlockLUX,
"locktime": uint64(0), // Immediately available
}
// Remaining 95% vests 1% per year starting from 2020
for year := 0; year < 95; year++ {
unlockTime := Jan1_2020 + (uint64(year) * SecondsPerYear)
schedule[year] = map[string]interface{}{
schedule[year+1] = map[string]interface{}{
"amount": OnePercentLUX,
"locktime": unlockTime,
}
@@ -145,8 +156,9 @@ func GenerateVestingSchedule() []map[string]interface{} {
}
// GenerateAllocationsFromKeys creates genesis allocations for loaded keys with vesting
// The first key gets immediately spendable funds (locktime=0) for transaction fees.
// Other keys get vesting schedules starting from Jan 1, 2020.
func GenerateAllocationsFromKeys(keys []KeyInfo, hrp string) ([]map[string]interface{}, error) {
vestingSchedule := GenerateVestingSchedule()
allocations := make([]map[string]interface{}, len(keys))
for i, key := range keys {
@@ -154,11 +166,27 @@ func GenerateAllocationsFromKeys(keys []KeyInfo, hrp string) ([]map[string]inter
if err != nil {
return nil, fmt.Errorf("failed to format address for key %d: %w", i, err)
}
var unlockSchedule []map[string]interface{}
if i == 0 {
// First key: all funds immediately available (locktime=0) for transactions
// This matches how mainnet treasury works
unlockSchedule = []map[string]interface{}{
{
"amount": OneBillionLUX,
"locktime": uint64(0),
},
}
} else {
// Other keys: use vesting schedule
unlockSchedule = GenerateVestingSchedule()
}
allocations[i] = map[string]interface{}{
"ethAddr": key.EthAddr,
"luxAddr": luxAddr,
"initialAmount": OneBillionLUX, // 1B LUX on P-chain
"unlockSchedule": vestingSchedule,
"initialAmount": uint64(0), // initialAmount is NOT immediately spendable
"unlockSchedule": unlockSchedule,
}
}
return allocations, nil
+207
View File
@@ -0,0 +1,207 @@
//go:build mainnet
// +build mainnet
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
// SPDX-License-Identifier: BSD-3-Clause
package local_test
import (
"context"
"encoding/json"
"testing"
"time"
"github.com/luxfi/genesis/configs"
"github.com/luxfi/netrunner/local"
luxlog "github.com/luxfi/log"
"github.com/luxfi/log/level"
"github.com/stretchr/testify/require"
)
const (
luxdBinaryPath = "/Users/z/go/bin/luxd"
healthyTimeout = 3 * time.Minute
mainnetNetworkID = uint32(96369)
)
func TestMainnetConfigGeneration(t *testing.T) {
// Test that we can generate a valid mainnet configuration
netConfig, err := local.NewMainnetConfig(luxdBinaryPath, 5)
require.NoError(t, err, "Failed to create mainnet config")
// Verify network ID in genesis
var genesis map[string]interface{}
err = json.Unmarshal([]byte(netConfig.Genesis), &genesis)
require.NoError(t, err, "Failed to parse genesis JSON")
// Network ID should be mainnet (96369)
networkIDFloat, ok := genesis["networkID"].(float64)
require.True(t, ok, "networkID should be a number")
require.Equal(t, mainnetNetworkID, uint32(networkIDFloat), "Expected mainnet network ID")
// Should have 5 node configs
require.Len(t, netConfig.NodeConfigs, 5, "Expected 5 node configs")
// Each node should have staking keys configured
for i, nodeConfig := range netConfig.NodeConfigs {
require.NotEmpty(t, nodeConfig.StakingKey, "Node %d should have staking key", i)
require.NotEmpty(t, nodeConfig.StakingCert, "Node %d should have staking cert", i)
require.NotEmpty(t, nodeConfig.StakingSigningKey, "Node %d should have BLS signing key", i)
}
// Should have initial stakers in genesis
initialStakers, ok := genesis["initialStakers"].([]interface{})
require.True(t, ok, "Genesis should have initialStakers")
require.Len(t, initialStakers, 5, "Should have 5 initial stakers")
// Verify each staker has required fields
for i, staker := range initialStakers {
stakerMap, ok := staker.(map[string]interface{})
require.True(t, ok, "Staker %d should be a map", i)
require.NotEmpty(t, stakerMap["nodeID"], "Staker %d should have nodeID", i)
require.NotEmpty(t, stakerMap["rewardAddress"], "Staker %d should have rewardAddress", i)
require.NotNil(t, stakerMap["delegationFee"], "Staker %d should have delegationFee", i)
require.NotNil(t, stakerMap["signer"], "Staker %d should have signer", i)
}
t.Logf("Successfully generated mainnet config with %d nodes", len(netConfig.NodeConfigs))
}
func TestMainnetGenesisFromConfigs(t *testing.T) {
// Test loading genesis from configs package
genesisData, err := configs.GetGenesis(mainnetNetworkID)
require.NoError(t, err, "Failed to load mainnet genesis")
require.NotEmpty(t, genesisData, "Genesis data should not be empty")
var genesis map[string]interface{}
err = json.Unmarshal(genesisData, &genesis)
require.NoError(t, err, "Failed to parse genesis JSON")
// Verify network ID
networkIDFloat, ok := genesis["networkID"].(float64)
require.True(t, ok, "networkID should be a number")
require.Equal(t, mainnetNetworkID, uint32(networkIDFloat), "Expected mainnet network ID")
// Verify C-chain genesis
cChainGenesis, ok := genesis["cChainGenesis"].(string)
require.True(t, ok, "cChainGenesis should be a string")
require.NotEmpty(t, cChainGenesis, "cChainGenesis should not be empty")
var cChain map[string]interface{}
err = json.Unmarshal([]byte(cChainGenesis), &cChain)
require.NoError(t, err, "Failed to parse cChainGenesis")
// Verify C-chain chainId matches network ID
configObj, ok := cChain["config"].(map[string]interface{})
require.True(t, ok, "cChain should have config")
chainIDFloat, ok := configObj["chainId"].(float64)
require.True(t, ok, "config should have chainId")
require.Equal(t, mainnetNetworkID, uint32(chainIDFloat), "C-chain chainId should match network ID")
t.Logf("Mainnet genesis loaded successfully with network ID %d", uint32(networkIDFloat))
}
func TestMainnetFiveNodeNetwork(t *testing.T) {
if testing.Short() {
t.Skip("skipping integration test in short mode")
}
// Create logger
logFactory := luxlog.NewFactoryWithConfig(luxlog.Config{
DisplayLevel: level.Info,
LogLevel: level.Debug,
})
log, err := logFactory.Make("test")
require.NoError(t, err, "Failed to create logger")
// Create mainnet config for 5 nodes
netConfig, err := local.NewMainnetConfig(luxdBinaryPath, 5)
require.NoError(t, err, "Failed to create mainnet config")
// Create network
nw, err := local.NewNetwork(log, netConfig, "", "", true)
require.NoError(t, err, "Failed to create network")
defer func() {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
_ = nw.Stop(ctx)
}()
// Wait for network to be healthy
ctx, cancel := context.WithTimeout(context.Background(), healthyTimeout)
defer cancel()
t.Log("Waiting for network to become healthy...")
err = nw.Healthy(ctx)
require.NoError(t, err, "Network failed to become healthy")
// Get network info
nodes, err := nw.GetAllNodes()
require.NoError(t, err, "Failed to get nodes")
require.Len(t, nodes, 5, "Should have 5 nodes")
t.Logf("Network healthy with %d nodes", len(nodes))
// Log node URIs
for name, node := range nodes {
t.Logf("Node %s: %s", name, node.GetURL())
}
}
func TestMainnetConsensusValidation(t *testing.T) {
if testing.Short() {
t.Skip("skipping integration test in short mode")
}
// Create logger
logFactory := luxlog.NewFactoryWithConfig(luxlog.Config{
DisplayLevel: level.Info,
LogLevel: level.Debug,
})
log, err := logFactory.Make("test")
require.NoError(t, err, "Failed to create logger")
// Create mainnet config for 5 nodes
netConfig, err := local.NewMainnetConfig(luxdBinaryPath, 5)
require.NoError(t, err, "Failed to create mainnet config")
// Create network
nw, err := local.NewNetwork(log, netConfig, "", "", true)
require.NoError(t, err, "Failed to create network")
defer func() {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
_ = nw.Stop(ctx)
}()
// Wait for network to be healthy
ctx, cancel := context.WithTimeout(context.Background(), healthyTimeout)
defer cancel()
t.Log("Waiting for network to become healthy...")
err = nw.Healthy(ctx)
require.NoError(t, err, "Network failed to become healthy")
// Verify each node is running with correct network ID
nodes, err := nw.GetAllNodes()
require.NoError(t, err, "Failed to get nodes")
for name, node := range nodes {
// Get info from node
client := node.GetAPIClient()
require.NotNil(t, client, "Node %s should have API client", name)
info := client.InfoAPI()
require.NotNil(t, info, "Node %s should have Info API", name)
// Get network ID from node
nodeNetworkID, err := info.GetNetworkID(ctx)
require.NoError(t, err, "Failed to get network ID from node %s", name)
require.Equal(t, mainnetNetworkID, nodeNetworkID, "Node %s should have mainnet network ID", name)
t.Logf("Node %s: Network ID = %d", name, nodeNetworkID)
}
t.Log("All nodes validated with correct mainnet configuration")
}
+29 -29
View File
@@ -69,8 +69,8 @@ var (
config.BootstrapIPsKey: {},
config.BootstrapIDsKey: {},
}
chainConfigSubDir = "chainConfigs"
subnetConfigSubDir = "subnetConfigs"
chainConfigSubDir = "chainConfigs"
pChainConfigSubDir = "pChainConfigs"
snapshotsRelPath = filepath.Join(".netrunner", "snapshots")
@@ -112,12 +112,12 @@ type localNetwork struct {
chainConfigFiles map[string]string
// upgrade config files to use per default
upgradeConfigFiles map[string]string
// subnet config files to use per default
subnetConfigFiles map[string]string
// P-chain config files to use per default
pChainConfigFiles map[string]string
// if true, for ports given in conf that are already taken, assign new random ones
reassignPortsIfUsed bool
// map from subnet id to elastic subnet tx id
subnetID2ElasticSubnetID map[ids.ID]ids.ID
// map from chain id to elastic chain tx id
chainID2ElasticChainID map[ids.ID]ids.ID
}
type deprecatedFlagEsp struct {
@@ -215,7 +215,7 @@ func init() {
"C": string(cChainConfig),
},
UpgradeConfigFiles: map[string]string{},
SubnetConfigFiles: map[string]string{},
PChainConfigFiles: map[string]string{},
}
for i := 0; i < len(defaultNetworkConfig.NodeConfigs); i++ {
@@ -330,7 +330,7 @@ func newNetwork(
rootDir: rootDir,
snapshotsDir: snapshotsDir,
reassignPortsIfUsed: reassignPortsIfUsed,
subnetID2ElasticSubnetID: map[ids.ID]ids.ID{},
chainID2ElasticChainID: map[ids.ID]ids.ID{},
}
return net, nil
}
@@ -442,9 +442,9 @@ func (ln *localNetwork) loadConfig(ctx context.Context, networkConfig network.Co
if ln.upgradeConfigFiles == nil {
ln.upgradeConfigFiles = map[string]string{}
}
ln.subnetConfigFiles = networkConfig.SubnetConfigFiles
if ln.subnetConfigFiles == nil {
ln.subnetConfigFiles = map[string]string{}
ln.chainConfigFiles = networkConfig.ChainConfigFiles
if ln.chainConfigFiles == nil {
ln.chainConfigFiles = map[string]string{}
}
// Sort node configs so beacons start first
@@ -496,8 +496,8 @@ func (ln *localNetwork) addNode(nodeConfig node.Config) (node.Node, error) {
if nodeConfig.UpgradeConfigFiles == nil {
nodeConfig.UpgradeConfigFiles = map[string]string{}
}
if nodeConfig.SubnetConfigFiles == nil {
nodeConfig.SubnetConfigFiles = map[string]string{}
if nodeConfig.ChainConfigFiles == nil {
nodeConfig.ChainConfigFiles = map[string]string{}
}
// load node defaults
@@ -516,10 +516,10 @@ func (ln *localNetwork) addNode(nodeConfig node.Config) (node.Node, error) {
nodeConfig.UpgradeConfigFiles[k] = v
}
}
for k, v := range ln.subnetConfigFiles {
_, ok := nodeConfig.SubnetConfigFiles[k]
for k, v := range ln.chainConfigFiles {
_, ok := nodeConfig.ChainConfigFiles[k]
if !ok {
nodeConfig.SubnetConfigFiles[k] = v
nodeConfig.ChainConfigFiles[k] = v
}
}
addNetworkFlags(ln.flags, nodeConfig.Flags)
@@ -688,7 +688,7 @@ func (ln *localNetwork) healthy(ctx context.Context) error {
if healthClient == nil {
return fmt.Errorf("health client is nil for node %v", nodeName)
}
health, err := (*healthClient).Health(ctx, nil)
health, err := healthClient.Health(ctx, nil)
if err == nil && health.Healthy {
ln.log.Debug("node became healthy", zap.String("name", nodeName))
return nil
@@ -893,16 +893,16 @@ func (ln *localNetwork) resumeNode(
}
// Restart [nodeName] using the same config, optionally changing [binaryPath],
// [pluginDir], [trackSubnets], [chainConfigs], [upgradeConfigs], [subnetConfigs]
// [pluginDir], [trackChains], [chainConfigs], [upgradeConfigs], [pChainConfigs]
func (ln *localNetwork) RestartNode(
ctx context.Context,
nodeName string,
binaryPath string,
pluginDir string,
trackSubnets string,
trackChains string,
chainConfigs map[string]string,
upgradeConfigs map[string]string,
subnetConfigs map[string]string,
pChainConfigs map[string]string,
) error {
ln.lock.Lock()
defer ln.lock.Unlock()
@@ -912,10 +912,10 @@ func (ln *localNetwork) RestartNode(
nodeName,
binaryPath,
pluginDir,
trackSubnets,
trackChains,
chainConfigs,
upgradeConfigs,
subnetConfigs,
pChainConfigs,
)
}
@@ -924,10 +924,10 @@ func (ln *localNetwork) restartNode(
nodeName string,
binaryPath string,
pluginDir string,
trackSubnets string,
trackChains string,
chainConfigs map[string]string,
upgradeConfigs map[string]string,
subnetConfigs map[string]string,
pChainConfigs map[string]string,
) error {
node, ok := ln.nodes[nodeName]
if !ok {
@@ -943,8 +943,8 @@ func (ln *localNetwork) restartNode(
nodeConfig.Flags[config.PluginDirKey] = pluginDir
}
if trackSubnets != "" {
nodeConfig.Flags[config.TrackNetsKey] = trackSubnets
if trackChains != "" {
nodeConfig.Flags[config.TrackNetsKey] = trackChains
}
// keep same ports, dbdir in node flags
@@ -961,9 +961,9 @@ func (ln *localNetwork) restartNode(
for k, v := range upgradeConfigs {
nodeConfig.UpgradeConfigFiles[k] = v
}
// apply subnet configs
for k, v := range subnetConfigs {
nodeConfig.SubnetConfigFiles[k] = v
// apply chain configs
for k, v := range chainConfigs {
nodeConfig.ChainConfigFiles[k] = v
}
if !node.paused {
+22 -22
View File
@@ -144,7 +144,7 @@ func TestNewNetworkEmpty(t *testing.T) {
networkConfig := testNetworkConfig(t)
networkConfig.NodeConfigs = nil
net, err := newNetwork(
logging.NoLog{},
luxlog.NoLog{},
newMockAPISuccessful,
&localTestProcessUndefNodeProcessCreator{},
"",
@@ -207,7 +207,7 @@ func TestNewNetworkOneNode(t *testing.T) {
networkConfig.NodeConfigs = networkConfig.NodeConfigs[:1]
creator := newLocalTestOneNodeCreator(require, networkConfig)
net, err := newNetwork(
logging.NoLog{},
luxlog.NoLog{},
newMockAPISuccessful,
creator,
"",
@@ -235,7 +235,7 @@ func TestNewNetworkFailToStartNode(t *testing.T) {
require := require.New(t)
networkConfig := testNetworkConfig(t)
net, err := newNetwork(
logging.NoLog{},
luxlog.NoLog{},
newMockAPISuccessful,
&localTestFailedStartProcessCreator{},
"",
@@ -477,7 +477,7 @@ func TestWrongNetworkConfigs(t *testing.T) {
require := require.New(t)
for name, tt := range tests {
t.Run(name, func(t *testing.T) {
net, err := newNetwork(logging.NoLog{}, newMockAPISuccessful, &localTestSuccessfulNodeProcessCreator{}, "", "", false)
net, err := newNetwork(luxlog.NoLog{}, newMockAPISuccessful, &localTestSuccessfulNodeProcessCreator{}, "", "", false)
require.NoError(err)
err = net.loadConfig(context.Background(), tt.config)
require.Error(err)
@@ -491,7 +491,7 @@ func TestUnhealthyNetwork(t *testing.T) {
t.Parallel()
require := require.New(t)
networkConfig := testNetworkConfig(t)
net, err := newNetwork(logging.NoLog{}, newMockAPIUnhealthy, &localTestSuccessfulNodeProcessCreator{}, "", "", false)
net, err := newNetwork(luxlog.NoLog{}, newMockAPIUnhealthy, &localTestSuccessfulNodeProcessCreator{}, "", "", false)
require.NoError(err)
err = net.loadConfig(context.Background(), networkConfig)
require.NoError(err)
@@ -506,7 +506,7 @@ func TestGeneratedNodesNames(t *testing.T) {
for i := range networkConfig.NodeConfigs {
networkConfig.NodeConfigs[i].Name = ""
}
net, err := newNetwork(logging.NoLog{}, newMockAPISuccessful, &localTestSuccessfulNodeProcessCreator{}, "", "", false)
net, err := newNetwork(luxlog.NoLog{}, newMockAPISuccessful, &localTestSuccessfulNodeProcessCreator{}, "", "", false)
require.NoError(err)
err = net.loadConfig(context.Background(), networkConfig)
require.NoError(err)
@@ -526,7 +526,7 @@ func TestGenerateDefaultNetwork(t *testing.T) {
require := require.New(t)
binaryPath := "pepito"
networkConfig := NewDefaultConfig(binaryPath)
net, err := newNetwork(logging.NoLog{}, newMockAPISuccessful, &localTestSuccessfulNodeProcessCreator{}, "", "", false)
net, err := newNetwork(luxlog.NoLog{}, newMockAPISuccessful, &localTestSuccessfulNodeProcessCreator{}, "", "", false)
require.NoError(err)
err = net.loadConfig(context.Background(), networkConfig)
require.NoError(err)
@@ -576,7 +576,7 @@ func TestNetworkFromConfig(t *testing.T) {
t.Parallel()
require := require.New(t)
networkConfig := testNetworkConfig(t)
net, err := newNetwork(logging.NoLog{}, newMockAPISuccessful, &localTestSuccessfulNodeProcessCreator{}, "", "", false)
net, err := newNetwork(luxlog.NoLog{}, newMockAPISuccessful, &localTestSuccessfulNodeProcessCreator{}, "", "", false)
require.NoError(err)
err = net.loadConfig(context.Background(), networkConfig)
require.NoError(err)
@@ -600,7 +600,7 @@ func TestNetworkNodeOps(t *testing.T) {
// Start a new, empty network
emptyNetworkConfig, err := emptyNetworkConfig()
require.NoError(err)
net, err := newNetwork(logging.NoLog{}, newMockAPISuccessful, &localTestSuccessfulNodeProcessCreator{}, "", "", false)
net, err := newNetwork(luxlog.NoLog{}, newMockAPISuccessful, &localTestSuccessfulNodeProcessCreator{}, "", "", false)
require.NoError(err)
err = net.loadConfig(context.Background(), emptyNetworkConfig)
require.NoError(err)
@@ -638,7 +638,7 @@ func TestNodeNotFound(t *testing.T) {
emptyNetworkConfig, err := emptyNetworkConfig()
require.NoError(err)
networkConfig := testNetworkConfig(t)
net, err := newNetwork(logging.NoLog{}, newMockAPISuccessful, &localTestSuccessfulNodeProcessCreator{}, "", "", false)
net, err := newNetwork(luxlog.NoLog{}, newMockAPISuccessful, &localTestSuccessfulNodeProcessCreator{}, "", "", false)
require.NoError(err)
err = net.loadConfig(context.Background(), emptyNetworkConfig)
require.NoError(err)
@@ -671,7 +671,7 @@ func TestStoppedNetwork(t *testing.T) {
emptyNetworkConfig, err := emptyNetworkConfig()
require.NoError(err)
networkConfig := testNetworkConfig(t)
net, err := newNetwork(logging.NoLog{}, newMockAPISuccessful, &localTestSuccessfulNodeProcessCreator{}, "", "", false)
net, err := newNetwork(luxlog.NoLog{}, newMockAPISuccessful, &localTestSuccessfulNodeProcessCreator{}, "", "", false)
require.NoError(err)
err = net.loadConfig(context.Background(), emptyNetworkConfig)
require.NoError(err)
@@ -704,7 +704,7 @@ func TestStoppedNetwork(t *testing.T) {
func TestGetAllNodes(t *testing.T) {
require := require.New(t)
networkConfig := testNetworkConfig(t)
net, err := newNetwork(logging.NoLog{}, newMockAPISuccessful, &localTestSuccessfulNodeProcessCreator{}, "", "", false)
net, err := newNetwork(luxlog.NoLog{}, newMockAPISuccessful, &localTestSuccessfulNodeProcessCreator{}, "", "", false)
require.NoError(err)
err = net.loadConfig(context.Background(), networkConfig)
require.NoError(err)
@@ -746,7 +746,7 @@ func TestFlags(t *testing.T) {
}
nw, err := newNetwork(
logging.NoLog{},
luxlog.NoLog{},
newMockAPISuccessful,
&localTestFlagCheckProcessCreator{
// after creating the network, one flag should have been overridden by the node configs
@@ -775,7 +775,7 @@ func TestFlags(t *testing.T) {
v.Flags = flags
}
nw, err = newNetwork(
logging.NoLog{},
luxlog.NoLog{},
newMockAPISuccessful,
&localTestFlagCheckProcessCreator{
// after creating the network, only node configs should exist
@@ -803,7 +803,7 @@ func TestFlags(t *testing.T) {
v.Flags = nil
}
nw, err = newNetwork(
logging.NoLog{},
luxlog.NoLog{},
newMockAPISuccessful,
&localTestFlagCheckProcessCreator{
// after creating the network, only flags from the network config should exist
@@ -845,7 +845,7 @@ func TestChildCmdRedirection(t *testing.T) {
writtenCh: make(chan struct{}),
}
npc := &nodeProcessCreator{
log: logging.NoLog{},
log: luxlog.NoLog{},
stdout: buf,
stderr: buf,
colorPicker: utils.NewColorPicker(),
@@ -895,7 +895,7 @@ func TestChildCmdRedirection(t *testing.T) {
// and it should have a specific length:
// the actual output + the color terminal escape sequence + node name + []<space> + color terminal reset escape sequence
expectedLen := len(expectedResult) + len(utils.NewColorPicker().NextColor()) + len(mockNodeName) + 3 + len(logging.Reset)
expectedLen := len(expectedResult) + len(utils.NewColorPicker().NextColor()) + len(mockNodeName) + 3 + len(luxlog.Reset)
if len(result) != expectedLen {
t.Fatalf("expected string length to be %d, but it was %d", expectedLen, len(result))
}
@@ -923,9 +923,9 @@ func checkNetwork(t *testing.T, net network.Network, runningNodes map[string]str
// Return a network config that has no nodes
func emptyNetworkConfig() (network.Config, error) {
networkID := uint32(1337)
networkID := uint32(12345) // Use a custom test network ID (not mainnet/testnet/local)
// Use a dummy genesis
genesis, err := network.NewLuxGenesis(
genesis, err := network.NewGenesis(
networkID,
[]network.AddrAndBalance{
{
@@ -1158,7 +1158,7 @@ func TestWriteFiles(t *testing.T) {
genesisPath := filepath.Join(tmpDir, genesisFileName)
configFilePath := filepath.Join(tmpDir, configFileName)
chainConfigDir := filepath.Join(tmpDir, chainConfigSubDir)
subnetConfigDir := filepath.Join(tmpDir, subnetConfigSubDir)
subnetConfigDir := filepath.Join(tmpDir, pChainConfigSubDir)
cChainConfigPath := filepath.Join(tmpDir, chainConfigSubDir, "C", configFileName)
type test struct {
@@ -1273,7 +1273,7 @@ func TestRemoveBeacon(t *testing.T) {
// create a network with no nodes in it
emptyNetworkConfig, err := emptyNetworkConfig()
require.NoError(err)
net, err := newNetwork(logging.NoLog{}, newMockAPISuccessful, &localTestSuccessfulNodeProcessCreator{}, "", "", false)
net, err := newNetwork(luxlog.NoLog{}, newMockAPISuccessful, &localTestSuccessfulNodeProcessCreator{}, "", "", false)
require.NoError(err)
err = net.loadConfig(context.Background(), emptyNetworkConfig)
require.NoError(err)
@@ -1324,7 +1324,7 @@ func TestHealthyDuringNetworkStop(t *testing.T) {
require := require.New(t)
networkConfig := testNetworkConfig(t)
// Calls to a node's Healthy() function blocks until context cancelled
net, err := newNetwork(logging.NoLog{}, newMockAPIHealthyBlocks, &localTestSuccessfulNodeProcessCreator{}, "", "", false)
net, err := newNetwork(luxlog.NoLog{}, newMockAPIHealthyBlocks, &localTestSuccessfulNodeProcessCreator{}, "", "", false)
require.NoError(err)
err = net.loadConfig(context.Background(), networkConfig)
require.NoError(err)
+3
View File
@@ -1,3 +1,6 @@
//go:build integration
// +build integration
package local
import (
+24 -24
View File
@@ -24,26 +24,26 @@ import (
const (
deprecatedBuildDirKey = "build-dir"
deprecatedWhitelistedSubnetsKey = "whitelisted-subnets"
deprecatedWhitelistedChainsKey = "whitelisted-chains"
)
// NetworkState defines dynamic network information not available on blockchain db
// NetworkState defines dynamic network information not available on chain db
type NetworkState struct {
// Map from subnet id to elastic subnet tx id
SubnetID2ElasticSubnetID map[string]string `json:"subnetID2ElasticSubnetID"`
// Map from chain id to elastic chain tx id
ChainID2ElasticChainID map[string]string `json:"chainID2ElasticChainID"`
}
// snapshots generated using older ANR versions may contain deprecated luxd flags
func fixDeprecatedLuxdFlags(flags map[string]interface{}) error {
if vIntf, ok := flags[deprecatedWhitelistedSubnetsKey]; ok {
if vIntf, ok := flags[deprecatedWhitelistedChainsKey]; ok {
v, ok := vIntf.(string)
if !ok {
return fmt.Errorf("expected %q to be of type string but got %T", deprecatedWhitelistedSubnetsKey, vIntf)
return fmt.Errorf("expected %q to be of type string but got %T", deprecatedWhitelistedChainsKey, vIntf)
}
if v != "" {
flags[config.TrackNetsKey] = v
}
delete(flags, deprecatedWhitelistedSubnetsKey)
delete(flags, deprecatedWhitelistedChainsKey)
}
if vIntf, ok := flags[deprecatedBuildDirKey]; ok {
v, ok := vIntf.(string)
@@ -68,7 +68,7 @@ func NewNetworkFromSnapshot(
pluginDir string,
chainConfigs map[string]string,
upgradeConfigs map[string]string,
subnetConfigs map[string]string,
pChainConfigs map[string]string,
flags map[string]interface{},
reassignPortsIfUsed bool,
) (network.Network, error) {
@@ -95,7 +95,7 @@ func NewNetworkFromSnapshot(
pluginDir,
chainConfigs,
upgradeConfigs,
subnetConfigs,
pChainConfigs,
flags,
)
return net, err
@@ -182,7 +182,7 @@ func (ln *localNetwork) SaveSnapshot(ctx context.Context, snapshotName string) (
BinaryPath: ln.binaryPath,
ChainConfigFiles: ln.chainConfigFiles,
UpgradeConfigFiles: ln.upgradeConfigFiles,
SubnetConfigFiles: ln.subnetConfigFiles,
PChainConfigFiles: ln.pChainConfigFiles,
}
// no need to save this, will be generated automatically on snapshot load
@@ -195,12 +195,12 @@ func (ln *localNetwork) SaveSnapshot(ctx context.Context, snapshotName string) (
return "", err
}
// save dynamic part of network not available on blockchain
subnetID2ElasticSubnetID := map[string]string{}
for subnetID, elasticSubnetID := range ln.subnetID2ElasticSubnetID {
subnetID2ElasticSubnetID[subnetID.String()] = elasticSubnetID.String()
chainID2ElasticChainID := map[string]string{}
for chainID, elasticChainID := range ln.chainID2ElasticChainID {
chainID2ElasticChainID[chainID.String()] = elasticChainID.String()
}
networkState := NetworkState{
SubnetID2ElasticSubnetID: subnetID2ElasticSubnetID,
ChainID2ElasticChainID: chainID2ElasticChainID,
}
networkStateJSON, err := json.MarshalIndent(networkState, "", " ")
if err != nil {
@@ -220,7 +220,7 @@ func (ln *localNetwork) loadSnapshot(
pluginDir string,
chainConfigs map[string]string,
upgradeConfigs map[string]string,
subnetConfigs map[string]string,
pChainConfigs map[string]string,
flags map[string]interface{},
) error {
ln.lock.Lock()
@@ -289,8 +289,8 @@ func (ln *localNetwork) loadSnapshot(
if networkConfig.NodeConfigs[i].UpgradeConfigFiles == nil {
networkConfig.NodeConfigs[i].UpgradeConfigFiles = map[string]string{}
}
if networkConfig.NodeConfigs[i].SubnetConfigFiles == nil {
networkConfig.NodeConfigs[i].SubnetConfigFiles = map[string]string{}
if networkConfig.NodeConfigs[i].ChainConfigFiles == nil {
networkConfig.NodeConfigs[i].ChainConfigFiles = map[string]string{}
}
for k, v := range chainConfigs {
networkConfig.NodeConfigs[i].ChainConfigFiles[k] = v
@@ -298,8 +298,8 @@ func (ln *localNetwork) loadSnapshot(
for k, v := range upgradeConfigs {
networkConfig.NodeConfigs[i].UpgradeConfigFiles[k] = v
}
for k, v := range subnetConfigs {
networkConfig.NodeConfigs[i].SubnetConfigFiles[k] = v
for k, v := range chainConfigs {
networkConfig.NodeConfigs[i].ChainConfigFiles[k] = v
}
}
// load network state not available at blockchain db
@@ -314,17 +314,17 @@ func (ln *localNetwork) loadSnapshot(
if err := json.Unmarshal(networkStateJSON, &networkState); err != nil {
return fmt.Errorf("failure unmarshaling network state from snapshot: %w", err)
}
ln.subnetID2ElasticSubnetID = map[ids.ID]ids.ID{}
for subnetIDStr, elasticSubnetIDStr := range networkState.SubnetID2ElasticSubnetID {
subnetID, err := ids.FromString(subnetIDStr)
ln.chainID2ElasticChainID = map[ids.ID]ids.ID{}
for chainIDStr, elasticChainIDStr := range networkState.ChainID2ElasticChainID {
chainID, err := ids.FromString(chainIDStr)
if err != nil {
return err
}
elasticSubnetID, err := ids.FromString(elasticSubnetIDStr)
elasticChainID, err := ids.FromString(elasticChainIDStr)
if err != nil {
return err
}
ln.subnetID2ElasticSubnetID[subnetID] = elasticSubnetID
ln.chainID2ElasticChainID[chainID] = elasticChainID
}
}
return ln.loadConfig(ctx, networkConfig)
+1 -1
View File
@@ -1,5 +1,5 @@
// Copyright (C) 2019-2025, Lux Industries Inc All rights reserved.
// See the file LICENSE for licensing terms.
// SPDX-License-Identifier: BSD-3-Clause
package local
+1 -1
View File
@@ -1,5 +1,5 @@
// Copyright (C) 2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// SPDX-License-Identifier: BSD-3-Clause
package main
import (
+8 -8
View File
@@ -19,7 +19,7 @@ type NetworkType string
const (
NetworkTypePrimary NetworkType = "primary" // Mainnet or Testnet
NetworkTypeSubnet NetworkType = "subnet" // L1/L2 subnets
NetworkTypeChain NetworkType = "chain" // L1/L2 chains
)
// NetworkConfig defines configuration for a single network
@@ -27,7 +27,7 @@ type NetworkConfig struct {
NetworkID uint32 `json:"networkID"`
Name string `json:"name"`
Type NetworkType `json:"type"`
ParentID uint32 `json:"parentID,omitempty"` // For subnets
ParentID uint32 `json:"parentID,omitempty"` // For chains
HTTPPort int `json:"httpPort"`
StakingPort int `json:"stakingPort"`
DataDir string `json:"dataDir"`
@@ -39,7 +39,7 @@ type NetworkConfig struct {
type ChainConfig struct {
ChainID string `json:"chainID"`
VMID string `json:"vmID"`
SubnetID string `json:"subnetID,omitempty"`
PChainID string `json:"pChainID,omitempty"`
Genesis []byte `json:"genesis,omitempty"`
IsEVM bool `json:"isEVM"`
}
@@ -73,14 +73,14 @@ type NetworkInstance struct {
Config NetworkConfig
Network network.Network
Status NetworkStatus
Subnets map[string]*SubnetInstance
Chains map[string]*ChainInstance
StartTime time.Time
}
// SubnetInstance represents a running subnet
type SubnetInstance struct {
SubnetID string
// ChainInstance represents a running chain
type ChainInstance struct {
ChainID string
PChainID string
ParentNetID uint32
Validators []ids.NodeID
Status NetworkStatus
@@ -197,7 +197,7 @@ func (m *MultiNetworkManager) AddNetwork(config NetworkConfig) error {
instance := &NetworkInstance{
Config: config,
Status: NetworkStatusStopped,
Subnets: make(map[string]*SubnetInstance),
Chains: make(map[string]*ChainInstance),
}
m.networks[config.NetworkID] = instance
+37 -15
View File
@@ -8,11 +8,12 @@ import (
"strconv"
"time"
"github.com/luxfi/netrunner/network/node"
"github.com/luxfi/netrunner/utils"
"github.com/luxfi/constants"
"github.com/luxfi/genesis/pkg/genesis"
"github.com/luxfi/ids"
"github.com/luxfi/constants"
"github.com/luxfi/netrunner/network/node"
"github.com/luxfi/netrunner/utils"
"github.com/luxfi/node/utils/formatting/address"
"github.com/luxfi/units"
"golang.org/x/exp/maps"
)
@@ -72,8 +73,8 @@ type Config struct {
ChainConfigFiles map[string]string `json:"chainConfigFiles"`
// Upgrade config files to use per default, if not specified in node config
UpgradeConfigFiles map[string]string `json:"upgradeConfigFiles"`
// Subnet config files to use per default, if not specified in node config
SubnetConfigFiles map[string]string `json:"subnetConfigFiles"`
// P-Chain config files to use per default, if not specified in node config
PChainConfigFiles map[string]string `json:"pChainConfigFiles"`
}
// Validate returns an error if this config is invalid
@@ -108,13 +109,13 @@ func (c *Config) Validate() error {
return nil
}
// Return a genesis JSON where:
// NewGenesis returns a genesis JSON where:
// The nodes in [genesisVdrs] are validators.
// The C-Chain and X-Chain balances are given by
// [cChainBalances] and [xChainBalances].
// Note that many of the genesis fields (i.e. reward addresses)
// are randomly generated or hard-coded.
func NewLuxGenesis(
func NewGenesis(
networkID uint32,
xChainBalances []AddrAndBalance,
cChainBalances []AddrAndBalance,
@@ -133,12 +134,23 @@ func NewLuxGenesis(
// Address that controls stake doesn't matter -- generate it randomly
genesisVdrStakeShortID := ids.GenerateTestShortID()
// Get the HRP for address formatting (custom for test networks)
hrp := constants.GetHRP(networkID)
// Format addresses as strings for the JSON config
zeroEthAddr := fmt.Sprintf("0x%s", ids.ShortID{}.Hex())
genesisVdrStakeAddr, err := address.Format("P", hrp, genesisVdrStakeShortID[:])
if err != nil {
return nil, fmt.Errorf("failed to format genesis validator stake address: %w", err)
}
config := genesis.UnparsedConfig{
NetworkID: networkID,
Allocations: []genesis.UnparsedAllocation{
{
ETHAddr: ids.ShortID{}, // Zero address
LUXAddr: genesisVdrStakeShortID,
ETHAddr: zeroEthAddr, // Zero address as hex string
LUXAddr: genesisVdrStakeAddr, // Bech32 formatted address
InitialAmount: 0,
UnlockSchedule: []genesis.LockedAmount{ // Provides stake to validators
{
@@ -148,22 +160,27 @@ func NewLuxGenesis(
},
},
StartTime: uint64(time.Now().Unix()),
InitialStakedFunds: []ids.ShortID{genesisVdrStakeShortID},
InitialStakedFunds: []string{genesisVdrStakeAddr},
InitialStakeDuration: 31_536_000, // 1 year
InitialStakeDurationOffset: 5_400, // 90 minutes
Message: "hello world",
}
for _, xChainBal := range xChainBalances {
// Format the LUX address as bech32 string
luxAddr, err := address.Format("P", hrp, xChainBal.Addr[:])
if err != nil {
return nil, fmt.Errorf("failed to format X-chain balance address: %w", err)
}
config.Allocations = append(
config.Allocations,
genesis.UnparsedAllocation{
ETHAddr: ids.ShortID{}, // Zero address
LUXAddr: xChainBal.Addr,
ETHAddr: zeroEthAddr, // Zero address as hex string
LUXAddr: luxAddr, // Bech32 formatted address
InitialAmount: xChainBal.Balance.Uint64(),
UnlockSchedule: []genesis.LockedAmount{
{
Amount: 100 * units.MegaLux, // Unlocked P-Chain balance for subnet deployment
Amount: 100 * units.MegaLux, // Unlocked P-Chain balance for chain deployment
Locktime: 0, // Locktime 0 = immediately available
},
{
@@ -193,12 +210,17 @@ func NewLuxGenesis(
// Set initial validators.
// Give staking rewards to random address.
rewardShortID := ids.GenerateTestShortID()
rewardAddr, err := address.Format("P", hrp, rewardShortID[:])
if err != nil {
return nil, fmt.Errorf("failed to format reward address: %w", err)
}
for _, genesisVdr := range genesisVdrs {
config.InitialStakers = append(
config.InitialStakers,
genesis.UnparsedStaker{
NodeID: genesisVdr,
RewardAddress: rewardShortID,
NodeID: genesisVdr.String(), // NodeID as string
RewardAddress: rewardAddr, // Bech32 formatted address
DelegationFee: 10_000,
},
)
+32 -12
View File
@@ -1,10 +1,9 @@
{
"networkID": 1337,
"allocations": [
{
"ethAddr": "0xb3d82b1367d362de99ab59a658165aff520cbd4d",
"luxAddr": "X-custom1g65uqn6t77p656w64023nh8nd9updzmxwd59gh",
"initialAmount": 0,
"luxAddr": "X-custom1g65uqn6t77p656w64023nh8nd9updzmxwd59gh",
"unlockSchedule": [
{
"amount": 10000000000000000,
@@ -14,8 +13,8 @@
},
{
"ethAddr": "0xb3d82b1367d362de99ab59a658165aff520cbd4d",
"luxAddr": "X-custom18jma8ppw3nhx5r4ap8clazz0dps7rv5u9xde7p",
"initialAmount": 300000000000000000,
"luxAddr": "X-custom18jma8ppw3nhx5r4ap8clazz0dps7rv5u9xde7p",
"unlockSchedule": [
{
"amount": 20000000000000000
@@ -28,8 +27,8 @@
},
{
"ethAddr": "0xb3d82b1367d362de99ab59a658165aff520cbd4d",
"luxAddr": "X-custom16045mxr3s2cjycqe2xfluk304xv3ezhkhsvkpr",
"initialAmount": 10000000000000000,
"luxAddr": "X-custom16045mxr3s2cjycqe2xfluk304xv3ezhkhsvkpr",
"unlockSchedule": [
{
"amount": 10000000000000000,
@@ -38,7 +37,7 @@
]
}
],
"startTime": 1630987200,
"cChainGenesis": "{\"config\":{\"chainId\":43112,\"homesteadBlock\":0,\"daoForkBlock\":0,\"daoForkSupport\":true,\"eip150Block\":0,\"eip150Hash\":\"0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0\",\"eip155Block\":0,\"eip158Block\":0,\"byzantiumBlock\":0,\"constantinopleBlock\":0,\"petersburgBlock\":0,\"istanbulBlock\":0,\"muirGlacierBlock\":0,\"apricotPhase1BlockTimestamp\":0,\"apricotPhase2BlockTimestamp\":0},\"nonce\":\"0x0\",\"timestamp\":\"0x0\",\"extraData\":\"0x00\",\"gasLimit\":\"0x5f5e100\",\"difficulty\":\"0x0\",\"mixHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"coinbase\":\"0x0000000000000000000000000000000000000000\",\"alloc\":{\"8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC\":{\"balance\":\"0x295BE96E64066972000000\"}},\"number\":\"0x0\",\"gasUsed\":\"0x0\",\"parentHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\"}",
"initialStakeDuration": 31536000,
"initialStakeDurationOffset": 5400,
"initialStakedFunds": [
@@ -46,31 +45,52 @@
],
"initialStakers": [
{
"delegationFee": 1000000,
"nodeID": "NodeID-7Xhw2mDxuDS44j42TCB6U5579esbSt3Lg",
"rewardAddress": "X-custom18jma8ppw3nhx5r4ap8clazz0dps7rv5u9xde7p",
"delegationFee": 1000000
"signer": {
"proofOfPossession": "0xa2569a137e65d3a507c4f1f7ffac74ec05916ba44dfbbb84d42c2736a2bc1e8be14038d3aeeeac7c78e19ecdde69d830051959f22559641a3f9e42377d7f64580acdc383c5c9e22f7f1114712a543c6997d6dc59c88555423497d9fff41fa79a",
"publicKey": "0xb3ebbe748a1f06d19ee25d4e345ba8d6b5a426498a140c2519b518e3e6224abd7895075892f361acf24c10af968bc7de"
}
},
{
"delegationFee": 500000,
"nodeID": "NodeID-MFrZFVCXPv5iCn6M9K6XduxGTYp891xXZ",
"rewardAddress": "X-custom18jma8ppw3nhx5r4ap8clazz0dps7rv5u9xde7p",
"delegationFee": 500000
"signer": {
"proofOfPossession": "0xaf92674c3615d462527675da121b06023b116ddebc6e163919e562ab7cbe6adf20515cd2fc17c3e2d2d59953f285229e15f04302187bef5a4aa3ebdea1d18bf34047be654dd12491e882fb90b17b3cbde99ad43fc5cd0c26828bbe49a4b9456c",
"publicKey": "0x8b49c4259529a801cc961c72248773b550379ff718a49f4ccc0e1f2ac338fe204432329aa1712f408f97eee12b22dd05"
}
},
{
"delegationFee": 250000,
"nodeID": "NodeID-NFBbbJ4qCmNaCzeW7sxErhvWqvEQMnYcN",
"rewardAddress": "X-custom18jma8ppw3nhx5r4ap8clazz0dps7rv5u9xde7p",
"delegationFee": 250000
"signer": {
"proofOfPossession": "0xa113145564d7ac540fade2526938fbd33233957901111328c86c4cbe7ed25f678173d95c54b3342fd7b723c3ed813f2b04fecf4a317205ce65638bb34bb58bb6746e74c63e6cbe5cb25c103b290ed270146b756d3901c8b0f99d083a44572c79",
"publicKey": "0xb20ab07ea5cf8b77ab50f2071a6f1d2aab693c8bd89761430cd29de9fa0dbae83a32c7697d59ff1b06995b1596c16fa6"
}
},
{
"delegationFee": 125000,
"nodeID": "NodeID-GWPcbFJZFfZreETSoWjPimr846mXEKCtu",
"rewardAddress": "X-custom18jma8ppw3nhx5r4ap8clazz0dps7rv5u9xde7p",
"delegationFee": 125000
"signer": {
"proofOfPossession": "0x8880500af123806295355eadbb36084619de729e2c4ac057f73784eb806ae19b4738550617fa8d6820cf1a352e19af6d042e140b3c967b8573e063c1e5aabce50dc2156e9caa6428b58e06a3624ac8170793ff4b4095397c1c981fbb2383dae6",
"publicKey": "0xb43f74196c2b9e9980f815a99288ef76166b42e93663dcbce3526f9652cdb58b31a3d68798b08fb9806024620ca1d6cd"
}
},
{
"delegationFee": 62500,
"nodeID": "NodeID-P7oB2McjBGgW2NXXWVYjV8JEDFoW9xDE5",
"rewardAddress": "X-custom18jma8ppw3nhx5r4ap8clazz0dps7rv5u9xde7p",
"delegationFee": 62500
"signer": {
"proofOfPossession": "0x8e01fe13a6fa3ca5be4f78230b0d03ffff4741f186ddebf9a04819e8011eb9c1b81debad2e52a35ec89f4dfc517884b9075e46968d6c735f17cca3ab5c07a2cdc181233fcd8e9260d9fa7edc12572dff13972c9b3233f34258fb34d9e6b732ba",
"publicKey": "0xa37e938a8e1fb71286ae1a9dc81585bae5c63d8cae3b95160ce9acce621c8ee64afa0491a2c757ba24d8be2da1052090"
}
}
],
"cChainGenesis": "{\"config\":{\"chainId\":43112,\"homesteadBlock\":0,\"daoForkBlock\":0,\"daoForkSupport\":true,\"eip150Block\":0,\"eip150Hash\":\"0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0\",\"eip155Block\":0,\"eip158Block\":0,\"byzantiumBlock\":0,\"constantinopleBlock\":0,\"petersburgBlock\":0,\"istanbulBlock\":0,\"muirGlacierBlock\":0,\"apricotPhase1BlockTimestamp\":0,\"apricotPhase2BlockTimestamp\":0},\"nonce\":\"0x0\",\"timestamp\":\"0x0\",\"extraData\":\"0x00\",\"gasLimit\":\"0x5f5e100\",\"difficulty\":\"0x0\",\"mixHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"coinbase\":\"0x0000000000000000000000000000000000000000\",\"alloc\":{\"8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC\":{\"balance\":\"0x295BE96E64066972000000\"}},\"number\":\"0x0\",\"gasUsed\":\"0x0\",\"parentHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\"}",
"message": "{{ fun_quote }}"
"message": "{{ fun_quote }}",
"networkID": 1337,
"startTime": 1630987200
}
+25 -24
View File
@@ -16,7 +16,7 @@ var (
)
type PermissionlessValidatorSpec struct {
SubnetID string
ChainID string
AssetID string
NodeName string
StakedAmount uint64
@@ -24,8 +24,8 @@ type PermissionlessValidatorSpec struct {
StakeDuration time.Duration
}
type ElasticSubnetSpec struct {
SubnetID *string
type ElasticChainSpec struct {
ChainID *string
AssetName string
AssetSymbol string
InitialSupply uint64
@@ -42,25 +42,26 @@ type ElasticSubnetSpec struct {
UptimeRequirement uint32
}
type SubnetSpec struct {
type ParticipantsSpec struct {
Participants []string
SubnetConfig []byte
ChainConfig []byte
}
type RemoveSubnetValidatorSpec struct {
type RemoveChainValidatorSpec struct {
NodeNames []string
SubnetID string
ChainID string
}
type BlockchainSpec struct {
type ChainSpec struct {
VMName string
Genesis []byte
SubnetID *string
SubnetSpec *SubnetSpec
ChainID *string
ParticipantsSpec *ParticipantsSpec
ChainConfig []byte
NetworkUpgrade []byte
BlockchainAlias string
Alias string
PerNodeChainConfig map[string][]byte
BlockchainName string // Unique name for the blockchain (defaults to VMName if empty)
}
// Network is an abstraction of an Lux network
@@ -103,19 +104,19 @@ type Network interface {
// Get name of available snapshots
GetSnapshotNames() ([]string, error)
// Restart a given node using the same config, optionally changing binary path, plugin dir,
// track subnets, a map of chain configs, a map of upgrade configs, and
// a map of subnet configs
// track chains, a map of chain configs, a map of upgrade configs, and
// a map of chain configs
RestartNode(context.Context, string, string, string, string, map[string]string, map[string]string, map[string]string) error
// Create the specified blockchains
CreateBlockchains(context.Context, []BlockchainSpec) ([]ids.ID, error)
// Create the given numbers of subnets
CreateSubnets(context.Context, []SubnetSpec) ([]ids.ID, error)
// Transform subnet into elastic subnet
TransformSubnet(context.Context, []ElasticSubnetSpec) ([]ids.ID, []ids.ID, error)
// Add a validator into an elastic subnet
// Create the specified chains
CreateChains(context.Context, []ChainSpec) ([]ids.ID, error)
// Create chain participant groups (validators)
CreateParticipantGroups(context.Context, []ParticipantsSpec) ([]ids.ID, error)
// Transform chain into elastic chain
TransformChain(context.Context, []ElasticChainSpec) ([]ids.ID, []ids.ID, error)
// Add a validator into an elastic chain
AddPermissionlessValidators(context.Context, []PermissionlessValidatorSpec) error
// Remove a validator from a subnet
RemoveSubnetValidators(context.Context, []RemoveSubnetValidatorSpec) error
// Get the elastic subnet tx id for the given subnet id
GetElasticSubnetID(context.Context, ids.ID) (ids.ID, error)
// Remove a validator from a chain
RemoveChainValidators(context.Context, []RemoveChainValidatorSpec) error
// Get the elastic chain tx id for the given chain id
GetElasticChainID(context.Context, ids.ID) (ids.ID, error)
}
+2 -2
View File
@@ -80,8 +80,8 @@ type Config struct {
ChainConfigFiles map[string]string `json:"chainConfigFiles"`
// May be nil.
UpgradeConfigFiles map[string]string `json:"upgradeConfigFiles"`
// May be nil.
SubnetConfigFiles map[string]string `json:"subnetConfigFiles"`
// May be nil. P-Chain chain (formerly subnet) config files.
PChainConfigFiles map[string]string `json:"pChainConfigFiles"`
// Flags can hold additional flags for the node.
// It can be empty.
// The precedence of flags handling is:
+1 -1
View File
@@ -1,5 +1,5 @@
// Copyright (C) 2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// SPDX-License-Identifier: BSD-3-Clause
package orchestrator
+16 -7
View File
@@ -1,14 +1,19 @@
//go:build integration
// +build integration
// Copyright (C) 2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// SPDX-License-Identifier: BSD-3-Clause
package orchestrator_test
import (
"context"
"os/exec"
"testing"
"time"
"github.com/luxfi/netrunner/engines"
_ "github.com/luxfi/netrunner/engines/lux" // Register lux engine
"github.com/luxfi/netrunner/orchestrator"
"github.com/stretchr/testify/require"
)
@@ -17,13 +22,17 @@ func TestMultiEngineOrchestration(t *testing.T) {
if testing.Short() {
t.Skip("skipping integration test")
}
// Skip if luxd binary is not available
if _, err := exec.LookPath("luxd"); err != nil {
t.Skip("luxd binary not found, skipping integration test")
}
ctx := context.Background()
host := orchestrator.NewHost()
// Test starting Lux engine
t.Run("StartLuxEngine", func(t *testing.T) {
config := &engines.NodeConfig{
options := &orchestrator.EngineOptions{
NetworkID: 96369,
HTTPPort: 19630,
StakingPort: 19631,
@@ -35,7 +44,7 @@ func TestMultiEngineOrchestration(t *testing.T) {
},
}
err := host.StartEngine(ctx, "test-lux", engines.EngineLux, config)
err := host.StartEngine(ctx, "test-lux", string(engines.EngineLux), options)
require.NoError(t, err)
// Wait for health
@@ -55,23 +64,23 @@ func TestMultiEngineOrchestration(t *testing.T) {
// Test starting multiple engines
t.Run("MultipleEngines", func(t *testing.T) {
// Start Lux
luxConfig := &engines.NodeConfig{
luxOptions := &orchestrator.EngineOptions{
NetworkID: 96369,
HTTPPort: 19640,
StakingPort: 19641,
LogLevel: "info",
}
err := host.StartEngine(ctx, "lux-1", engines.EngineLux, luxConfig)
err := host.StartEngine(ctx, "lux-1", string(engines.EngineLux), luxOptions)
require.NoError(t, err)
// Start Lux
avaConfig := &engines.NodeConfig{
avaOptions := &orchestrator.EngineOptions{
NetworkID: 43114,
HTTPPort: 19650,
StakingPort: 19651,
LogLevel: "info",
}
err = host.StartEngine(ctx, "ava-1", engines.EngineLux, avaConfig)
err = host.StartEngine(ctx, "ava-1", string(engines.EngineLux), avaOptions)
require.NoError(t, err)
// Check both are running
+1 -1
View File
@@ -1,5 +1,5 @@
// Copyright (C) 2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// SPDX-License-Identifier: BSD-3-Clause
package orchestrator
+1 -1
View File
@@ -1,5 +1,5 @@
// Copyright (C) 2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// SPDX-License-Identifier: BSD-3-Clause
package orchestrator
+1 -1
View File
@@ -1,5 +1,5 @@
// Copyright (C) 2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// SPDX-License-Identifier: BSD-3-Clause
package orchestrator
+1 -1
View File
@@ -1,5 +1,5 @@
// Copyright (C) 2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// SPDX-License-Identifier: BSD-3-Clause
package orchestrator
+1070 -2427
View File
File diff suppressed because it is too large Load Diff
+509 -968
View File
File diff suppressed because it is too large Load Diff
+62 -62
View File
@@ -43,9 +43,9 @@ service ControlService {
};
}
rpc TransformElasticSubnets(TransformElasticSubnetsRequest) returns (TransformElasticSubnetsResponse) {
rpc TransformElasticChains(TransformElasticChainsRequest) returns (TransformElasticChainsResponse) {
option (google.api.http) = {
post: "/v1/control/transformelasticsubnets"
post: "/v1/control/transformelasticchains"
body: "*"
};
}
@@ -57,16 +57,16 @@ service ControlService {
};
}
rpc RemoveSubnetValidator(RemoveSubnetValidatorRequest) returns (RemoveSubnetValidatorResponse) {
rpc RemoveChainValidator(RemoveChainValidatorRequest) returns (RemoveChainValidatorResponse) {
option (google.api.http) = {
post: "/v1/control/removesubnetvalidator"
post: "/v1/control/removechainvalidator"
body: "*"
};
}
rpc CreateSubnets(CreateSubnetsRequest) returns (CreateSubnetsResponse) {
rpc CreateChains(CreateChainsRequest) returns (CreateChainsResponse) {
option (google.api.http) = {
post: "/v1/control/createsubnets"
post: "/v1/control/createchains"
body: "*"
};
}
@@ -191,7 +191,7 @@ service ControlService {
}
}
message SubnetParticipants {
message ChainParticipants {
repeated string node_names = 1;
}
@@ -209,18 +209,18 @@ message ClusterInfo {
bool custom_chains_healthy = 7;
// The map of blockchain IDs in "ids.ID" format to its blockchain information.
map<string, CustomChainInfo> custom_chains = 8;
map<string, SubnetInfo> subnets = 9;
map<string, ChainInfo> chains = 9;
}
message SubnetInfo {
// If Subnet is an Elastic Subnet
message ChainInfo {
// If Chain is an Elastic Chain
bool is_elastic = 1;
// TXID for the elastic subnet transform
string elastic_subnet_id = 2;
// TXID for the elastic chain transform
string elastic_chain_id = 2;
// node validators of subnet
SubnetParticipants subnet_participants = 3;
// node validators of chain
ChainParticipants chain_participants = 3;
}
message CustomChainInfo {
@@ -232,13 +232,13 @@ message CustomChainInfo {
// VM ID in "ids.ID" format.
string vm_id = 2;
// Create subnet transaction ID -- subnet ID.
// The subnet ID must be whitelisted by the lux node.
string subnet_id = 3;
// Create chain transaction ID -- P-Chain chain ID.
// The chain ID must be whitelisted by the lux node.
string pchain_id = 3;
// Create blockchain transaction ID -- blockchain ID>
// The blockchain ID is used for RPC endpoints.
string chain_id = 4;
string blockchain_id = 4;
}
message NodeInfo {
@@ -249,7 +249,7 @@ message NodeInfo {
string log_dir = 5;
string db_dir = 6;
string plugin_dir = 7;
string whitelisted_subnets = 8;
string whitelisted_chains = 8;
bytes config = 9;
bool paused = 10;
}
@@ -265,7 +265,7 @@ message ListOfAttachedPeerInfo {
message StartRequest {
string exec_path = 1;
optional uint32 num_nodes = 2;
optional string whitelisted_subnets = 3;
optional string whitelisted_chains = 3;
optional string global_node_config = 4;
// Used for both database and log files.
optional string root_data_dir = 5;
@@ -276,19 +276,19 @@ message StartRequest {
// The list of:
// - custom chain's VM name
// - genesis file path
// - (optional) subnet id to use.
// - (optional) chain id to use.
// - chain config file path
// - network upgrade file path
//
// subnet id must be always nil when using StartRequest, as the network is empty and has no preloaded
// subnet ids available.
// chain id must be always nil when using StartRequest, as the network is empty and has no preloaded
// chain ids available.
//
// The matching file with the name in "ids.ID" format must exist.
// e.g., ids.ToID(hashing.ComputeHash256("subnetevm")).String()
// e.g., subnet-cli create VMID subnetevm
// e.g., ids.ToID(hashing.ComputeHash256("chainevm")).String()
// e.g., chain-cli create VMID chainevm
//
// If this field is set to none (by default), the node/network-runner
// does not install the custom chain and does not create the subnet,
// does not install the custom chain and does not create the chain,
// even if the VM binary exists on the local plugins directory.
repeated BlockchainSpec blockchain_specs = 7;
@@ -310,10 +310,10 @@ message StartRequest {
// use dynamic ports instead of default ones
optional bool dynamic_ports = 12;
// Map of subnet id to subnet config file contents.
// If specified, will create a file "subnetid.json" under subnets config dir with
// Map of chain id to chain config file contents.
// If specified, will create a file "chainid.json" under chains config dir with
// the contents provided here.
map<string, string> subnet_configs = 13;
map<string, string> chain_config_files = 13;
}
message RPCVersionRequest {}
@@ -327,15 +327,15 @@ message StartResponse {
repeated string chain_ids = 2;
}
message SubnetSpec {
message ChainSpec {
// if empty, assumes all nodes should be participants
repeated string participants = 1;
// either file path or file contents
string subnet_config = 2;
string chain_config_file = 2;
}
message ElasticSubnetSpec {
string subnet_id = 1;
message ElasticChainSpec {
string chain_id = 1;
string asset_name = 2;
string asset_symbol = 3;
uint64 initial_supply = 4;
@@ -352,18 +352,18 @@ message ElasticSubnetSpec {
uint32 uptime_requirement = 15;
}
message TransformElasticSubnetsRequest {
repeated ElasticSubnetSpec elastic_subnet_spec = 1;
message TransformElasticChainsRequest {
repeated ElasticChainSpec elastic_chain_spec = 1;
}
message TransformElasticSubnetsResponse {
message TransformElasticChainsResponse {
ClusterInfo cluster_info = 1;
repeated string tx_ids = 2;
repeated string asset_ids = 3;
}
message PermissionlessValidatorSpec {
string subnet_id = 1;
string chain_id = 1;
string node_name = 2;
uint64 staked_token_amount = 3;
string asset_id = 4;
@@ -379,16 +379,16 @@ message AddPermissionlessValidatorResponse {
ClusterInfo cluster_info = 1;
}
message RemoveSubnetValidatorSpec {
string subnet_id = 1;
message RemoveChainValidatorSpec {
string chain_id = 1;
repeated string node_names = 2;
}
message RemoveSubnetValidatorRequest {
repeated RemoveSubnetValidatorSpec validator_spec = 1;
message RemoveChainValidatorRequest {
repeated RemoveChainValidatorSpec validator_spec = 1;
}
message RemoveSubnetValidatorResponse {
message RemoveChainValidatorResponse {
ClusterInfo cluster_info = 1;
}
@@ -396,10 +396,10 @@ message BlockchainSpec {
string vm_name = 1;
// either file path or file contents
string genesis = 2;
// either a subnet_id is given for a previously created subnet,
// or a subnet specification is given for a new subnet generation
optional string subnet_id = 3;
optional SubnetSpec subnet_spec = 4;
// either a chain_id is given for a previously created chain,
// or a chain specification is given for a new chain generation
optional string chain_id = 3;
optional ChainSpec chain_spec = 4;
// General chain config, either file path or file contents
string chain_config = 5;
// either file path or file contents
@@ -413,15 +413,15 @@ message CreateBlockchainsRequest {
// The list of:
// - custom chain's VM name
// - genesis file path
// - (optional) subnet id to use.
// - (optional) chain id to use.
// - chain config file path
// - network upgrade file path
// - subnet config file path
// - chain config file path
// - chain config file path for specific nodes
//
// The matching file with the name in "ids.ID" format must exist.
// e.g., ids.ToID(hashing.ComputeHash256("subnetevm")).String()
// e.g., subnet-cli create VMID subnetevm
// e.g., ids.ToID(hashing.ComputeHash256("chainevm")).String()
// e.g., chain-cli create VMID chainevm
//
// If this field is set to none (by default), the node/network-runner
// will return error
@@ -433,13 +433,13 @@ message CreateBlockchainsResponse {
repeated string chain_ids = 2;
}
message CreateSubnetsRequest {
repeated SubnetSpec subnet_specs = 1;
message CreateChainsRequest {
repeated ChainSpec chain_specs = 1;
}
message CreateSubnetsResponse {
message CreateChainsResponse {
ClusterInfo cluster_info = 1;
repeated string subnet_ids = 2;
repeated string chain_ids = 2;
}
message HealthRequest {}
@@ -480,7 +480,7 @@ message RestartNodeRequest {
// Optional fields are set to the previous values if empty.
optional string exec_path = 2;
optional string whitelisted_subnets = 3;
optional string whitelisted_chains = 3;
// Map of chain name to config file contents.
// If specified, will create a file "chainname/config.json" with
@@ -492,10 +492,10 @@ message RestartNodeRequest {
// the contents provided here.
map<string, string> upgrade_configs = 5;
// Map of subnet id to subnet config file contents.
// If specified, will create a file "subnetid.json" under subnets config dir with
// Map of chain id to chain config file contents.
// If specified, will create a file "chainid.json" under chains config dir with
// the contents provided here.
map<string, string> subnet_configs = 6;
map<string, string> chain_config_files = 6;
// Plugin dir from which to load all custom VM executables.
string plugin_dir = 7;
@@ -544,10 +544,10 @@ message AddNodeRequest {
// the contents provided here.
map<string, string> upgrade_configs = 5;
// Map of subnet id to subnet config file contents.
// If specified, will create a file "subnetid.json" under subnets config dir with
// Map of chain id to chain config file contents.
// If specified, will create a file "chainid.json" under chains config dir with
// the contents provided here.
map<string, string> subnet_configs = 6;
map<string, string> chain_config_files = 6;
// Plugin dir from which to load all custom VM executables.
string plugin_dir = 7;
@@ -600,7 +600,7 @@ message LoadSnapshotRequest {
map<string, string> upgrade_configs = 6;
optional string global_node_config = 7;
optional bool reassign_ports_if_used = 8;
map<string, string> subnet_configs = 9;
map<string, string> chain_config_files = 9;
}
message LoadSnapshotResponse {
+157 -134
View File
@@ -1,6 +1,6 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.3.0
// - protoc-gen-go-grpc v1.6.0
// - protoc (unknown)
// source: rpcpb/rpc.proto
@@ -15,8 +15,8 @@ import (
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.32.0 or later.
const _ = grpc.SupportPackageIsVersion7
// Requires gRPC-Go v1.64.0 or later.
const _ = grpc.SupportPackageIsVersion9
const (
PingService_Ping_FullMethodName = "/rpcpb.PingService/Ping"
@@ -38,8 +38,9 @@ func NewPingServiceClient(cc grpc.ClientConnInterface) PingServiceClient {
}
func (c *pingServiceClient) Ping(ctx context.Context, in *PingRequest, opts ...grpc.CallOption) (*PingResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(PingResponse)
err := c.cc.Invoke(ctx, PingService_Ping_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, PingService_Ping_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -48,20 +49,24 @@ func (c *pingServiceClient) Ping(ctx context.Context, in *PingRequest, opts ...g
// PingServiceServer is the server API for PingService service.
// All implementations must embed UnimplementedPingServiceServer
// for forward compatibility
// for forward compatibility.
type PingServiceServer interface {
Ping(context.Context, *PingRequest) (*PingResponse, error)
mustEmbedUnimplementedPingServiceServer()
}
// UnimplementedPingServiceServer must be embedded to have forward compatible implementations.
type UnimplementedPingServiceServer struct {
}
// UnimplementedPingServiceServer must be embedded to have
// forward compatible implementations.
//
// NOTE: this should be embedded by value instead of pointer to avoid a nil
// pointer dereference when methods are called.
type UnimplementedPingServiceServer struct{}
func (UnimplementedPingServiceServer) Ping(context.Context, *PingRequest) (*PingResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Ping not implemented")
return nil, status.Error(codes.Unimplemented, "method Ping not implemented")
}
func (UnimplementedPingServiceServer) mustEmbedUnimplementedPingServiceServer() {}
func (UnimplementedPingServiceServer) testEmbeddedByValue() {}
// UnsafePingServiceServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to PingServiceServer will
@@ -71,6 +76,13 @@ type UnsafePingServiceServer interface {
}
func RegisterPingServiceServer(s grpc.ServiceRegistrar, srv PingServiceServer) {
// If the following call panics, it indicates UnimplementedPingServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
t.testEmbeddedByValue()
}
s.RegisterService(&PingService_ServiceDesc, srv)
}
@@ -112,10 +124,10 @@ const (
ControlService_RPCVersion_FullMethodName = "/rpcpb.ControlService/RPCVersion"
ControlService_Start_FullMethodName = "/rpcpb.ControlService/Start"
ControlService_CreateBlockchains_FullMethodName = "/rpcpb.ControlService/CreateBlockchains"
ControlService_TransformElasticSubnets_FullMethodName = "/rpcpb.ControlService/TransformElasticSubnets"
ControlService_TransformElasticChains_FullMethodName = "/rpcpb.ControlService/TransformElasticChains"
ControlService_AddPermissionlessValidator_FullMethodName = "/rpcpb.ControlService/AddPermissionlessValidator"
ControlService_RemoveSubnetValidator_FullMethodName = "/rpcpb.ControlService/RemoveSubnetValidator"
ControlService_CreateSubnets_FullMethodName = "/rpcpb.ControlService/CreateSubnets"
ControlService_RemoveChainValidator_FullMethodName = "/rpcpb.ControlService/RemoveChainValidator"
ControlService_CreateChains_FullMethodName = "/rpcpb.ControlService/CreateChains"
ControlService_Health_FullMethodName = "/rpcpb.ControlService/Health"
ControlService_URIs_FullMethodName = "/rpcpb.ControlService/URIs"
ControlService_WaitForHealthy_FullMethodName = "/rpcpb.ControlService/WaitForHealthy"
@@ -142,15 +154,15 @@ type ControlServiceClient interface {
RPCVersion(ctx context.Context, in *RPCVersionRequest, opts ...grpc.CallOption) (*RPCVersionResponse, error)
Start(ctx context.Context, in *StartRequest, opts ...grpc.CallOption) (*StartResponse, error)
CreateBlockchains(ctx context.Context, in *CreateBlockchainsRequest, opts ...grpc.CallOption) (*CreateBlockchainsResponse, error)
TransformElasticSubnets(ctx context.Context, in *TransformElasticSubnetsRequest, opts ...grpc.CallOption) (*TransformElasticSubnetsResponse, error)
TransformElasticChains(ctx context.Context, in *TransformElasticChainsRequest, opts ...grpc.CallOption) (*TransformElasticChainsResponse, error)
AddPermissionlessValidator(ctx context.Context, in *AddPermissionlessValidatorRequest, opts ...grpc.CallOption) (*AddPermissionlessValidatorResponse, error)
RemoveSubnetValidator(ctx context.Context, in *RemoveSubnetValidatorRequest, opts ...grpc.CallOption) (*RemoveSubnetValidatorResponse, error)
CreateSubnets(ctx context.Context, in *CreateSubnetsRequest, opts ...grpc.CallOption) (*CreateSubnetsResponse, error)
RemoveChainValidator(ctx context.Context, in *RemoveChainValidatorRequest, opts ...grpc.CallOption) (*RemoveChainValidatorResponse, error)
CreateChains(ctx context.Context, in *CreateChainsRequest, opts ...grpc.CallOption) (*CreateChainsResponse, error)
Health(ctx context.Context, in *HealthRequest, opts ...grpc.CallOption) (*HealthResponse, error)
URIs(ctx context.Context, in *URIsRequest, opts ...grpc.CallOption) (*URIsResponse, error)
WaitForHealthy(ctx context.Context, in *WaitForHealthyRequest, opts ...grpc.CallOption) (*WaitForHealthyResponse, error)
Status(ctx context.Context, in *StatusRequest, opts ...grpc.CallOption) (*StatusResponse, error)
StreamStatus(ctx context.Context, in *StreamStatusRequest, opts ...grpc.CallOption) (ControlService_StreamStatusClient, error)
StreamStatus(ctx context.Context, in *StreamStatusRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[StreamStatusResponse], error)
RemoveNode(ctx context.Context, in *RemoveNodeRequest, opts ...grpc.CallOption) (*RemoveNodeResponse, error)
AddNode(ctx context.Context, in *AddNodeRequest, opts ...grpc.CallOption) (*AddNodeResponse, error)
RestartNode(ctx context.Context, in *RestartNodeRequest, opts ...grpc.CallOption) (*RestartNodeResponse, error)
@@ -174,8 +186,9 @@ func NewControlServiceClient(cc grpc.ClientConnInterface) ControlServiceClient {
}
func (c *controlServiceClient) RPCVersion(ctx context.Context, in *RPCVersionRequest, opts ...grpc.CallOption) (*RPCVersionResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(RPCVersionResponse)
err := c.cc.Invoke(ctx, ControlService_RPCVersion_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, ControlService_RPCVersion_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -183,8 +196,9 @@ func (c *controlServiceClient) RPCVersion(ctx context.Context, in *RPCVersionReq
}
func (c *controlServiceClient) Start(ctx context.Context, in *StartRequest, opts ...grpc.CallOption) (*StartResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(StartResponse)
err := c.cc.Invoke(ctx, ControlService_Start_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, ControlService_Start_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -192,17 +206,19 @@ func (c *controlServiceClient) Start(ctx context.Context, in *StartRequest, opts
}
func (c *controlServiceClient) CreateBlockchains(ctx context.Context, in *CreateBlockchainsRequest, opts ...grpc.CallOption) (*CreateBlockchainsResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(CreateBlockchainsResponse)
err := c.cc.Invoke(ctx, ControlService_CreateBlockchains_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, ControlService_CreateBlockchains_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *controlServiceClient) TransformElasticSubnets(ctx context.Context, in *TransformElasticSubnetsRequest, opts ...grpc.CallOption) (*TransformElasticSubnetsResponse, error) {
out := new(TransformElasticSubnetsResponse)
err := c.cc.Invoke(ctx, ControlService_TransformElasticSubnets_FullMethodName, in, out, opts...)
func (c *controlServiceClient) TransformElasticChains(ctx context.Context, in *TransformElasticChainsRequest, opts ...grpc.CallOption) (*TransformElasticChainsResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(TransformElasticChainsResponse)
err := c.cc.Invoke(ctx, ControlService_TransformElasticChains_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -210,26 +226,29 @@ func (c *controlServiceClient) TransformElasticSubnets(ctx context.Context, in *
}
func (c *controlServiceClient) AddPermissionlessValidator(ctx context.Context, in *AddPermissionlessValidatorRequest, opts ...grpc.CallOption) (*AddPermissionlessValidatorResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(AddPermissionlessValidatorResponse)
err := c.cc.Invoke(ctx, ControlService_AddPermissionlessValidator_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, ControlService_AddPermissionlessValidator_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *controlServiceClient) RemoveSubnetValidator(ctx context.Context, in *RemoveSubnetValidatorRequest, opts ...grpc.CallOption) (*RemoveSubnetValidatorResponse, error) {
out := new(RemoveSubnetValidatorResponse)
err := c.cc.Invoke(ctx, ControlService_RemoveSubnetValidator_FullMethodName, in, out, opts...)
func (c *controlServiceClient) RemoveChainValidator(ctx context.Context, in *RemoveChainValidatorRequest, opts ...grpc.CallOption) (*RemoveChainValidatorResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(RemoveChainValidatorResponse)
err := c.cc.Invoke(ctx, ControlService_RemoveChainValidator_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *controlServiceClient) CreateSubnets(ctx context.Context, in *CreateSubnetsRequest, opts ...grpc.CallOption) (*CreateSubnetsResponse, error) {
out := new(CreateSubnetsResponse)
err := c.cc.Invoke(ctx, ControlService_CreateSubnets_FullMethodName, in, out, opts...)
func (c *controlServiceClient) CreateChains(ctx context.Context, in *CreateChainsRequest, opts ...grpc.CallOption) (*CreateChainsResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(CreateChainsResponse)
err := c.cc.Invoke(ctx, ControlService_CreateChains_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -237,8 +256,9 @@ func (c *controlServiceClient) CreateSubnets(ctx context.Context, in *CreateSubn
}
func (c *controlServiceClient) Health(ctx context.Context, in *HealthRequest, opts ...grpc.CallOption) (*HealthResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(HealthResponse)
err := c.cc.Invoke(ctx, ControlService_Health_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, ControlService_Health_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -246,8 +266,9 @@ func (c *controlServiceClient) Health(ctx context.Context, in *HealthRequest, op
}
func (c *controlServiceClient) URIs(ctx context.Context, in *URIsRequest, opts ...grpc.CallOption) (*URIsResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(URIsResponse)
err := c.cc.Invoke(ctx, ControlService_URIs_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, ControlService_URIs_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -255,8 +276,9 @@ func (c *controlServiceClient) URIs(ctx context.Context, in *URIsRequest, opts .
}
func (c *controlServiceClient) WaitForHealthy(ctx context.Context, in *WaitForHealthyRequest, opts ...grpc.CallOption) (*WaitForHealthyResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(WaitForHealthyResponse)
err := c.cc.Invoke(ctx, ControlService_WaitForHealthy_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, ControlService_WaitForHealthy_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -264,20 +286,22 @@ func (c *controlServiceClient) WaitForHealthy(ctx context.Context, in *WaitForHe
}
func (c *controlServiceClient) Status(ctx context.Context, in *StatusRequest, opts ...grpc.CallOption) (*StatusResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(StatusResponse)
err := c.cc.Invoke(ctx, ControlService_Status_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, ControlService_Status_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *controlServiceClient) StreamStatus(ctx context.Context, in *StreamStatusRequest, opts ...grpc.CallOption) (ControlService_StreamStatusClient, error) {
stream, err := c.cc.NewStream(ctx, &ControlService_ServiceDesc.Streams[0], ControlService_StreamStatus_FullMethodName, opts...)
func (c *controlServiceClient) StreamStatus(ctx context.Context, in *StreamStatusRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[StreamStatusResponse], error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
stream, err := c.cc.NewStream(ctx, &ControlService_ServiceDesc.Streams[0], ControlService_StreamStatus_FullMethodName, cOpts...)
if err != nil {
return nil, err
}
x := &controlServiceStreamStatusClient{stream}
x := &grpc.GenericClientStream[StreamStatusRequest, StreamStatusResponse]{ClientStream: stream}
if err := x.ClientStream.SendMsg(in); err != nil {
return nil, err
}
@@ -287,26 +311,13 @@ func (c *controlServiceClient) StreamStatus(ctx context.Context, in *StreamStatu
return x, nil
}
type ControlService_StreamStatusClient interface {
Recv() (*StreamStatusResponse, error)
grpc.ClientStream
}
type controlServiceStreamStatusClient struct {
grpc.ClientStream
}
func (x *controlServiceStreamStatusClient) Recv() (*StreamStatusResponse, error) {
m := new(StreamStatusResponse)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
type ControlService_StreamStatusClient = grpc.ServerStreamingClient[StreamStatusResponse]
func (c *controlServiceClient) RemoveNode(ctx context.Context, in *RemoveNodeRequest, opts ...grpc.CallOption) (*RemoveNodeResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(RemoveNodeResponse)
err := c.cc.Invoke(ctx, ControlService_RemoveNode_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, ControlService_RemoveNode_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -314,8 +325,9 @@ func (c *controlServiceClient) RemoveNode(ctx context.Context, in *RemoveNodeReq
}
func (c *controlServiceClient) AddNode(ctx context.Context, in *AddNodeRequest, opts ...grpc.CallOption) (*AddNodeResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(AddNodeResponse)
err := c.cc.Invoke(ctx, ControlService_AddNode_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, ControlService_AddNode_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -323,8 +335,9 @@ func (c *controlServiceClient) AddNode(ctx context.Context, in *AddNodeRequest,
}
func (c *controlServiceClient) RestartNode(ctx context.Context, in *RestartNodeRequest, opts ...grpc.CallOption) (*RestartNodeResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(RestartNodeResponse)
err := c.cc.Invoke(ctx, ControlService_RestartNode_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, ControlService_RestartNode_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -332,8 +345,9 @@ func (c *controlServiceClient) RestartNode(ctx context.Context, in *RestartNodeR
}
func (c *controlServiceClient) PauseNode(ctx context.Context, in *PauseNodeRequest, opts ...grpc.CallOption) (*PauseNodeResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(PauseNodeResponse)
err := c.cc.Invoke(ctx, ControlService_PauseNode_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, ControlService_PauseNode_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -341,8 +355,9 @@ func (c *controlServiceClient) PauseNode(ctx context.Context, in *PauseNodeReque
}
func (c *controlServiceClient) ResumeNode(ctx context.Context, in *ResumeNodeRequest, opts ...grpc.CallOption) (*ResumeNodeResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ResumeNodeResponse)
err := c.cc.Invoke(ctx, ControlService_ResumeNode_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, ControlService_ResumeNode_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -350,8 +365,9 @@ func (c *controlServiceClient) ResumeNode(ctx context.Context, in *ResumeNodeReq
}
func (c *controlServiceClient) Stop(ctx context.Context, in *StopRequest, opts ...grpc.CallOption) (*StopResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(StopResponse)
err := c.cc.Invoke(ctx, ControlService_Stop_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, ControlService_Stop_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -359,8 +375,9 @@ func (c *controlServiceClient) Stop(ctx context.Context, in *StopRequest, opts .
}
func (c *controlServiceClient) AttachPeer(ctx context.Context, in *AttachPeerRequest, opts ...grpc.CallOption) (*AttachPeerResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(AttachPeerResponse)
err := c.cc.Invoke(ctx, ControlService_AttachPeer_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, ControlService_AttachPeer_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -368,8 +385,9 @@ func (c *controlServiceClient) AttachPeer(ctx context.Context, in *AttachPeerReq
}
func (c *controlServiceClient) SendOutboundMessage(ctx context.Context, in *SendOutboundMessageRequest, opts ...grpc.CallOption) (*SendOutboundMessageResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(SendOutboundMessageResponse)
err := c.cc.Invoke(ctx, ControlService_SendOutboundMessage_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, ControlService_SendOutboundMessage_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -377,8 +395,9 @@ func (c *controlServiceClient) SendOutboundMessage(ctx context.Context, in *Send
}
func (c *controlServiceClient) SaveSnapshot(ctx context.Context, in *SaveSnapshotRequest, opts ...grpc.CallOption) (*SaveSnapshotResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(SaveSnapshotResponse)
err := c.cc.Invoke(ctx, ControlService_SaveSnapshot_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, ControlService_SaveSnapshot_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -386,8 +405,9 @@ func (c *controlServiceClient) SaveSnapshot(ctx context.Context, in *SaveSnapsho
}
func (c *controlServiceClient) LoadSnapshot(ctx context.Context, in *LoadSnapshotRequest, opts ...grpc.CallOption) (*LoadSnapshotResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(LoadSnapshotResponse)
err := c.cc.Invoke(ctx, ControlService_LoadSnapshot_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, ControlService_LoadSnapshot_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -395,8 +415,9 @@ func (c *controlServiceClient) LoadSnapshot(ctx context.Context, in *LoadSnapsho
}
func (c *controlServiceClient) RemoveSnapshot(ctx context.Context, in *RemoveSnapshotRequest, opts ...grpc.CallOption) (*RemoveSnapshotResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(RemoveSnapshotResponse)
err := c.cc.Invoke(ctx, ControlService_RemoveSnapshot_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, ControlService_RemoveSnapshot_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -404,8 +425,9 @@ func (c *controlServiceClient) RemoveSnapshot(ctx context.Context, in *RemoveSna
}
func (c *controlServiceClient) GetSnapshotNames(ctx context.Context, in *GetSnapshotNamesRequest, opts ...grpc.CallOption) (*GetSnapshotNamesResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(GetSnapshotNamesResponse)
err := c.cc.Invoke(ctx, ControlService_GetSnapshotNames_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, ControlService_GetSnapshotNames_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -414,20 +436,20 @@ func (c *controlServiceClient) GetSnapshotNames(ctx context.Context, in *GetSnap
// ControlServiceServer is the server API for ControlService service.
// All implementations must embed UnimplementedControlServiceServer
// for forward compatibility
// for forward compatibility.
type ControlServiceServer interface {
RPCVersion(context.Context, *RPCVersionRequest) (*RPCVersionResponse, error)
Start(context.Context, *StartRequest) (*StartResponse, error)
CreateBlockchains(context.Context, *CreateBlockchainsRequest) (*CreateBlockchainsResponse, error)
TransformElasticSubnets(context.Context, *TransformElasticSubnetsRequest) (*TransformElasticSubnetsResponse, error)
TransformElasticChains(context.Context, *TransformElasticChainsRequest) (*TransformElasticChainsResponse, error)
AddPermissionlessValidator(context.Context, *AddPermissionlessValidatorRequest) (*AddPermissionlessValidatorResponse, error)
RemoveSubnetValidator(context.Context, *RemoveSubnetValidatorRequest) (*RemoveSubnetValidatorResponse, error)
CreateSubnets(context.Context, *CreateSubnetsRequest) (*CreateSubnetsResponse, error)
RemoveChainValidator(context.Context, *RemoveChainValidatorRequest) (*RemoveChainValidatorResponse, error)
CreateChains(context.Context, *CreateChainsRequest) (*CreateChainsResponse, error)
Health(context.Context, *HealthRequest) (*HealthResponse, error)
URIs(context.Context, *URIsRequest) (*URIsResponse, error)
WaitForHealthy(context.Context, *WaitForHealthyRequest) (*WaitForHealthyResponse, error)
Status(context.Context, *StatusRequest) (*StatusResponse, error)
StreamStatus(*StreamStatusRequest, ControlService_StreamStatusServer) error
StreamStatus(*StreamStatusRequest, grpc.ServerStreamingServer[StreamStatusResponse]) error
RemoveNode(context.Context, *RemoveNodeRequest) (*RemoveNodeResponse, error)
AddNode(context.Context, *AddNodeRequest) (*AddNodeResponse, error)
RestartNode(context.Context, *RestartNodeRequest) (*RestartNodeResponse, error)
@@ -443,83 +465,87 @@ type ControlServiceServer interface {
mustEmbedUnimplementedControlServiceServer()
}
// UnimplementedControlServiceServer must be embedded to have forward compatible implementations.
type UnimplementedControlServiceServer struct {
}
// UnimplementedControlServiceServer must be embedded to have
// forward compatible implementations.
//
// NOTE: this should be embedded by value instead of pointer to avoid a nil
// pointer dereference when methods are called.
type UnimplementedControlServiceServer struct{}
func (UnimplementedControlServiceServer) RPCVersion(context.Context, *RPCVersionRequest) (*RPCVersionResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method RPCVersion not implemented")
return nil, status.Error(codes.Unimplemented, "method RPCVersion not implemented")
}
func (UnimplementedControlServiceServer) Start(context.Context, *StartRequest) (*StartResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Start not implemented")
return nil, status.Error(codes.Unimplemented, "method Start not implemented")
}
func (UnimplementedControlServiceServer) CreateBlockchains(context.Context, *CreateBlockchainsRequest) (*CreateBlockchainsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreateBlockchains not implemented")
return nil, status.Error(codes.Unimplemented, "method CreateBlockchains not implemented")
}
func (UnimplementedControlServiceServer) TransformElasticSubnets(context.Context, *TransformElasticSubnetsRequest) (*TransformElasticSubnetsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method TransformElasticSubnets not implemented")
func (UnimplementedControlServiceServer) TransformElasticChains(context.Context, *TransformElasticChainsRequest) (*TransformElasticChainsResponse, error) {
return nil, status.Error(codes.Unimplemented, "method TransformElasticChains not implemented")
}
func (UnimplementedControlServiceServer) AddPermissionlessValidator(context.Context, *AddPermissionlessValidatorRequest) (*AddPermissionlessValidatorResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method AddPermissionlessValidator not implemented")
return nil, status.Error(codes.Unimplemented, "method AddPermissionlessValidator not implemented")
}
func (UnimplementedControlServiceServer) RemoveSubnetValidator(context.Context, *RemoveSubnetValidatorRequest) (*RemoveSubnetValidatorResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method RemoveSubnetValidator not implemented")
func (UnimplementedControlServiceServer) RemoveChainValidator(context.Context, *RemoveChainValidatorRequest) (*RemoveChainValidatorResponse, error) {
return nil, status.Error(codes.Unimplemented, "method RemoveChainValidator not implemented")
}
func (UnimplementedControlServiceServer) CreateSubnets(context.Context, *CreateSubnetsRequest) (*CreateSubnetsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreateSubnets not implemented")
func (UnimplementedControlServiceServer) CreateChains(context.Context, *CreateChainsRequest) (*CreateChainsResponse, error) {
return nil, status.Error(codes.Unimplemented, "method CreateChains not implemented")
}
func (UnimplementedControlServiceServer) Health(context.Context, *HealthRequest) (*HealthResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Health not implemented")
return nil, status.Error(codes.Unimplemented, "method Health not implemented")
}
func (UnimplementedControlServiceServer) URIs(context.Context, *URIsRequest) (*URIsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method URIs not implemented")
return nil, status.Error(codes.Unimplemented, "method URIs not implemented")
}
func (UnimplementedControlServiceServer) WaitForHealthy(context.Context, *WaitForHealthyRequest) (*WaitForHealthyResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method WaitForHealthy not implemented")
return nil, status.Error(codes.Unimplemented, "method WaitForHealthy not implemented")
}
func (UnimplementedControlServiceServer) Status(context.Context, *StatusRequest) (*StatusResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Status not implemented")
return nil, status.Error(codes.Unimplemented, "method Status not implemented")
}
func (UnimplementedControlServiceServer) StreamStatus(*StreamStatusRequest, ControlService_StreamStatusServer) error {
return status.Errorf(codes.Unimplemented, "method StreamStatus not implemented")
func (UnimplementedControlServiceServer) StreamStatus(*StreamStatusRequest, grpc.ServerStreamingServer[StreamStatusResponse]) error {
return status.Error(codes.Unimplemented, "method StreamStatus not implemented")
}
func (UnimplementedControlServiceServer) RemoveNode(context.Context, *RemoveNodeRequest) (*RemoveNodeResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method RemoveNode not implemented")
return nil, status.Error(codes.Unimplemented, "method RemoveNode not implemented")
}
func (UnimplementedControlServiceServer) AddNode(context.Context, *AddNodeRequest) (*AddNodeResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method AddNode not implemented")
return nil, status.Error(codes.Unimplemented, "method AddNode not implemented")
}
func (UnimplementedControlServiceServer) RestartNode(context.Context, *RestartNodeRequest) (*RestartNodeResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method RestartNode not implemented")
return nil, status.Error(codes.Unimplemented, "method RestartNode not implemented")
}
func (UnimplementedControlServiceServer) PauseNode(context.Context, *PauseNodeRequest) (*PauseNodeResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method PauseNode not implemented")
return nil, status.Error(codes.Unimplemented, "method PauseNode not implemented")
}
func (UnimplementedControlServiceServer) ResumeNode(context.Context, *ResumeNodeRequest) (*ResumeNodeResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ResumeNode not implemented")
return nil, status.Error(codes.Unimplemented, "method ResumeNode not implemented")
}
func (UnimplementedControlServiceServer) Stop(context.Context, *StopRequest) (*StopResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Stop not implemented")
return nil, status.Error(codes.Unimplemented, "method Stop not implemented")
}
func (UnimplementedControlServiceServer) AttachPeer(context.Context, *AttachPeerRequest) (*AttachPeerResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method AttachPeer not implemented")
return nil, status.Error(codes.Unimplemented, "method AttachPeer not implemented")
}
func (UnimplementedControlServiceServer) SendOutboundMessage(context.Context, *SendOutboundMessageRequest) (*SendOutboundMessageResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method SendOutboundMessage not implemented")
return nil, status.Error(codes.Unimplemented, "method SendOutboundMessage not implemented")
}
func (UnimplementedControlServiceServer) SaveSnapshot(context.Context, *SaveSnapshotRequest) (*SaveSnapshotResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method SaveSnapshot not implemented")
return nil, status.Error(codes.Unimplemented, "method SaveSnapshot not implemented")
}
func (UnimplementedControlServiceServer) LoadSnapshot(context.Context, *LoadSnapshotRequest) (*LoadSnapshotResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method LoadSnapshot not implemented")
return nil, status.Error(codes.Unimplemented, "method LoadSnapshot not implemented")
}
func (UnimplementedControlServiceServer) RemoveSnapshot(context.Context, *RemoveSnapshotRequest) (*RemoveSnapshotResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method RemoveSnapshot not implemented")
return nil, status.Error(codes.Unimplemented, "method RemoveSnapshot not implemented")
}
func (UnimplementedControlServiceServer) GetSnapshotNames(context.Context, *GetSnapshotNamesRequest) (*GetSnapshotNamesResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetSnapshotNames not implemented")
return nil, status.Error(codes.Unimplemented, "method GetSnapshotNames not implemented")
}
func (UnimplementedControlServiceServer) mustEmbedUnimplementedControlServiceServer() {}
func (UnimplementedControlServiceServer) testEmbeddedByValue() {}
// UnsafeControlServiceServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to ControlServiceServer will
@@ -529,6 +555,13 @@ type UnsafeControlServiceServer interface {
}
func RegisterControlServiceServer(s grpc.ServiceRegistrar, srv ControlServiceServer) {
// If the following call panics, it indicates UnimplementedControlServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
t.testEmbeddedByValue()
}
s.RegisterService(&ControlService_ServiceDesc, srv)
}
@@ -586,20 +619,20 @@ func _ControlService_CreateBlockchains_Handler(srv interface{}, ctx context.Cont
return interceptor(ctx, in, info, handler)
}
func _ControlService_TransformElasticSubnets_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(TransformElasticSubnetsRequest)
func _ControlService_TransformElasticChains_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(TransformElasticChainsRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ControlServiceServer).TransformElasticSubnets(ctx, in)
return srv.(ControlServiceServer).TransformElasticChains(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: ControlService_TransformElasticSubnets_FullMethodName,
FullMethod: ControlService_TransformElasticChains_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ControlServiceServer).TransformElasticSubnets(ctx, req.(*TransformElasticSubnetsRequest))
return srv.(ControlServiceServer).TransformElasticChains(ctx, req.(*TransformElasticChainsRequest))
}
return interceptor(ctx, in, info, handler)
}
@@ -622,38 +655,38 @@ func _ControlService_AddPermissionlessValidator_Handler(srv interface{}, ctx con
return interceptor(ctx, in, info, handler)
}
func _ControlService_RemoveSubnetValidator_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(RemoveSubnetValidatorRequest)
func _ControlService_RemoveChainValidator_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(RemoveChainValidatorRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ControlServiceServer).RemoveSubnetValidator(ctx, in)
return srv.(ControlServiceServer).RemoveChainValidator(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: ControlService_RemoveSubnetValidator_FullMethodName,
FullMethod: ControlService_RemoveChainValidator_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ControlServiceServer).RemoveSubnetValidator(ctx, req.(*RemoveSubnetValidatorRequest))
return srv.(ControlServiceServer).RemoveChainValidator(ctx, req.(*RemoveChainValidatorRequest))
}
return interceptor(ctx, in, info, handler)
}
func _ControlService_CreateSubnets_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(CreateSubnetsRequest)
func _ControlService_CreateChains_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(CreateChainsRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ControlServiceServer).CreateSubnets(ctx, in)
return srv.(ControlServiceServer).CreateChains(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: ControlService_CreateSubnets_FullMethodName,
FullMethod: ControlService_CreateChains_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ControlServiceServer).CreateSubnets(ctx, req.(*CreateSubnetsRequest))
return srv.(ControlServiceServer).CreateChains(ctx, req.(*CreateChainsRequest))
}
return interceptor(ctx, in, info, handler)
}
@@ -735,21 +768,11 @@ func _ControlService_StreamStatus_Handler(srv interface{}, stream grpc.ServerStr
if err := stream.RecvMsg(m); err != nil {
return err
}
return srv.(ControlServiceServer).StreamStatus(m, &controlServiceStreamStatusServer{stream})
return srv.(ControlServiceServer).StreamStatus(m, &grpc.GenericServerStream[StreamStatusRequest, StreamStatusResponse]{ServerStream: stream})
}
type ControlService_StreamStatusServer interface {
Send(*StreamStatusResponse) error
grpc.ServerStream
}
type controlServiceStreamStatusServer struct {
grpc.ServerStream
}
func (x *controlServiceStreamStatusServer) Send(m *StreamStatusResponse) error {
return x.ServerStream.SendMsg(m)
}
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
type ControlService_StreamStatusServer = grpc.ServerStreamingServer[StreamStatusResponse]
func _ControlService_RemoveNode_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(RemoveNodeRequest)
@@ -987,20 +1010,20 @@ var ControlService_ServiceDesc = grpc.ServiceDesc{
Handler: _ControlService_CreateBlockchains_Handler,
},
{
MethodName: "TransformElasticSubnets",
Handler: _ControlService_TransformElasticSubnets_Handler,
MethodName: "TransformElasticChains",
Handler: _ControlService_TransformElasticChains_Handler,
},
{
MethodName: "AddPermissionlessValidator",
Handler: _ControlService_AddPermissionlessValidator_Handler,
},
{
MethodName: "RemoveSubnetValidator",
Handler: _ControlService_RemoveSubnetValidator_Handler,
MethodName: "RemoveChainValidator",
Handler: _ControlService_RemoveChainValidator_Handler,
},
{
MethodName: "CreateSubnets",
Handler: _ControlService_CreateSubnets_Handler,
MethodName: "CreateChains",
Handler: _ControlService_CreateChains_Handler,
},
{
MethodName: "Health",
+56 -56
View File
@@ -1,5 +1,5 @@
// Copyright (C) 2021-2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// SPDX-License-Identifier: BSD-3-Clause
package server
@@ -71,14 +71,14 @@ type localNetwork struct {
stopCh chan struct{}
stopOnce sync.Once
subnets map[string]*rpcpb.SubnetInfo
chains map[string]*rpcpb.ChainInfo
prometheusConfPath string
}
type chainInfo struct {
info *rpcpb.CustomChainInfo
subnetID ids.ID
chainID ids.ID
blockchainID ids.ID
}
@@ -86,7 +86,7 @@ type localNetworkOptions struct {
execPath string
rootDataDir string
numNodes uint32
trackSubnets string
trackChains string
redirectNodesOutput bool
globalNodeConfig string
@@ -97,8 +97,8 @@ type localNetworkOptions struct {
chainConfigs map[string]string
// upgrade configs to be added to the network, besides the ones in default config, or saved snapshot
upgradeConfigs map[string]string
// subnet configs to be added to the network, besides the ones in default config, or saved snapshot
subnetConfigs map[string]string
// P-chain configs to be added to the network, besides the ones in default config, or saved snapshot
pChainConfigs map[string]string
snapshotsDir string
@@ -130,7 +130,7 @@ func newLocalNetwork(opts localNetworkOptions) (*localNetwork, error) {
customChainIDToInfo: make(map[ids.ID]chainInfo),
stopCh: make(chan struct{}),
nodeInfos: make(map[string]*rpcpb.NodeInfo),
subnets: make(map[string]*rpcpb.SubnetInfo),
chains: make(map[string]*rpcpb.ChainInfo),
}, nil
}
@@ -211,8 +211,8 @@ func (lc *localNetwork) createConfig() error {
if cfg.NodeConfigs[i].UpgradeConfigFiles == nil {
cfg.NodeConfigs[i].UpgradeConfigFiles = map[string]string{}
}
if cfg.NodeConfigs[i].SubnetConfigFiles == nil {
cfg.NodeConfigs[i].SubnetConfigFiles = map[string]string{}
if cfg.NodeConfigs[i].ChainConfigFiles == nil {
cfg.NodeConfigs[i].ChainConfigFiles = map[string]string{}
}
for k, v := range lc.options.chainConfigs {
@@ -221,8 +221,8 @@ func (lc *localNetwork) createConfig() error {
for k, v := range lc.options.upgradeConfigs {
cfg.NodeConfigs[i].UpgradeConfigFiles[k] = v
}
for k, v := range lc.options.subnetConfigs {
cfg.NodeConfigs[i].SubnetConfigFiles[k] = v
for k, v := range lc.options.chainConfigs {
cfg.NodeConfigs[i].ChainConfigFiles[k] = v
}
if lc.options.dynamicPorts {
@@ -231,8 +231,8 @@ func (lc *localNetwork) createConfig() error {
delete(cfg.NodeConfigs[i].Flags, config.StakingPortKey)
}
if lc.options.trackSubnets != "" {
cfg.NodeConfigs[i].Flags[config.TrackNetsKey] = lc.options.trackSubnets
if lc.options.trackChains != "" {
cfg.NodeConfigs[i].Flags[config.TrackNetsKey] = lc.options.trackChains
}
cfg.NodeConfigs[i].BinaryPath = lc.execPath
@@ -301,7 +301,7 @@ func (lc *localNetwork) Start(ctx context.Context) error {
// Assumes [lc.lock] isn't held.
func (lc *localNetwork) CreateChains(
ctx context.Context,
chainSpecs []network.BlockchainSpec, // VM name + genesis bytes
chainSpecs []network.ChainSpec, // VM name + genesis bytes
) ([]ids.ID, error) {
lc.lock.Lock()
defer lc.lock.Unlock()
@@ -327,7 +327,7 @@ func (lc *localNetwork) CreateChains(
return nil, err
}
chainIDs, err := lc.nw.CreateBlockchains(ctx, chainSpecs)
chainIDs, err := lc.nw.CreateChains(ctx, chainSpecs)
if err != nil {
return nil, err
}
@@ -378,7 +378,7 @@ func (lc *localNetwork) AddPermissionlessValidators(ctx context.Context, validat
return nil
}
func (lc *localNetwork) RemoveSubnetValidator(ctx context.Context, validatorSpecs []network.RemoveSubnetValidatorSpec) error {
func (lc *localNetwork) RemoveChainValidator(ctx context.Context, validatorSpecs []network.RemoveChainValidatorSpec) error {
lc.lock.Lock()
defer lc.lock.Unlock()
@@ -404,7 +404,7 @@ func (lc *localNetwork) RemoveSubnetValidator(ctx context.Context, validatorSpec
return err
}
err := lc.nw.RemoveSubnetValidators(ctx, validatorSpecs)
err := lc.nw.RemoveChainValidators(ctx, validatorSpecs)
if err != nil {
return err
}
@@ -413,16 +413,16 @@ func (lc *localNetwork) RemoveSubnetValidator(ctx context.Context, validatorSpec
return err
}
ux.Print(lc.log, luxlog.Green.Wrap(luxlog.Bold.Wrap("finished removing subnet validators")))
ux.Print(lc.log, luxlog.Green.Wrap(luxlog.Bold.Wrap("finished removing chain validators")))
return nil
}
func (lc *localNetwork) TransformSubnets(ctx context.Context, elasticSubnetSpecs []network.ElasticSubnetSpec) ([]ids.ID, []ids.ID, error) {
func (lc *localNetwork) TransformChains(ctx context.Context, elasticChainSpecs []network.ElasticChainSpec) ([]ids.ID, []ids.ID, error) {
lc.lock.Lock()
defer lc.lock.Unlock()
if len(elasticSubnetSpecs) == 0 {
ux.Print(lc.log, luxlog.Orange.Wrap(luxlog.Bold.Wrap("no subnets specified...")))
if len(elasticChainSpecs) == 0 {
ux.Print(lc.log, luxlog.Orange.Wrap(luxlog.Bold.Wrap("no chains specified...")))
return nil, nil, nil
}
@@ -443,7 +443,7 @@ func (lc *localNetwork) TransformSubnets(ctx context.Context, elasticSubnetSpecs
return nil, nil, err
}
chainIDs, assetIDs, err := lc.nw.TransformSubnet(ctx, elasticSubnetSpecs)
chainIDs, assetIDs, err := lc.nw.TransformChain(ctx, elasticChainSpecs)
if err != nil {
return nil, nil, err
}
@@ -452,18 +452,18 @@ func (lc *localNetwork) TransformSubnets(ctx context.Context, elasticSubnetSpecs
return nil, nil, err
}
ux.Print(lc.log, luxlog.Green.Wrap(luxlog.Bold.Wrap("finished transforming subnets")))
ux.Print(lc.log, luxlog.Green.Wrap(luxlog.Bold.Wrap("finished transforming chains")))
return chainIDs, assetIDs, nil
}
// Creates the given number of subnets.
// CreateParticipantGroups creates the given number of participant groups (validators).
// Assumes [lc.lock] isn't held.
func (lc *localNetwork) CreateSubnets(ctx context.Context, subnetSpecs []network.SubnetSpec) ([]ids.ID, error) {
func (lc *localNetwork) CreateParticipantGroups(ctx context.Context, participantsSpecs []network.ParticipantsSpec) ([]ids.ID, error) {
lc.lock.Lock()
defer lc.lock.Unlock()
if len(subnetSpecs) == 0 {
ux.Print(lc.log, luxlog.Orange.Wrap(luxlog.Bold.Wrap("no subnets specified...")))
if len(participantsSpecs) == 0 {
ux.Print(lc.log, luxlog.Orange.Wrap(luxlog.Bold.Wrap("no chains specified...")))
return nil, nil
}
@@ -484,7 +484,7 @@ func (lc *localNetwork) CreateSubnets(ctx context.Context, subnetSpecs []network
return nil, err
}
subnetIDs, err := lc.nw.CreateSubnets(ctx, subnetSpecs)
chainIDs, err := lc.nw.CreateParticipantGroups(ctx, participantsSpecs)
if err != nil {
return nil, err
}
@@ -493,8 +493,8 @@ func (lc *localNetwork) CreateSubnets(ctx context.Context, subnetSpecs []network
return nil, err
}
ux.Print(lc.log, luxlog.Green.Wrap(luxlog.Bold.Wrap("finished adding subnets")))
return subnetIDs, nil
ux.Print(lc.log, luxlog.Green.Wrap(luxlog.Bold.Wrap("finished adding chains")))
return chainIDs, nil
}
// Loads a snapshot and sets [l.nw] to the network created from the snapshot.
@@ -521,7 +521,7 @@ func (lc *localNetwork) LoadSnapshot(snapshotName string) error {
lc.pluginDir,
lc.options.chainConfigs,
lc.options.upgradeConfigs,
lc.options.subnetConfigs,
lc.options.chainConfigs,
globalNodeConfig,
lc.options.reassignPortsIfUsed,
)
@@ -539,10 +539,10 @@ func (lc *localNetwork) LoadSnapshot(snapshotName string) error {
// Populates [lc.customChainIDToInfo] for all chains other than those on
// the Primary Network (P-Chain, X-Chain, C-Chain.)
// Populates [lc.subnets] with all subnets that exist.
// Populates [lc.chains] with all chains that exist.
// Doesn't contain the Primary network.
// Assumes [lc.lock] is held.
func (lc *localNetwork) updateSubnetInfo(ctx context.Context) error {
func (lc *localNetwork) updateChainInfo(ctx context.Context) error {
nodes, err := lc.nw.GetAllNodes()
if err != nil {
return err
@@ -580,33 +580,33 @@ func (lc *localNetwork) updateSubnetInfo(ctx context.Context) error {
info: &rpcpb.CustomChainInfo{
ChainName: blockchain.Name,
VmId: blockchain.VMID.String(),
SubnetId: blockchain.NetID.String(),
ChainId: blockchain.ID.String(),
PchainId: blockchain.NetID.String(),
BlockchainId: blockchain.ID.String(),
},
subnetID: blockchain.NetID,
chainID: blockchain.NetID,
blockchainID: blockchain.ID,
}
}
subnets, err := (*pChainClient).GetNets(ctx, nil)
chains, err := (*pChainClient).GetNets(ctx, nil)
if err != nil {
return err
}
subnetIDList := []string{}
for _, subnet := range subnets {
chainIDList := []string{}
for _, chain := range chains {
// Skip both PlatformChainID and PrimaryNetworkID (which is ids.Empty)
if subnet.ID != luxd_constants.PlatformChainID && subnet.ID != luxd_constants.PrimaryNetworkID {
subnetIDList = append(subnetIDList, subnet.ID.String())
if chain.ID != luxd_constants.PlatformChainID && chain.ID != luxd_constants.PrimaryNetworkID {
chainIDList = append(chainIDList, chain.ID.String())
}
}
for _, subnetIDStr := range subnetIDList {
subnetID, err := ids.FromString(subnetIDStr)
for _, chainIDStr := range chainIDList {
chainID, err := ids.FromString(chainIDStr)
if err != nil {
return err
}
vdrs, err := (*pChainClient).GetCurrentValidators(ctx, subnetID, nil)
vdrs, err := (*pChainClient).GetCurrentValidators(ctx, chainID, nil)
if err != nil {
return err
}
@@ -621,24 +621,24 @@ func (lc *localNetwork) updateSubnetInfo(ctx context.Context) error {
}
isElastic := false
elasticSubnetID := ids.Empty
if _, _, err := (*pChainClient).GetCurrentSupply(ctx, subnetID); err == nil {
elasticChainID := ids.Empty
if _, _, err := (*pChainClient).GetCurrentSupply(ctx, chainID); err == nil {
isElastic = true
elasticSubnetID, err = lc.nw.GetElasticSubnetID(ctx, subnetID)
elasticChainID, err = lc.nw.GetElasticChainID(ctx, chainID)
if err != nil {
return err
}
}
lc.subnets[subnetIDStr] = &rpcpb.SubnetInfo{
lc.chains[chainIDStr] = &rpcpb.ChainInfo{
IsElastic: isElastic,
ElasticSubnetId: elasticSubnetID.String(),
SubnetParticipants: &rpcpb.SubnetParticipants{NodeNames: nodeNameList},
ElasticChainId: elasticChainID.String(),
ChainParticipants: &rpcpb.ChainParticipants{NodeNames: nodeNameList},
}
}
for chainID, chainInfo := range lc.customChainIDToInfo {
vs, err := (*pChainClient).GetCurrentValidators(ctx, chainInfo.subnetID, nil)
vs, err := (*pChainClient).GetCurrentValidators(ctx, chainInfo.chainID, nil)
if err != nil {
return err
}
@@ -651,7 +651,7 @@ func (lc *localNetwork) updateSubnetInfo(ctx context.Context) error {
}
}
if len(nodeNames) != len(vs) {
return fmt.Errorf("not all subnet validators are in network for subnet %s", chainInfo.subnetID.String())
return fmt.Errorf("not all chain validators are in network for chain %s", chainInfo.chainID.String())
}
sort.Strings(nodeNames)
@@ -676,7 +676,7 @@ func (lc *localNetwork) AwaitHealthyAndUpdateNetworkInfo(ctx context.Context) er
}
// Returns nil when [lc.nw] reports healthy.
// Updates node and subnet info.
// Updates node and chain info.
// Assumes [lc.lock] is held.
func (lc *localNetwork) awaitHealthyAndUpdateNetworkInfo(ctx context.Context) error {
ux.Print(lc.log, luxlog.Blue.Wrap(luxlog.Bold.Wrap("waiting for all nodes to report healthy...")))
@@ -689,7 +689,7 @@ func (lc *localNetwork) awaitHealthyAndUpdateNetworkInfo(ctx context.Context) er
return err
}
if err := lc.updateSubnetInfo(ctx); err != nil {
if err := lc.updateChainInfo(ctx); err != nil {
return err
}
@@ -725,7 +725,7 @@ func (lc *localNetwork) updateNodeInfo() error {
lc.nodeInfos = make(map[string]*rpcpb.NodeInfo)
for name, node := range nodes {
trackSubnets, err := node.GetFlag(config.TrackNetsKey)
trackChains, err := node.GetFlag(config.TrackNetsKey)
if err != nil {
return err
}
@@ -739,7 +739,7 @@ func (lc *localNetwork) updateNodeInfo() error {
DbDir: node.GetDbDir(),
Config: []byte(node.GetConfigFile()),
PluginDir: node.GetPluginDir(),
WhitelistedSubnets: trackSubnets,
WhitelistedChains: trackChains,
Paused: node.GetPaused(),
}
+123 -123
View File
@@ -1,5 +1,5 @@
// Copyright (C) 2021-2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// SPDX-License-Identifier: BSD-3-Clause
// Package server implements server.
package server
@@ -70,9 +70,9 @@ var (
ErrNodeNotFound = errors.New("node not found")
ErrPeerNotFound = errors.New("peer not found")
ErrStatusCanceled = errors.New("gRPC stream status canceled")
ErrNoBlockchainSpec = errors.New("no blockchain spec was provided")
ErrNoSubnetID = errors.New("subnetID is missing")
ErrNoElasticSubnetSpec = errors.New("no elastic subnet spec was provided")
ErrNoChainSpec = errors.New("no blockchain spec was provided")
ErrNoChainID = errors.New("chainID is missing")
ErrNoElasticChainSpec = errors.New("no elastic chain spec was provided")
ErrNoValidatorSpec = errors.New("no validator spec was provided")
)
@@ -285,11 +285,11 @@ func (s *server) Start(_ context.Context, req *rpcpb.StartRequest) (*rpcpb.Start
}
pluginDir := req.GetPluginDir()
chainSpecs := []network.BlockchainSpec{}
chainSpecs := []network.ChainSpec{}
if len(req.GetBlockchainSpecs()) > 0 {
s.log.Info("plugin-dir:", zap.String("plugin-dir", pluginDir))
for _, spec := range req.GetBlockchainSpecs() {
chainSpec, err := getNetworkBlockchainSpec(s.log, spec, true, pluginDir)
chainSpec, err := getNetworkChainSpec(s.log, spec, true, pluginDir)
if err != nil {
return nil, err
}
@@ -300,7 +300,7 @@ func (s *server) Start(_ context.Context, req *rpcpb.StartRequest) (*rpcpb.Start
var (
execPath = req.GetExecPath()
numNodes = req.GetNumNodes()
trackSubnets = req.GetWhitelistedSubnets()
trackChains = req.GetWhitelistedChains()
rootDataDir = req.GetRootDataDir()
pid = int32(os.Getpid())
globalNodeConfig = req.GetGlobalNodeConfig()
@@ -335,14 +335,14 @@ func (s *server) Start(_ context.Context, req *rpcpb.StartRequest) (*rpcpb.Start
execPath: execPath,
rootDataDir: rootDataDir,
numNodes: numNodes,
trackSubnets: trackSubnets,
trackChains: trackChains,
redirectNodesOutput: s.cfg.RedirectNodesOutput,
pluginDir: pluginDir,
globalNodeConfig: globalNodeConfig,
customNodeConfigs: customNodeConfigs,
chainConfigs: req.ChainConfigs,
upgradeConfigs: req.UpgradeConfigs,
subnetConfigs: req.SubnetConfigs,
pChainConfigs: req.ChainConfigFiles,
logLevel: s.cfg.LogLevel,
reassignPortsIfUsed: req.GetReassignPortsIfUsed(),
dynamicPorts: req.GetDynamicPorts(),
@@ -355,7 +355,7 @@ func (s *server) Start(_ context.Context, req *rpcpb.StartRequest) (*rpcpb.Start
s.log.Info("starting",
zap.String("exec-path", execPath),
zap.Uint32("num-nodes", numNodes),
zap.String("track-subnets", trackSubnets),
zap.String("track-chains", trackChains),
zap.Int32("pid", pid),
zap.String("root-data-dir", rootDataDir),
zap.String("plugin-dir", pluginDir),
@@ -409,7 +409,7 @@ func (s *server) updateClusterInfo() {
for chainID, chainInfo := range s.network.customChainIDToInfo {
s.clusterInfo.CustomChains[chainID.String()] = chainInfo.info
}
s.clusterInfo.Subnets = s.network.subnets
s.clusterInfo.Chains = s.network.chains
}
// wait until some of this conditions is met:
@@ -477,26 +477,26 @@ func (s *server) CreateBlockchains(
s.log.Debug("CreateBlockchains")
if len(req.GetBlockchainSpecs()) == 0 {
return nil, ErrNoBlockchainSpec
return nil, ErrNoChainSpec
}
chainSpecs := []network.BlockchainSpec{}
chainSpecs := []network.ChainSpec{}
for _, spec := range req.GetBlockchainSpecs() {
chainSpec, err := getNetworkBlockchainSpec(s.log, spec, false, s.network.pluginDir)
chainSpec, err := getNetworkChainSpec(s.log, spec, false, s.network.pluginDir)
if err != nil {
return nil, err
}
chainSpecs = append(chainSpecs, chainSpec)
}
// check that the given subnets exist
subnetsSet := set.Set[string]{}
subnetIDsList := maps.Keys(s.clusterInfo.Subnets)
subnetsSet.Add(subnetIDsList...)
// check that the given chains exist
chainsSet := set.Set[string]{}
chainIDsList := maps.Keys(s.clusterInfo.Chains)
chainsSet.Add(chainIDsList...)
for _, chainSpec := range chainSpecs {
if chainSpec.SubnetID != nil && !subnetsSet.Contains(*chainSpec.SubnetID) {
return nil, fmt.Errorf("subnet id %q does not exits", *chainSpec.SubnetID)
if chainSpec.ChainID != nil && !chainsSet.Contains(*chainSpec.ChainID) {
return nil, fmt.Errorf("chain id %q does not exist", *chainSpec.ChainID)
}
}
@@ -554,15 +554,15 @@ func (s *server) AddPermissionlessValidator(
validatorSpecList = append(validatorSpecList, validatorSpec)
}
// check that the given subnets exist
subnetsSet := set.Set[string]{}
subnetsSet.Add(maps.Keys(s.clusterInfo.Subnets)...)
// check that the given chains exist
chainsSet := set.Set[string]{}
chainsSet.Add(maps.Keys(s.clusterInfo.Chains)...)
for _, validatorSpec := range validatorSpecList {
if validatorSpec.SubnetID == "" {
return nil, ErrNoSubnetID
} else if !subnetsSet.Contains(validatorSpec.SubnetID) {
return nil, fmt.Errorf("subnet id %q does not exist", validatorSpec.SubnetID)
if validatorSpec.ChainID == "" {
return nil, ErrNoChainID
} else if !chainsSet.Contains(validatorSpec.ChainID) {
return nil, fmt.Errorf("chain id %q does not exist", validatorSpec.ChainID)
}
}
@@ -589,10 +589,10 @@ func (s *server) AddPermissionlessValidator(
return &rpcpb.AddPermissionlessValidatorResponse{ClusterInfo: clusterInfo}, nil
}
func (s *server) RemoveSubnetValidator(
func (s *server) RemoveChainValidator(
_ context.Context,
req *rpcpb.RemoveSubnetValidatorRequest,
) (*rpcpb.RemoveSubnetValidatorResponse, error) {
req *rpcpb.RemoveChainValidatorRequest,
) (*rpcpb.RemoveChainValidatorResponse, error) {
s.mu.Lock()
defer s.mu.Unlock()
@@ -600,27 +600,27 @@ func (s *server) RemoveSubnetValidator(
return nil, ErrNotBootstrapped
}
s.log.Debug("RemoveSubnetValidator")
s.log.Debug("RemoveChainValidator")
if len(req.GetValidatorSpec()) == 0 {
return nil, ErrNoValidatorSpec
}
validatorSpecList := []network.RemoveSubnetValidatorSpec{}
validatorSpecList := []network.RemoveChainValidatorSpec{}
for _, spec := range req.GetValidatorSpec() {
validatorSpec := getRemoveSubnetValidatorSpec(spec)
validatorSpec := getRemoveChainValidatorSpec(spec)
validatorSpecList = append(validatorSpecList, validatorSpec)
}
// check that the given subnets exist
subnetsSet := set.Set[string]{}
subnetsSet.Add(maps.Keys(s.clusterInfo.Subnets)...)
// check that the given chains exist
chainsSet := set.Set[string]{}
chainsSet.Add(maps.Keys(s.clusterInfo.Chains)...)
for _, validatorSpec := range validatorSpecList {
if validatorSpec.SubnetID == "" {
return nil, ErrNoSubnetID
} else if !subnetsSet.Contains(validatorSpec.SubnetID) {
return nil, fmt.Errorf("subnet id %q does not exist", validatorSpec.SubnetID)
if validatorSpec.ChainID == "" {
return nil, ErrNoChainID
} else if !chainsSet.Contains(validatorSpec.ChainID) {
return nil, fmt.Errorf("chain id %q does not exist", validatorSpec.ChainID)
}
}
@@ -629,28 +629,28 @@ func (s *server) RemoveSubnetValidator(
ctx, cancel := context.WithTimeout(context.Background(), waitForHealthyTimeout)
defer cancel()
err := s.network.RemoveSubnetValidator(ctx, validatorSpecList)
err := s.network.RemoveChainValidator(ctx, validatorSpecList)
s.updateClusterInfo()
if err != nil {
s.log.Error("failed to remove subnet validator", zap.Error(err))
s.log.Error("failed to remove chain validator", zap.Error(err))
return nil, err
}
s.log.Info("successfully removed subnet validator")
s.log.Info("successfully removed chain validator")
clusterInfo, err := deepCopy(s.clusterInfo)
if err != nil {
return nil, err
}
return &rpcpb.RemoveSubnetValidatorResponse{ClusterInfo: clusterInfo}, nil
return &rpcpb.RemoveChainValidatorResponse{ClusterInfo: clusterInfo}, nil
}
func (s *server) TransformElasticSubnets(
func (s *server) TransformElasticChains(
_ context.Context,
req *rpcpb.TransformElasticSubnetsRequest,
) (*rpcpb.TransformElasticSubnetsResponse, error) {
req *rpcpb.TransformElasticChainsRequest,
) (*rpcpb.TransformElasticChainsResponse, error) {
s.mu.Lock()
defer s.mu.Unlock()
@@ -658,27 +658,27 @@ func (s *server) TransformElasticSubnets(
return nil, ErrNotBootstrapped
}
s.log.Debug("TransformElasticSubnet")
s.log.Debug("TransformElasticChain")
if len(req.GetElasticSubnetSpec()) == 0 {
return nil, ErrNoElasticSubnetSpec
if len(req.GetElasticChainSpec()) == 0 {
return nil, ErrNoElasticChainSpec
}
elasticSubnetSpecList := []network.ElasticSubnetSpec{}
for _, spec := range req.GetElasticSubnetSpec() {
elasticSubnetSpec := getNetworkElasticSubnetSpec(spec)
elasticSubnetSpecList = append(elasticSubnetSpecList, elasticSubnetSpec)
elasticParticipantsSpecList := []network.ElasticChainSpec{}
for _, spec := range req.GetElasticChainSpec() {
elasticParticipantsSpec := getNetworkElasticChainSpec(spec)
elasticParticipantsSpecList = append(elasticParticipantsSpecList, elasticParticipantsSpec)
}
// check that the given subnets exist
subnetsSet := set.Set[string]{}
subnetsSet.Add(maps.Keys(s.clusterInfo.Subnets)...)
// check that the given chains exist
chainsSet := set.Set[string]{}
chainsSet.Add(maps.Keys(s.clusterInfo.Chains)...)
for _, elasticSubnetSpec := range elasticSubnetSpecList {
if elasticSubnetSpec.SubnetID == nil {
return nil, ErrNoSubnetID
} else if !subnetsSet.Contains(*elasticSubnetSpec.SubnetID) {
return nil, fmt.Errorf("subnet id %q does not exist", *elasticSubnetSpec.SubnetID)
for _, elasticParticipantsSpec := range elasticParticipantsSpecList {
if elasticParticipantsSpec.ChainID == nil {
return nil, ErrNoChainID
} else if !chainsSet.Contains(*elasticParticipantsSpec.ChainID) {
return nil, fmt.Errorf("chain id %q does not exist", *elasticParticipantsSpec.ChainID)
}
}
@@ -687,16 +687,16 @@ func (s *server) TransformElasticSubnets(
ctx, cancel := context.WithTimeout(context.Background(), waitForHealthyTimeout)
defer cancel()
txIDs, assetIDs, err := s.network.TransformSubnets(ctx, elasticSubnetSpecList)
txIDs, assetIDs, err := s.network.TransformChains(ctx, elasticParticipantsSpecList)
s.updateClusterInfo()
if err != nil {
s.log.Error("failed to transform subnet into elastic subnet", zap.Error(err))
s.log.Error("failed to transform chain into elastic chain", zap.Error(err))
return nil, err
}
s.log.Info("subnet transformed into elastic subnet")
s.log.Info("chain transformed into elastic chain")
strTXIDs := []string{}
for _, txID := range txIDs {
@@ -712,10 +712,10 @@ func (s *server) TransformElasticSubnets(
if err != nil {
return nil, err
}
return &rpcpb.TransformElasticSubnetsResponse{ClusterInfo: clusterInfo, TxIds: strTXIDs, AssetIds: strAssetIDs}, nil
return &rpcpb.TransformElasticChainsResponse{ClusterInfo: clusterInfo, TxIds: strTXIDs, AssetIds: strAssetIDs}, nil
}
func (s *server) CreateSubnets(_ context.Context, req *rpcpb.CreateSubnetsRequest) (*rpcpb.CreateSubnetsResponse, error) {
func (s *server) CreateChains(_ context.Context, req *rpcpb.CreateChainsRequest) (*rpcpb.CreateChainsResponse, error) {
s.mu.Lock()
defer s.mu.Unlock()
@@ -723,12 +723,12 @@ func (s *server) CreateSubnets(_ context.Context, req *rpcpb.CreateSubnetsReques
return nil, ErrNotBootstrapped
}
s.log.Debug("CreateSubnets", zap.Uint32("num-subnets", uint32(len(req.GetSubnetSpecs()))))
s.log.Debug("CreateParticipantGroups", zap.Uint32("num-groups", uint32(len(req.GetChainSpecs()))))
subnetSpecs := []network.SubnetSpec{}
for _, spec := range req.GetSubnetSpecs() {
subnetSpec := getNetworkSubnetSpec(spec)
subnetSpecs = append(subnetSpecs, subnetSpec)
participantsSpecs := []network.ParticipantsSpec{}
for _, spec := range req.GetChainSpecs() {
participantsSpec := getNetworkParticipantsSpec(spec)
participantsSpecs = append(participantsSpecs, participantsSpec)
}
s.log.Info("waiting for local cluster readiness")
@@ -738,26 +738,26 @@ func (s *server) CreateSubnets(_ context.Context, req *rpcpb.CreateSubnetsReques
ctx, cancel := context.WithTimeout(context.Background(), waitForHealthyTimeout)
defer cancel()
subnetIDs, err := s.network.CreateSubnets(ctx, subnetSpecs)
chainIDs, err := s.network.CreateParticipantGroups(ctx, participantsSpecs)
if err != nil {
s.log.Error("failed to create subnets", zap.Error(err))
s.log.Error("failed to create chains", zap.Error(err))
s.stopAndRemoveNetwork(err)
return nil, err
} else {
s.updateClusterInfo()
}
s.log.Info("subnets created")
s.log.Info("chains created")
strSubnetIDs := []string{}
for _, subnetID := range subnetIDs {
strSubnetIDs = append(strSubnetIDs, subnetID.String())
strChainIDs := []string{}
for _, chainID := range chainIDs {
strChainIDs = append(strChainIDs, chainID.String())
}
clusterInfo, err := deepCopy(s.clusterInfo)
if err != nil {
return nil, err
}
return &rpcpb.CreateSubnetsResponse{ClusterInfo: clusterInfo, SubnetIds: strSubnetIDs}, nil
return &rpcpb.CreateChainsResponse{ClusterInfo: clusterInfo, ChainIds: strChainIDs}, nil
}
func (s *server) Health(ctx context.Context, _ *rpcpb.HealthRequest) (*rpcpb.HealthResponse, error) {
@@ -971,7 +971,7 @@ func (s *server) AddNode(_ context.Context, req *rpcpb.AddNodeRequest) (*rpcpb.A
RedirectStderr: s.cfg.RedirectNodesOutput,
ChainConfigFiles: req.ChainConfigs,
UpgradeConfigFiles: req.UpgradeConfigs,
SubnetConfigFiles: req.SubnetConfigs,
PChainConfigFiles: req.ChainConfigFiles,
}
if _, err := s.network.nw.AddNode(nodeConfig); err != nil {
@@ -1037,10 +1037,10 @@ func (s *server) RestartNode(ctx context.Context, req *rpcpb.RestartNodeRequest)
req.Name,
req.GetExecPath(),
req.GetPluginDir(),
req.GetWhitelistedSubnets(),
req.GetWhitelistedChains(),
req.GetChainConfigs(),
req.GetUpgradeConfigs(),
req.GetSubnetConfigs(),
req.GetChainConfigFiles(),
); err != nil {
return nil, err
}
@@ -1278,7 +1278,7 @@ func (s *server) LoadSnapshot(_ context.Context, req *rpcpb.LoadSnapshotRequest)
rootDataDir: rootDataDir,
chainConfigs: req.ChainConfigs,
upgradeConfigs: req.UpgradeConfigs,
subnetConfigs: req.SubnetConfigs,
pChainConfigs: req.ChainConfigFiles,
globalNodeConfig: req.GetGlobalNodeConfig(),
logLevel: s.cfg.LogLevel,
reassignPortsIfUsed: req.GetReassignPortsIfUsed(),
@@ -1404,14 +1404,14 @@ func isClientCanceled(ctxErr error, err error) bool {
return false
}
func getNetworkElasticSubnetSpec(
spec *rpcpb.ElasticSubnetSpec,
) network.ElasticSubnetSpec {
func getNetworkElasticChainSpec(
spec *rpcpb.ElasticChainSpec,
) network.ElasticChainSpec {
minStakeDuration := time.Duration(spec.MinStakeDuration) * time.Hour
maxStakeDuration := time.Duration(spec.MaxStakeDuration) * time.Hour
elasticSubnetSpec := network.ElasticSubnetSpec{
SubnetID: &spec.SubnetId,
elasticParticipantsSpec := network.ElasticChainSpec{
ChainID: &spec.ChainId,
AssetName: spec.AssetName,
AssetSymbol: spec.AssetSymbol,
InitialSupply: spec.InitialSupply,
@@ -1427,7 +1427,7 @@ func getNetworkElasticSubnetSpec(
MaxValidatorWeightFactor: byte(spec.MaxValidatorWeightFactor),
UptimeRequirement: spec.UptimeRequirement,
}
return elasticSubnetSpec
return elasticParticipantsSpec
}
func getPermissionlessValidatorSpec(
@@ -1448,7 +1448,7 @@ func getPermissionlessValidatorSpec(
stakeDuration := time.Duration(spec.StakeDuration) * time.Hour
validatorSpec := network.PermissionlessValidatorSpec{
SubnetID: spec.SubnetId,
ChainID: spec.ChainId,
AssetID: spec.AssetId,
NodeName: spec.NodeName,
StakedAmount: spec.StakedTokenAmount,
@@ -1458,24 +1458,24 @@ func getPermissionlessValidatorSpec(
return validatorSpec, nil
}
func getRemoveSubnetValidatorSpec(
spec *rpcpb.RemoveSubnetValidatorSpec,
) network.RemoveSubnetValidatorSpec {
validatorSpec := network.RemoveSubnetValidatorSpec{
SubnetID: spec.SubnetId,
func getRemoveChainValidatorSpec(
spec *rpcpb.RemoveChainValidatorSpec,
) network.RemoveChainValidatorSpec {
validatorSpec := network.RemoveChainValidatorSpec{
ChainID: spec.ChainId,
NodeNames: spec.GetNodeNames(),
}
return validatorSpec
}
func getNetworkBlockchainSpec(
func getNetworkChainSpec(
log luxlog.Logger,
spec *rpcpb.BlockchainSpec,
isNewEmptyNetwork bool,
pluginDir string,
) (network.BlockchainSpec, error) {
if isNewEmptyNetwork && spec.SubnetId != nil {
return network.BlockchainSpec{}, errors.New("blockchain subnet id must be nil if starting a new empty network")
) (network.ChainSpec, error) {
if isNewEmptyNetwork && spec.ChainId != nil {
return network.ChainSpec{}, errors.New("blockchain chain id must be nil if starting a new empty network")
}
vmName := spec.VmName
@@ -1483,7 +1483,7 @@ func getNetworkBlockchainSpec(
vmID, err := utils.VMID(vmName)
if err != nil {
log.Warn("failed to convert VM name to VM ID", zap.String("vm-name", vmName), zap.Error(err))
return network.BlockchainSpec{}, ErrInvalidVMName
return network.ChainSpec{}, ErrInvalidVMName
}
// there is no default plugindir from the ANR point of view, will not check if not given
@@ -1491,7 +1491,7 @@ func getNetworkBlockchainSpec(
if err := utils.CheckPluginPath(
filepath.Join(pluginDir, vmID.String()),
); err != nil {
return network.BlockchainSpec{}, err
return network.ChainSpec{}, err
}
}
@@ -1507,9 +1507,9 @@ func getNetworkBlockchainSpec(
networkUpgradeBytes = readFileOrString(spec.NetworkUpgrade)
}
var subnetConfigBytes []byte
if spec.SubnetSpec != nil && spec.SubnetSpec.SubnetConfig != "" {
subnetConfigBytes = readFileOrString(spec.SubnetSpec.SubnetConfig)
// Override chain config with ChainSpec.ChainConfigFile if provided
if spec.ChainSpec != nil && spec.ChainSpec.ChainConfigFile != "" {
chainConfigBytes = readFileOrString(spec.ChainSpec.ChainConfigFile)
}
perNodeChainConfig := map[string][]byte{}
@@ -1518,49 +1518,49 @@ func getNetworkBlockchainSpec(
perNodeChainConfigMap := map[string]interface{}{}
if err := json.Unmarshal(perNodeChainConfigBytes, &perNodeChainConfigMap); err != nil {
return network.BlockchainSpec{}, err
return network.ChainSpec{}, err
}
for nodeName, cfg := range perNodeChainConfigMap {
cfgBytes, err := json.Marshal(cfg)
if err != nil {
return network.BlockchainSpec{}, err
return network.ChainSpec{}, err
}
perNodeChainConfig[nodeName] = cfgBytes
}
}
blockchainSpec := network.BlockchainSpec{
chainSpec := network.ChainSpec{
VMName: vmName,
Genesis: genesisBytes,
ChainConfig: chainConfigBytes,
NetworkUpgrade: networkUpgradeBytes,
SubnetID: spec.SubnetId,
BlockchainAlias: spec.BlockchainAlias,
ChainID: spec.ChainId,
Alias: spec.BlockchainAlias,
PerNodeChainConfig: perNodeChainConfig,
}
if spec.SubnetSpec != nil {
subnetSpec := network.SubnetSpec{
Participants: spec.SubnetSpec.Participants,
SubnetConfig: subnetConfigBytes,
if spec.ChainSpec != nil {
participantsSpec := network.ParticipantsSpec{
Participants: spec.ChainSpec.Participants,
ChainConfig: chainConfigBytes,
}
blockchainSpec.SubnetSpec = &subnetSpec
chainSpec.ParticipantsSpec = &participantsSpec
}
return blockchainSpec, nil
return chainSpec, nil
}
func getNetworkSubnetSpec(
spec *rpcpb.SubnetSpec,
) network.SubnetSpec {
var subnetConfigBytes []byte
if spec.SubnetConfig != "" {
subnetConfigBytes = readFileOrString(spec.SubnetConfig)
func getNetworkParticipantsSpec(
spec *rpcpb.ChainSpec,
) network.ParticipantsSpec {
var chainConfigBytes []byte
if spec.ChainConfigFile != "" {
chainConfigBytes = readFileOrString(spec.ChainConfigFile)
}
return network.SubnetSpec{
return network.ParticipantsSpec{
Participants: spec.Participants,
SubnetConfig: subnetConfigBytes,
ChainConfig: chainConfigBytes,
}
}
+23
View File
@@ -0,0 +1,23 @@
name: lux-local
version: 1.0.0
description: Local Lux L1 for testing
engines:
- name: lux-node-1
type: lux
network_id: 96369
http_port: 9650
staking_port: 9651
log_level: info
wait_healthy: true
extra:
dev-mode: true
sybil-protection-disabled-weight: 100
networks:
- name: lux-local
type: l1
engine: lux-node-1
chain_id: 96369
endpoints:
- http://localhost:9650
+60
View File
@@ -0,0 +1,60 @@
name: lux-mainnet-5node
version: 1.0.0
description: 5-node Lux Mainnet local network
engines:
- name: mainnet-node-1
type: lux
network_id: 96369
http_port: 9650
staking_port: 9651
log_level: info
wait_healthy: true
data_dir: /tmp/lux-mainnet-5node/node1
- name: mainnet-node-2
type: lux
network_id: 96369
http_port: 9660
staking_port: 9661
log_level: info
wait_healthy: true
data_dir: /tmp/lux-mainnet-5node/node2
- name: mainnet-node-3
type: lux
network_id: 96369
http_port: 9670
staking_port: 9671
log_level: info
wait_healthy: true
data_dir: /tmp/lux-mainnet-5node/node3
- name: mainnet-node-4
type: lux
network_id: 96369
http_port: 9680
staking_port: 9681
log_level: info
wait_healthy: true
data_dir: /tmp/lux-mainnet-5node/node4
- name: mainnet-node-5
type: lux
network_id: 96369
http_port: 9690
staking_port: 9691
log_level: info
wait_healthy: true
data_dir: /tmp/lux-mainnet-5node/node5
networks:
- name: lux-mainnet-local
type: l1
chain_id: 96369
endpoints:
- http://localhost:9650
- http://localhost:9660
- http://localhost:9670
- http://localhost:9680
- http://localhost:9690
+60
View File
@@ -0,0 +1,60 @@
name: lux-mainnet-alt
version: 1.0.0
description: 5-node Lux Mainnet on alternate ports (9640+) for parallel network operation
engines:
- name: mainnet-alt-node-1
type: lux
network_id: 96369
http_port: 9640
staking_port: 9641
log_level: info
wait_healthy: true
data_dir: /tmp/lux-mainnet-alt/node1
- name: mainnet-alt-node-2
type: lux
network_id: 96369
http_port: 9642
staking_port: 9643
log_level: info
wait_healthy: true
data_dir: /tmp/lux-mainnet-alt/node2
- name: mainnet-alt-node-3
type: lux
network_id: 96369
http_port: 9644
staking_port: 9645
log_level: info
wait_healthy: true
data_dir: /tmp/lux-mainnet-alt/node3
- name: mainnet-alt-node-4
type: lux
network_id: 96369
http_port: 9646
staking_port: 9647
log_level: info
wait_healthy: true
data_dir: /tmp/lux-mainnet-alt/node4
- name: mainnet-alt-node-5
type: lux
network_id: 96369
http_port: 9648
staking_port: 9649
log_level: info
wait_healthy: true
data_dir: /tmp/lux-mainnet-alt/node5
networks:
- name: lux-mainnet-alt
type: l1
chain_id: 96369
endpoints:
- http://localhost:9640
- http://localhost:9642
- http://localhost:9644
- http://localhost:9646
- http://localhost:9648
+60
View File
@@ -0,0 +1,60 @@
name: lux-testnet-5node
version: 1.0.0
description: 5-node Lux Testnet local network
engines:
- name: testnet-node-1
type: lux
network_id: 96368
http_port: 9750
staking_port: 9751
log_level: info
wait_healthy: true
data_dir: /tmp/lux-testnet-5node/node1
- name: testnet-node-2
type: lux
network_id: 96368
http_port: 9760
staking_port: 9761
log_level: info
wait_healthy: true
data_dir: /tmp/lux-testnet-5node/node2
- name: testnet-node-3
type: lux
network_id: 96368
http_port: 9770
staking_port: 9771
log_level: info
wait_healthy: true
data_dir: /tmp/lux-testnet-5node/node3
- name: testnet-node-4
type: lux
network_id: 96368
http_port: 9780
staking_port: 9781
log_level: info
wait_healthy: true
data_dir: /tmp/lux-testnet-5node/node4
- name: testnet-node-5
type: lux
network_id: 96368
http_port: 9790
staking_port: 9791
log_level: info
wait_healthy: true
data_dir: /tmp/lux-testnet-5node/node5
networks:
- name: lux-testnet-local
type: l1
chain_id: 96368
endpoints:
- http://localhost:9750
- http://localhost:9760
- http://localhost:9770
- http://localhost:9780
- http://localhost:9790
+72 -69
View File
@@ -1,5 +1,8 @@
//go:build e2e
// +build e2e
// Copyright (C) 2021-2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// SPDX-License-Identifier: BSD-3-Clause
// e2e implements the e2e tests.
package e2e_test
@@ -81,8 +84,8 @@ var (
{"new_node1", "new_node2"},
{"new_node3", "new_node4"},
}
testElasticSubnetConfig = rpcpb.ElasticSubnetSpec{
SubnetId: "",
testElasticChainConfig = rpcpb.ElasticChainSpec{
ChainId: "",
AssetName: "BLIZZARD",
AssetSymbol: "BRRR",
InitialSupply: 240000000,
@@ -110,7 +113,7 @@ func init() {
flag.StringVar(
&logLevel,
"log-level",
logging.Info.String(),
luxlog.Info.String(),
"log level",
)
flag.StringVar(
@@ -166,10 +169,10 @@ var _ = ginkgo.BeforeSuite(func() {
logDir, err = utils.MkDirWithTimestamp(clientRootDir)
gomega.Ω(err).Should(gomega.BeNil())
}
lvl, err := logging.ToLevel(logLevel)
lvl, err := luxlog.ToLevel(logLevel)
gomega.Ω(err).Should(gomega.BeNil())
logFactory := logging.NewFactory(logging.Config{
RotatingWriterConfig: logging.RotatingWriterConfig{
logFactory := luxlog.NewFactory(luxlog.Config{
RotatingWriterConfig: luxlog.RotatingWriterConfig{
Directory: logDir,
},
DisplayLevel: lvl,
@@ -191,13 +194,13 @@ var _ = ginkgo.BeforeSuite(func() {
})
var _ = ginkgo.AfterSuite(func() {
ux.Print(log, logging.Red.Wrap("shutting down cluster"))
ux.Print(log, luxlog.Red.Wrap("shutting down cluster"))
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
_, err := cli.Stop(ctx)
cancel()
gomega.Ω(err).Should(gomega.BeNil())
ux.Print(log, logging.Red.Wrap("shutting down client"))
ux.Print(log, luxlog.Red.Wrap("shutting down client"))
err = cli.Close()
gomega.Ω(err).Should(gomega.BeNil())
})
@@ -208,11 +211,11 @@ var _ = ginkgo.Describe("[Start/Remove/Restart/Add/Stop]", func() {
createdBlockchainID := ""
createdBlockchainID2 := ""
ginkgo.By("start with blockchain specs", func() {
ux.Print(log, logging.Green.Wrap("sending 'start' with the valid binary path: %s"), execPath1)
ux.Print(log, luxlog.Green.Wrap("sending 'start' with the valid binary path: %s"), execPath1)
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
resp, err := cli.Start(ctx, execPath1,
client.WithPluginDir(filepath.Join(filepath.Dir(execPath1), "plugins")),
client.WithBlockchainSpecs([]*rpcpb.BlockchainSpec{
client.WithChainSpecs([]*rpcpb.BlockchainSpec{
{
VmName: "subnetevm",
Genesis: genesisPath, // test genesis path usage
@@ -223,13 +226,13 @@ var _ = ginkgo.Describe("[Start/Remove/Restart/Add/Stop]", func() {
gomega.Ω(err).Should(gomega.BeNil())
gomega.Ω(len(resp.ChainIds)).Should(gomega.Equal(1))
createdBlockchainID = resp.ChainIds[0]
ux.Print(log, logging.Green.Wrap("successfully started, node-names: %s"), resp.ClusterInfo.NodeNames)
ux.Print(log, luxlog.Green.Wrap("successfully started, node-names: %s"), resp.ClusterInfo.NodeNames)
})
ginkgo.By("can create a blockchain with a new subnet id", func() {
ux.Print(log, logging.Blue.Wrap("can create a blockchain in a new subnet"))
ux.Print(log, luxlog.Blue.Wrap("can create a blockchain in a new subnet"))
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
resp, err := cli.CreateBlockchains(ctx,
resp, err := cli.CreateChains(ctx,
[]*rpcpb.BlockchainSpec{
{
VmName: "subnetevm",
@@ -267,9 +270,9 @@ var _ = ginkgo.Describe("[Start/Remove/Restart/Add/Stop]", func() {
})
ginkgo.By("can create a blockchain with an existing subnet id", func() {
ux.Print(log, logging.Blue.Wrap("can create a blockchain in an existing subnet"))
ux.Print(log, luxlog.Blue.Wrap("can create a blockchain in an existing subnet"))
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
resp, err := cli.CreateBlockchains(ctx,
resp, err := cli.CreateChains(ctx,
[]*rpcpb.BlockchainSpec{
{
VmName: "subnetevm",
@@ -307,9 +310,9 @@ var _ = ginkgo.Describe("[Start/Remove/Restart/Add/Stop]", func() {
})
ginkgo.By("can create a blockchain with an existing subnet id loaded from snapshot", func() {
ux.Print(log, logging.Blue.Wrap("can create a blockchain in an existing subnet"))
ux.Print(log, luxlog.Blue.Wrap("can create a blockchain in an existing subnet"))
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
_, err := cli.CreateBlockchains(ctx,
_, err := cli.CreateChains(ctx,
[]*rpcpb.BlockchainSpec{
{
VmName: "subnetevm",
@@ -323,14 +326,14 @@ var _ = ginkgo.Describe("[Start/Remove/Restart/Add/Stop]", func() {
})
ginkgo.By("can create a blockchain with new subnet id with some of existing participating nodes", func() {
ux.Print(log, logging.Blue.Wrap("can create a blockchain with new subnet id with some of existing participating nodes"))
ux.Print(log, luxlog.Blue.Wrap("can create a blockchain with new subnet id with some of existing participating nodes"))
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
resp, err := cli.CreateBlockchains(ctx,
resp, err := cli.CreateChains(ctx,
[]*rpcpb.BlockchainSpec{
{
VmName: "subnetevm",
Genesis: genesisContents,
SubnetSpec: &rpcpb.SubnetSpec{Participants: subnetParticipants},
ParticipantsSpec: &rpcpb.ParticipantsSpec{Participants: subnetParticipants},
},
},
)
@@ -355,14 +358,14 @@ var _ = ginkgo.Describe("[Start/Remove/Restart/Add/Stop]", func() {
})
ginkgo.By("can create a blockchain with new subnet id with some of existing participating nodes and a new node", func() {
ux.Print(log, logging.Blue.Wrap("can create a blockchain new subnet id with some of existing participating nodes and a new node"))
ux.Print(log, luxlog.Blue.Wrap("can create a blockchain new subnet id with some of existing participating nodes and a new node"))
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
resp, err := cli.CreateBlockchains(ctx,
resp, err := cli.CreateChains(ctx,
[]*rpcpb.BlockchainSpec{
{
VmName: "subnetevm",
Genesis: genesisContents,
SubnetSpec: &rpcpb.SubnetSpec{Participants: subnetParticipants2},
ParticipantsSpec: &rpcpb.ParticipantsSpec{Participants: subnetParticipants2},
},
},
)
@@ -389,7 +392,7 @@ var _ = ginkgo.Describe("[Start/Remove/Restart/Add/Stop]", func() {
ginkgo.By("can create two blockchains at a time", func() {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
resp, err := cli.CreateBlockchains(ctx,
resp, err := cli.CreateChains(ctx,
[]*rpcpb.BlockchainSpec{
{
VmName: "subnetevm",
@@ -416,17 +419,17 @@ var _ = ginkgo.Describe("[Start/Remove/Restart/Add/Stop]", func() {
cancel()
// create blockchains
ctx, cancel = context.WithTimeout(context.Background(), 3*time.Minute)
resp, err := cli.CreateBlockchains(ctx,
resp, err := cli.CreateChains(ctx,
[]*rpcpb.BlockchainSpec{
{
VmName: "subnetevm",
Genesis: genesisContents,
SubnetSpec: &rpcpb.SubnetSpec{Participants: disjointNewSubnetParticipants[0]},
ParticipantsSpec: &rpcpb.ParticipantsSpec{Participants: disjointNewSubnetParticipants[0]},
},
{
VmName: "subnetevm",
Genesis: genesisContents,
SubnetSpec: &rpcpb.SubnetSpec{Participants: disjointNewSubnetParticipants[1]},
ParticipantsSpec: &rpcpb.ParticipantsSpec{Participants: disjointNewSubnetParticipants[1]},
},
},
)
@@ -516,7 +519,7 @@ var _ = ginkgo.Describe("[Start/Remove/Restart/Add/Stop]", func() {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
_, err := cli.Start(ctx, execPath1,
client.WithPluginDir(os.TempDir()),
client.WithBlockchainSpecs([]*rpcpb.BlockchainSpec{
client.WithChainSpecs([]*rpcpb.BlockchainSpec{
{
VmName: "invalid",
},
@@ -535,7 +538,7 @@ var _ = ginkgo.Describe("[Start/Remove/Restart/Add/Stop]", func() {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
_, err = cli.Start(ctx, execPath1,
client.WithPluginDir(filepath.Dir(filePath)),
client.WithBlockchainSpecs([]*rpcpb.BlockchainSpec{
client.WithChainSpecs([]*rpcpb.BlockchainSpec{
{
VmName: filepath.Base(filePath),
},
@@ -548,12 +551,12 @@ var _ = ginkgo.Describe("[Start/Remove/Restart/Add/Stop]", func() {
})
ginkgo.By("calling start API with the valid binary path", func() {
ux.Print(log, logging.Green.Wrap("sending 'start' with the valid binary path: %s"), execPath1)
ux.Print(log, luxlog.Green.Wrap("sending 'start' with the valid binary path: %s"), execPath1)
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
resp, err := cli.Start(ctx, execPath1)
cancel()
gomega.Ω(err).Should(gomega.BeNil())
ux.Print(log, logging.Green.Wrap("successfully started, node-names: %s"), resp.ClusterInfo.NodeNames)
ux.Print(log, luxlog.Green.Wrap("successfully started, node-names: %s"), resp.ClusterInfo.NodeNames)
})
})
@@ -569,7 +572,7 @@ var _ = ginkgo.Describe("[Start/Remove/Restart/Add/Stop]", func() {
uris, err := cli.URIs(ctx)
cancel()
gomega.Ω(err).Should(gomega.BeNil())
ux.Print(log, logging.Blue.Wrap("URIs: %s"), uris)
ux.Print(log, luxlog.Blue.Wrap("URIs: %s"), uris)
})
ginkgo.It("can fetch status", func() {
@@ -589,7 +592,7 @@ var _ = ginkgo.Describe("[Start/Remove/Restart/Add/Stop]", func() {
ch, err := cli.StreamStatus(ctx, 5*time.Second)
gomega.Ω(err).Should(gomega.BeNil())
for info := range ch {
ux.Print(log, logging.Green.Wrap("fetched info, node-names: %s"), info.NodeNames)
ux.Print(log, luxlog.Green.Wrap("fetched info, node-names: %s"), info.NodeNames)
if info.Healthy {
break
}
@@ -602,7 +605,7 @@ var _ = ginkgo.Describe("[Start/Remove/Restart/Add/Stop]", func() {
resp, err := cli.RemoveNode(ctx, "node5")
cancel()
gomega.Ω(err).Should(gomega.BeNil())
ux.Print(log, logging.Green.Wrap("successfully removed, node-names: %s"), resp.ClusterInfo.NodeNames)
ux.Print(log, luxlog.Green.Wrap("successfully removed, node-names: %s"), resp.ClusterInfo.NodeNames)
})
})
@@ -612,7 +615,7 @@ var _ = ginkgo.Describe("[Start/Remove/Restart/Add/Stop]", func() {
resp, err := cli.RestartNode(ctx, "node4", client.WithExecPath(execPath2))
cancel()
gomega.Ω(err).Should(gomega.BeNil())
ux.Print(log, logging.Green.Wrap("successfully restarted, node-names: %s"), resp.ClusterInfo.NodeNames)
ux.Print(log, luxlog.Green.Wrap("successfully restarted, node-names: %s"), resp.ClusterInfo.NodeNames)
})
})
@@ -625,10 +628,10 @@ var _ = ginkgo.Describe("[Start/Remove/Restart/Add/Stop]", func() {
v, ok := resp.ClusterInfo.AttachedPeerInfos["node1"]
gomega.Ω(ok).Should(gomega.BeTrue())
ux.Print(log, logging.Green.Wrap("successfully attached peer, peers: %+v"), v.Peers)
ux.Print(log, luxlog.Green.Wrap("successfully attached peer, peers: %+v"), v.Peers)
mc, err := message.NewCreator(
logging.NoLog{},
luxlog.NoLog{},
metrics.NewRegistry(),
"",
luxd_constants.DefaultNetworkCompressionType,
@@ -656,38 +659,38 @@ var _ = ginkgo.Describe("[Start/Remove/Restart/Add/Stop]", func() {
ginkgo.It("can add a node", func() {
ginkgo.By("calling AddNode", func() {
ux.Print(log, logging.Green.Wrap("calling 'add-node' with the valid binary path: %s"), execPath1)
ux.Print(log, luxlog.Green.Wrap("calling 'add-node' with the valid binary path: %s"), execPath1)
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
resp, err := cli.AddNode(ctx, newNodeName, execPath1)
cancel()
gomega.Ω(err).Should(gomega.BeNil())
ux.Print(log, logging.Green.Wrap("successfully started, node-names: %s"), resp.ClusterInfo.NodeNames)
ux.Print(log, luxlog.Green.Wrap("successfully started, node-names: %s"), resp.ClusterInfo.NodeNames)
})
ginkgo.By("calling AddNode with existing node name, should fail", func() {
ux.Print(log, logging.Green.Wrap("calling 'add-node' with the valid binary path: %s"), execPath1)
ux.Print(log, luxlog.Green.Wrap("calling 'add-node' with the valid binary path: %s"), execPath1)
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
resp, err := cli.AddNode(ctx, newNodeName, execPath1)
cancel()
gomega.Ω(err.Error()).Should(gomega.ContainSubstring("repeated node name"))
gomega.Ω(resp).Should(gomega.BeNil())
ux.Print(log, logging.Green.Wrap("'add-node' failed as expected"))
ux.Print(log, luxlog.Green.Wrap("'add-node' failed as expected"))
})
})
ginkgo.It("can start with custom config", func() {
ginkgo.By("stopping network first", func() {
ux.Print(log, logging.Red.Wrap("shutting down cluster"))
ux.Print(log, luxlog.Red.Wrap("shutting down cluster"))
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
_, err := cli.Stop(ctx)
cancel()
gomega.Ω(err).Should(gomega.BeNil())
ux.Print(log, logging.Red.Wrap("shutting down client"))
ux.Print(log, luxlog.Red.Wrap("shutting down client"))
gomega.Ω(err).Should(gomega.BeNil())
})
ginkgo.By("calling start API with custom config", func() {
ux.Print(log, logging.Green.Wrap("sending 'start' with the valid binary path: %s"), execPath1)
ux.Print(log, luxlog.Green.Wrap("sending 'start' with the valid binary path: %s"), execPath1)
opts := []client.OpOption{
client.WithNumNodes(numNodes),
client.WithCustomNodeConfigs(customNodeConfigs),
@@ -696,7 +699,7 @@ var _ = ginkgo.Describe("[Start/Remove/Restart/Add/Stop]", func() {
resp, err := cli.Start(ctx, execPath1, opts...)
cancel()
gomega.Ω(err).Should(gomega.BeNil())
ux.Print(log, logging.Green.Wrap("successfully started, node-names: %s"), resp.ClusterInfo.NodeNames)
ux.Print(log, luxlog.Green.Wrap("successfully started, node-names: %s"), resp.ClusterInfo.NodeNames)
})
ginkgo.By("can wait for health", func() {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
@@ -705,15 +708,15 @@ var _ = ginkgo.Describe("[Start/Remove/Restart/Add/Stop]", func() {
gomega.Ω(err).Should(gomega.BeNil())
})
ginkgo.By("overrides num-nodes", func() {
ux.Print(log, logging.Green.Wrap("checking that given num-nodes %d have been overridden by custom configs: %d"), numNodes, len(customNodeConfigs))
ux.Print(log, luxlog.Green.Wrap("checking that given num-nodes %d have been overridden by custom configs: %d"), numNodes, len(customNodeConfigs))
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
uris, err := cli.URIs(ctx)
cancel()
gomega.Ω(err).Should(gomega.BeNil())
gomega.Ω(uris).Should(gomega.HaveLen(len(customNodeConfigs)))
ux.Print(log, logging.Green.Wrap("expected number of nodes up: %d"), len(customNodeConfigs))
ux.Print(log, luxlog.Green.Wrap("expected number of nodes up: %d"), len(customNodeConfigs))
ux.Print(log, logging.Green.Wrap("checking correct admin APIs are enabled resp. disabled"))
ux.Print(log, luxlog.Green.Wrap("checking correct admin APIs are enabled resp. disabled"))
// we have 7 nodes, 3 have the admin API enabled, the other 4 disabled
// therefore we expect exactly 4 calls to fail and exactly 3 to succeed.
ctx, cancel = context.WithTimeout(context.Background(), 15*time.Second)
@@ -752,12 +755,12 @@ var _ = ginkgo.Describe("[Start/Remove/Restart/Add/Stop]", func() {
})
ginkgo.It("can pause a node", func() {
ginkgo.By("calling PauseNode", func() {
ux.Print(log, logging.Green.Wrap("calling 'pause-node'"))
ux.Print(log, luxlog.Green.Wrap("calling 'pause-node'"))
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
resp, err := cli.PauseNode(ctx, pausedNodeName)
cancel()
gomega.Ω(err).Should(gomega.BeNil())
ux.Print(log, logging.Green.Wrap("successfully paused, node-names: %s"), resp.ClusterInfo.NodeNames)
ux.Print(log, luxlog.Green.Wrap("successfully paused, node-names: %s"), resp.ClusterInfo.NodeNames)
})
ginkgo.By("can wait for health", func() {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
@@ -777,7 +780,7 @@ var _ = ginkgo.Describe("[Start/Remove/Restart/Add/Stop]", func() {
resp, err := cli.ResumeNode(ctx, pausedNodeName)
cancel()
gomega.Ω(err).Should(gomega.BeNil())
ux.Print(log, logging.Green.Wrap("successfully resumed %s, cluster node-names: %s"), "node1", resp.ClusterInfo.NodeNames)
ux.Print(log, luxlog.Green.Wrap("successfully resumed %s, cluster node-names: %s"), "node1", resp.ClusterInfo.NodeNames)
})
ginkgo.By("can wait for health", func() {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
@@ -796,7 +799,7 @@ var _ = ginkgo.Describe("[Start/Remove/Restart/Add/Stop]", func() {
ginkgo.It("can add primary validator with BLS Keys", func() {
ginkgo.By("calling AddNode", func() {
ux.Print(log, logging.Green.Wrap("calling 'add-node' with the valid binary path: %s"), execPath1)
ux.Print(log, luxlog.Green.Wrap("calling 'add-node' with the valid binary path: %s"), execPath1)
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
resp, err := cli.AddNode(ctx, newNodeName2, execPath1)
cancel()
@@ -806,7 +809,7 @@ var _ = ginkgo.Describe("[Start/Remove/Restart/Add/Stop]", func() {
newNode2NodeID = nodeInfo.Id
}
}
ux.Print(log, logging.Green.Wrap("successfully started, node-names: %s"), resp.ClusterInfo.NodeNames)
ux.Print(log, luxlog.Green.Wrap("successfully started, node-names: %s"), resp.ClusterInfo.NodeNames)
})
ginkgo.By("can wait for health", func() {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
@@ -816,7 +819,7 @@ var _ = ginkgo.Describe("[Start/Remove/Restart/Add/Stop]", func() {
})
ginkgo.By("add 1 subnet", func() {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
resp, err := cli.CreateSubnets(ctx, []*rpcpb.SubnetSpec{{}})
resp, err := cli.CreateParticipantGroups(ctx, []*rpcpb.ParticipantsSpec{{}})
cancel()
gomega.Ω(err).Should(gomega.BeNil())
gomega.Ω(len(resp.SubnetIds)).Should(gomega.Equal(1))
@@ -847,7 +850,7 @@ var _ = ginkgo.Describe("[Start/Remove/Restart/Add/Stop]", func() {
ginkgo.It("subnet creation", func() {
ginkgo.By("add 1 subnet", func() {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
_, err := cli.CreateSubnets(ctx, []*rpcpb.SubnetSpec{{}})
_, err := cli.CreateParticipantGroups(ctx, []*rpcpb.ParticipantsSpec{{}})
cancel()
gomega.Ω(err).Should(gomega.BeNil())
})
@@ -861,7 +864,7 @@ var _ = ginkgo.Describe("[Start/Remove/Restart/Add/Stop]", func() {
})
ginkgo.By("add 1 subnet with participants", func() {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
response, err := cli.CreateSubnets(ctx, []*rpcpb.SubnetSpec{{Participants: subnetParticipants}})
response, err := cli.CreateParticipantGroups(ctx, []*rpcpb.ParticipantsSpec{{Participants: subnetParticipants}})
cancel()
gomega.Ω(err).Should(gomega.BeNil())
gomega.Ω(len(response.SubnetIds)).Should(gomega.Equal(1))
@@ -877,7 +880,7 @@ var _ = ginkgo.Describe("[Start/Remove/Restart/Add/Stop]", func() {
})
ginkgo.By("add 1 subnet with node not currently added", func() {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
response, err := cli.CreateSubnets(ctx, []*rpcpb.SubnetSpec{{Participants: subnetParticipants2}})
response, err := cli.CreateParticipantGroups(ctx, []*rpcpb.ParticipantsSpec{{Participants: subnetParticipants2}})
cancel()
gomega.Ω(err).Should(gomega.BeNil())
// verify that a new node is added to cluster
@@ -888,13 +891,13 @@ var _ = ginkgo.Describe("[Start/Remove/Restart/Add/Stop]", func() {
newSubnetID = response.SubnetIds[0]
})
ginkgo.By("calling AddNode with existing node name, should fail", func() {
ux.Print(log, logging.Green.Wrap("calling 'add-node' with the valid binary path: %s"), execPath1)
ux.Print(log, luxlog.Green.Wrap("calling 'add-node' with the valid binary path: %s"), execPath1)
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
resp, err := cli.AddNode(ctx, newParticipantNode, execPath1)
cancel()
gomega.Ω(err.Error()).Should(gomega.ContainSubstring("repeated node name"))
gomega.Ω(resp).Should(gomega.BeNil())
ux.Print(log, logging.Green.Wrap("'add-node' failed as expected"))
ux.Print(log, luxlog.Green.Wrap("'add-node' failed as expected"))
})
ginkgo.By("verify the newer subnet also has correct participants", func() {
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
@@ -910,11 +913,11 @@ var _ = ginkgo.Describe("[Start/Remove/Restart/Add/Stop]", func() {
ginkgo.By("removing a subnet validator", func() {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
defer cancel()
testRemoveSubnetValidatorConfig := rpcpb.RemoveSubnetValidatorSpec{
testRemoveSubnetValidatorConfig := rpcpb.RemoveChainValidatorSpec{
NodeNames: []string{"node2"},
SubnetId: newSubnetID,
}
_, err := cli.RemoveSubnetValidator(ctx, []*rpcpb.RemoveSubnetValidatorSpec{&testRemoveSubnetValidatorConfig})
_, err := cli.RemoveSubnetValidator(ctx, []*rpcpb.RemoveChainValidatorSpec{&testRemoveSubnetValidatorConfig})
gomega.Ω(err).Should(gomega.BeNil())
})
ginkgo.By("verify there are only two validators left for subnet", func() {
@@ -940,7 +943,7 @@ var _ = ginkgo.Describe("[Start/Remove/Restart/Add/Stop]", func() {
var elasticSubnetID string
ginkgo.By("add 1 subnet", func() {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
resp, err := cli.CreateSubnets(ctx, []*rpcpb.SubnetSpec{{}})
resp, err := cli.CreateParticipantGroups(ctx, []*rpcpb.ParticipantsSpec{{}})
cancel()
gomega.Ω(err).Should(gomega.BeNil())
gomega.Ω(len(resp.SubnetIds)).Should(gomega.Equal(1))
@@ -954,13 +957,13 @@ var _ = ginkgo.Describe("[Start/Remove/Restart/Add/Stop]", func() {
gomega.Ω(err).Should(gomega.BeNil())
subnetInfo := status.ClusterInfo.Subnets[createdSubnetID]
gomega.Ω(subnetInfo.IsElastic).Should(gomega.Equal(false))
gomega.Ω(subnetInfo.ElasticSubnetId).Should(gomega.Equal(ids.Empty.String()))
gomega.Ω(subnetInfo.ElasticChainId).Should(gomega.Equal(ids.Empty.String()))
})
ginkgo.By("transform 1 subnet to elastic subnet", func() {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
defer cancel()
testElasticSubnetConfig.SubnetId = createdSubnetID
response, err := cli.TransformElasticSubnets(ctx, []*rpcpb.ElasticSubnetSpec{&testElasticSubnetConfig})
response, err := cli.TransformElasticSubnets(ctx, []*rpcpb.ElasticParticipantsSpec{&testElasticSubnetConfig})
gomega.Ω(err).Should(gomega.BeNil())
gomega.Ω(len(response.TxIds)).Should(gomega.Equal(1))
gomega.Ω(len(response.AssetIds)).Should(gomega.Equal(1))
@@ -973,14 +976,14 @@ var _ = ginkgo.Describe("[Start/Remove/Restart/Add/Stop]", func() {
gomega.Ω(err).Should(gomega.BeNil())
subnetInfo := status.ClusterInfo.Subnets[createdSubnetID]
gomega.Ω(subnetInfo.IsElastic).Should(gomega.Equal(true))
gomega.Ω(subnetInfo.ElasticSubnetId).ShouldNot(gomega.Equal(ids.Empty.String()))
elasticSubnetID = subnetInfo.ElasticSubnetId
gomega.Ω(subnetInfo.ElasticChainId).ShouldNot(gomega.Equal(ids.Empty.String()))
elasticSubnetID = subnetInfo.ElasticChainId
})
ginkgo.By("transforming a subnet with same subnetID to elastic subnet will fail", func() {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
defer cancel()
testElasticSubnetConfig.SubnetId = createdSubnetID
_, err := cli.TransformElasticSubnets(ctx, []*rpcpb.ElasticSubnetSpec{&testElasticSubnetConfig})
_, err := cli.TransformElasticSubnets(ctx, []*rpcpb.ElasticParticipantsSpec{&testElasticSubnetConfig})
gomega.Ω(err).Should(gomega.HaveOccurred())
})
ginkgo.By("save snapshot with elastic info", func() {
@@ -1002,7 +1005,7 @@ var _ = ginkgo.Describe("[Start/Remove/Restart/Add/Stop]", func() {
gomega.Ω(err).Should(gomega.BeNil())
subnetInfo := status.ClusterInfo.Subnets[createdSubnetID]
gomega.Ω(subnetInfo.IsElastic).Should(gomega.Equal(true))
gomega.Ω(subnetInfo.ElasticSubnetId).Should(gomega.Equal(elasticSubnetID))
gomega.Ω(subnetInfo.ElasticChainId).Should(gomega.Equal(elasticSubnetID))
})
ginkgo.By("remove snapshot with elastic info", func() {
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
+1 -1
View File
@@ -1,5 +1,5 @@
// Copyright (C) 2021-2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// SPDX-License-Identifier: BSD-3-Clause
package constants
import "path/filepath"
+2 -2
View File
@@ -1,12 +1,12 @@
// Copyright (C) 2021-2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// SPDX-License-Identifier: BSD-3-Clause
package utils
import "encoding/json"
// Set k=v in JSON string
// e.g., "track-subnets" is the key and value is "a,b,c".
// e.g., "track-chains" is the key and value is "a,b,c".
func SetJSONKey(jsonBody string, k string, v string) (string, error) {
var config map[string]interface{}
+1 -1
View File
@@ -1,5 +1,5 @@
// Copyright (C) 2021-2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// SPDX-License-Identifier: BSD-3-Clause
package utils
+11 -11
View File
@@ -97,34 +97,34 @@ func MkDirWithTimestamp(dirPrefix string) (string, error) {
return dirName, os.MkdirAll(dirName, os.ModePerm)
}
func VerifySubnetHasCorrectParticipants(
func VerifyChainHasCorrectParticipants(
log luxlog.Logger,
subnetParticipants []string,
chainParticipants []string,
cluster *rpcb.ClusterInfo,
subnetID string,
chainID string,
) bool {
if cluster != nil {
participatingNodeNames := cluster.Subnets[subnetID].GetSubnetParticipants().GetNodeNames()
participatingNodeNames := cluster.Chains[chainID].GetChainParticipants().GetNodeNames()
var nodeIsInList bool
// Check that all subnet validators are equal to the node IDs added as participant in subnet creation
for _, node := range subnetParticipants {
// Check that all chain validators are equal to the node IDs added as participant in chain creation
for _, node := range chainParticipants {
nodeIsInList = false
for _, subnetValidator := range participatingNodeNames {
if subnetValidator == node {
for _, chainValidator := range participatingNodeNames {
if chainValidator == node {
nodeIsInList = true
break
}
}
if !nodeIsInList {
ux.Print(log, luxlog.Red.Wrap(fmt.Sprintf("VerifySubnetHasCorrectParticipants: %#v", cluster)))
ux.Print(log, luxlog.Red.Wrap(fmt.Sprintf("VerifySubnetHasCorrectParticipants: node not in list subnet %q node %q %v %v", subnetID, node, subnetParticipants, participatingNodeNames)))
ux.Print(log, "%s", luxlog.Red.Wrap(fmt.Sprintf("VerifyChainHasCorrectParticipants: %#v", cluster)))
ux.Print(log, "%s", luxlog.Red.Wrap(fmt.Sprintf("VerifyChainHasCorrectParticipants: node not in list chain %q node %q %v %v", chainID, node, chainParticipants, participatingNodeNames)))
return false
}
}
return true
} else {
ux.Print(log, luxlog.Red.Wrap("VerifySubnetHasCorrectParticipants: cluster is nil"))
ux.Print(log, "%s", luxlog.Red.Wrap("VerifyChainHasCorrectParticipants: cluster is nil"))
}
return false
}
+2 -1
View File
@@ -1,5 +1,5 @@
// Copyright (C) 2021-2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// SPDX-License-Identifier: BSD-3-Clause
package ux
import (
@@ -8,6 +8,7 @@ import (
luxlog "github.com/luxfi/log"
)
//nolint:govet // msg is intentionally a format string that may come from variables
func Print(log luxlog.Logger, msg string, args ...interface{}) {
fmtMsg := fmt.Sprintf(msg, args...)
log.Info(fmtMsg)