Files
zeekay fce1de5227 docs: replace subnet terminology with L2/chain
Update prose descriptions in README and LLM.md to use "chain" or "L2"
instead of "subnet". Preserves API endpoint paths, JSON field names,
CLI subcommand names, genesis config keys, Go struct fields, and
deprecatedFlagsSupport.json backward compatibility mappings.
2026-03-29 10:56:02 -07:00

159 lines
5.6 KiB
Go

package node
import (
"context"
"encoding/json"
"errors"
"fmt"
"github.com/luxfi/config"
"github.com/luxfi/ids"
"github.com/luxfi/netrunner/api"
"github.com/luxfi/netrunner/network/node/status"
"github.com/luxfi/p2p/peer"
)
// Node represents an Lux node
type Node interface {
// Return this node's name, which is unique
// across all the nodes in its network.
GetName() string
// Return this node's Lux node ID.
GetNodeID() ids.NodeID
// Return a client that can be used to make API calls.
GetAPIClient() api.Client
// Return this node's host/IP (e.g. 127.0.0.1).
GetHost() string
// Return this node's full HTTP API URL (e.g. http://127.0.0.1:9630).
GetURL() string
// Return this node's P2P (staking) port.
GetP2PPort() uint16
// Return this node's HTTP API port.
GetAPIPort() uint16
// Starts a new test peer, connects it to the given node, and returns the peer.
// [handler] defines how the test peer handles messages it receives.
// The test peer can be used to send messages to the node it's attached to.
// It's left to the caller to maintain a reference to the returned peer.
// The caller should call StartClose() on the peer when they're done with it.
AttachPeer(ctx context.Context, handler peer.InboundHandler) (peer.Peer, error)
// Sends a message from the attached peer to the node
SendOutboundMessage(ctx context.Context, peerID string, content []byte, op uint32) (bool, error)
// Return the state of the node process
Status() status.Status
// Return this node's node binary path
GetBinaryPath() string
// Return this node's data dir
GetDataDir() string
// Return this node's db dir
GetDbDir() string
// Return this node's logs dir
GetLogsDir() string
// Return this node's plugin dir
GetPluginDir() string
// Return this node's config file contents
GetConfigFile() string
// Return this node's config
GetConfig() Config
// Return this node's flag value
GetFlag(string) (string, error)
// Return this node's paused status
GetPaused() bool
}
// Config encapsulates an node configuration
type Config struct {
// A node's name must be unique from all other nodes
// in a network. If Name is the empty string, a
// unique name is assigned on node creation.
Name string `json:"name"`
// True if other nodes should use this node
// as a bootstrap beacon.
IsBeacon bool `json:"isBeacon"`
// Must not be nil.
StakingKey string `json:"stakingKey"`
// Must not be nil.
StakingCert string `json:"stakingCert"`
// Must not be nil.
StakingSigningKey string `json:"stakingSigningKey"`
// May be nil.
ConfigFile string `json:"configFile"`
// May be nil.
ChainConfigFiles map[string]string `json:"chainConfigFiles"`
// May be nil. Genesis files for EVM chains (genesis.json)
GenesisConfigFiles map[string]string `json:"genesisConfigFiles"`
// May be nil.
UpgradeConfigFiles map[string]string `json:"upgradeConfigFiles"`
// May be nil. P-Chain 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. Flags defined in node.Config (this struct) override
// 2. Flags defined in network.Config override
// 3. Flags defined in the json config file
Flags map[string]interface{} `json:"flags"`
// What type of node this is
BinaryPath string `json:"binaryPath"`
// If non-nil, direct this node's Stdout to os.Stdout
RedirectStdout bool `json:"redirectStdout"`
// If non-nil, direct this node's Stderr to os.Stderr
RedirectStderr bool `json:"redirectStderr"`
}
// Validate returns an error if this config is invalid
func (c *Config) Validate(expectedNetworkID uint32) error {
switch {
case c.StakingKey == "":
return errors.New("staking key not given")
case c.StakingCert == "":
return errors.New("staking cert not given")
default:
return validateConfigFile([]byte(c.ConfigFile), expectedNetworkID)
}
}
// Returns an error if config file [configFile] is invalid.
// If len([configFile]) == 0, returns nil.
func validateConfigFile(configFile []byte, expectedNetworkID uint32) error {
if len(configFile) == 0 {
// No config file given. Skip.
return nil
}
// Validate that values given in the config file,
// if any, are the correct type.
var configMap map[string]interface{}
if err := json.Unmarshal(configFile, &configMap); err != nil {
return fmt.Errorf("could not unmarshal config file: %w", err)
}
if networkIDIntf, ok := configMap[config.NetworkIDKey]; ok {
networkID, ok := networkIDIntf.(float64)
if !ok {
return fmt.Errorf("wrong type for field %q in config expected float64 got %T", config.NetworkIDKey, networkIDIntf)
}
if uint32(networkID) != expectedNetworkID {
return fmt.Errorf("config file network id %d differs from genesis network id %d", int(networkID), expectedNetworkID)
}
}
if dbPathIntf, ok := configMap[config.DBPathKey]; ok {
if _, ok := dbPathIntf.(string); !ok {
return fmt.Errorf("wrong type for field %q in config expected string got %T", config.DBPathKey, dbPathIntf)
}
}
if logsDirIntf, ok := configMap[config.LogsDirKey]; ok {
if _, ok := logsDirIntf.(string); !ok {
return fmt.Errorf("wrong type for field %q in config expected string got %T", config.LogsDirKey, logsDirIntf)
}
}
if apiPortIntf, ok := configMap[config.HTTPPortKey]; ok {
if _, ok := apiPortIntf.(float64); !ok {
return fmt.Errorf("wrong type for field %q in config expected float64 got %T", config.HTTPPortKey, apiPortIntf)
}
}
if p2pPortIntf, ok := configMap[config.StakingPortKey]; ok {
if _, ok := p2pPortIntf.(float64); !ok {
return fmt.Errorf("wrong type for field %q in config expected float64 got %T", config.StakingPortKey, p2pPortIntf)
}
}
return nil
}