From 4e2a4e432e0ab41afc9c82a5575116cbdb0f1931 Mon Sep 17 00:00:00 2001 From: Zach Kelling Date: Sat, 3 Jan 2026 07:05:17 -0800 Subject: [PATCH] chore: remove local replace directives and update dependencies MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Update github.com/luxfi/keys v1.0.5 โ†’ v1.0.6 - Update github.com/luxfi/genesis v1.5.18 โ†’ v1.5.19 - Remove local replace directives for keys and genesis - Use published package versions for reproducible builds --- .golangci.yml | 139 +++++++------------------ api/client.go | 2 +- api/eth_client_stub.go | 2 +- api/mocks/EthClient.go | 1 - api/mocks/client.go | 2 +- client/client.go | 42 ++++---- cmd/check-last-block/main.go | 4 +- cmd/control/control.go | 12 +-- cmd/genkeys/main.go | 12 ++- cmd/netrunner/commands/bridge.go | 27 ++--- cmd/netrunner/commands/engine.go | 80 +++++++------- cmd/netrunner/commands/logs.go | 17 +-- cmd/netrunner/commands/stack.go | 133 +++++++++++------------ cmd/netrunner/commands/status.go | 13 +-- cmd/netrunner/commands/test.go | 57 +++++----- cmd/netrunner/main.go | 2 +- cmd/ping/ping.go | 4 +- cmd/server/server.go | 4 +- engines/avalanche/engine.go | 108 +++++++++---------- engines/engine_test.go | 32 +++--- engines/eth2.go | 76 +++++++------- engines/geth/engine.go | 58 +++++------ engines/lux/engine.go | 56 +++++----- engines/opstack.go | 38 +++---- engines/types.go | 18 ++-- examples/local/fivenodenetwork/main.go | 4 +- examples/local/indepth/main.go | 4 +- examples/local/l2chains/main.go | 10 +- go.mod | 4 +- go.sum | 8 +- local/blockchain.go | 55 +++++----- local/genesis_config.go | 105 +++++++++++++------ local/helpers.go | 2 +- local/mainnet_test.go | 2 +- local/mnemonic_test.go | 130 +++++++++++------------ local/network.go | 45 ++++---- local/network_test.go | 28 ++--- local/node.go | 16 +-- local/node_process.go | 10 +- local/node_test.go | 8 +- local/snapshot.go | 7 +- local/wallet_client.go | 6 +- multinet/manager.go | 60 +++++------ network/config.go | 9 +- network/network.go | 2 +- network/node/node.go | 2 +- orchestrator/host.go | 92 ++++++++-------- orchestrator/host_test.go | 4 +- orchestrator/indexer.go | 14 +-- orchestrator/manifest.go | 68 ++++++------ orchestrator/metrics.go | 24 ++--- orchestrator/ports.go | 24 ++--- rpcpb/rpc.pb.go | 7 +- rpcpb/rpc_grpc.pb.go | 1 + server/network.go | 43 ++++---- server/server.go | 43 ++++---- tests/e2e/e2e_test.go | 31 +++--- utils/constants/constants.go | 14 +-- utils/utils.go | 4 +- 59 files changed, 909 insertions(+), 916 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index b0f0551c..58cabf1f 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,113 +1,50 @@ -# https://golangci-lint.run/usage/configuration/ +# golangci-lint v2 configuration +version: "2" + run: timeout: 10m - # skip auto-generated files. - skip-files: - - ".*\\.pb\\.go$" - - ".*mock.*" -issues: - # Maximum count of issues with the same text. Set to 0 to disable. Default is 3. - max-same-issues: 0 +formatters: + enable: + - gofmt + - goimports linters: - # please, do not use `enable-all`: it's deprecated and will be removed soon. - # inverted configuration with `enable-all` and `disable` is not scalable during updates of golangci-lint - disable-all: true + default: none enable: - - asciicheck - - depguard - errcheck - - errorlint - - exportloopref - - goconst - - gocritic - - gofmt - - gofumpt - - goimports - - revive - - gosec - - gosimple - govet - ineffassign - - misspell - - nakedret - - nolintlint - - prealloc - - stylecheck - - unconvert - - unparam - - unused - - unconvert - - whitespace - staticcheck - # - bodyclose - # - structcheck - # - lll - # - gomnd - # - goprintffuncname - # - interfacer - # - typecheck - # - goerr113 - # - noctx + - misspell -linters-settings: - errorlint: - # Check for plain type assertions and type switches. - asserts: false - # Check for plain error comparisons. - comparison: false - revive: + settings: + staticcheck: + checks: + - "all" + - "-SA6002" + - "-SA1019" + - "-ST1000" + - "-ST1021" + - "-QF1008" + + exclusions: + generated: lax + presets: + - comments rules: - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#bool-literal-in-expr - - name: bool-literal-in-expr - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#early-return - - name: early-return - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#empty-lines - - name: empty-lines - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#struct-tag - - name: struct-tag - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unexported-naming - - name: unexported-naming - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unhandled-error - - name: unhandled-error - disabled: false - arguments: - - "fmt.Fprint" - - "fmt.Fprintf" - - "fmt.Print" - - "fmt.Printf" - - "fmt.Println" - - "rand.Read" - - "sb.WriteString" - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unused-parameter - - name: unused-parameter - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unused-receiver - - name: unused-receiver - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#useless-break - - name: useless-break - disabled: false - staticcheck: - go: "1.19" - # https://staticcheck.io/docs/options#checks - checks: - - "all" - - "-SA6002" # argument should be pointer-like to avoid allocation, for sync.Pool - - "-SA1019" # deprecated packages e.g., golang.org/x/crypto/ripemd160 - # https://golangci-lint.run/usage/linters#gosec - gosec: - excludes: - - G107 # https://securego.io/docs/rules/g107.html - depguard: - list-type: blacklist - packages-with-error-message: - - io/ioutil: 'io/ioutil is deprecated. Use package io or os instead.' - - github.com/stretchr/testify/assert: 'github.com/stretchr/testify/require should be used instead.' - include-go-root: true + - path: ".*\\.pb\\.go$" + linters: + - all + - path: ".*mock.*" + linters: + - all + - path: _test\.go + linters: + - errcheck + - linters: + - errcheck + text: "Error return value of .*(Close|Flush|Fprintln|Fprintf|Print|Printf|Println|Write|Signal|Kill|RemoveAll|Sync|Sscanf|MarkFlagRequired).*is not checked" + +issues: + max-same-issues: 0 diff --git a/api/client.go b/api/client.go index aa2e9f41..0b9ad751 100644 --- a/api/client.go +++ b/api/client.go @@ -68,7 +68,7 @@ type Client interface { PChainAPI() *platformvm.Client XChainAPI() *exchangevm.Client XChainWalletAPI() *exchangevm.WalletClient - CChainAPI() interface{} // evmclient.Client + CChainAPI() interface{} // evmclient.Client CChainEthAPI() EthClient // ethclient websocket wrapper that adds mutexed calls, and lazy conn init (on first call) InfoAPI() *info.Client HealthAPI() HealthClient diff --git a/api/eth_client_stub.go b/api/eth_client_stub.go index cf669c05..bdaf8846 100644 --- a/api/eth_client_stub.go +++ b/api/eth_client_stub.go @@ -131,4 +131,4 @@ func (c *ethClientStub) SuggestGasTipCap(ctx context.Context) (*big.Int, error) // TransactionReceipt stub implementation func (c *ethClientStub) TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) { return nil, fmt.Errorf("TransactionReceipt not implemented in stub") -} \ No newline at end of file +} diff --git a/api/mocks/EthClient.go b/api/mocks/EthClient.go index f3606c80..d28e77d5 100644 --- a/api/mocks/EthClient.go +++ b/api/mocks/EthClient.go @@ -87,7 +87,6 @@ func (_m *EthClient) AcceptedNonceAt(_a0 context.Context, _a1 common.Address) (u return r0, r1 } - // BalanceAt provides a mock function with given fields: _a0, _a1, _a2 func (_m *EthClient) BalanceAt(_a0 context.Context, _a1 common.Address, _a2 *big.Int) (*big.Int, error) { ret := _m.Called(_a0, _a1, _a2) diff --git a/api/mocks/client.go b/api/mocks/client.go index 92a7ab6d..9dc5a202 100644 --- a/api/mocks/client.go +++ b/api/mocks/client.go @@ -178,4 +178,4 @@ func (_m *Client) XChainWalletAPI() *exchangevm.WalletClient { } return r0 -} \ No newline at end of file +} diff --git a/client/client.go b/client/client.go index 216a6d02..a0fe7aff 100644 --- a/client/client.go +++ b/client/client.go @@ -12,9 +12,9 @@ import ( "sync" "time" + "github.com/luxfi/log" "github.com/luxfi/netrunner/local" "github.com/luxfi/netrunner/rpcpb" - "github.com/luxfi/log" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/credentials/insecure" @@ -59,7 +59,7 @@ type Client interface { } type client struct { - cfg Config + cfg Config logger log.Logger conn *grpc.ClientConn @@ -88,7 +88,7 @@ func New(cfg Config, logger log.Logger) (Client, error) { return &client{ cfg: cfg, - logger: logger, + logger: logger, conn: conn, pingc: rpcpb.NewPingServiceClient(conn), controlc: rpcpb.NewControlServiceClient(conn), @@ -114,11 +114,11 @@ func (c *client) Start(ctx context.Context, execPath string, opts ...OpOption) ( ret.applyOpts(opts) req := &rpcpb.StartRequest{ - ExecPath: execPath, - NumNodes: &ret.numNodes, - ChainConfigs: ret.chainConfigs, - UpgradeConfigs: ret.upgradeConfigs, - ChainConfigFiles: ret.chainConfigs, + ExecPath: execPath, + NumNodes: &ret.numNodes, + ChainConfigs: ret.chainConfigs, + UpgradeConfigs: ret.upgradeConfigs, + ChainConfigFiles: ret.chainConfigs, } if ret.trackChains != "" { req.WhitelistedChains = &ret.trackChains @@ -271,12 +271,12 @@ func (c *client) AddNode(ctx context.Context, name string, execPath string, opts ret.applyOpts(opts) req := &rpcpb.AddNodeRequest{ - Name: name, - ExecPath: execPath, - NodeConfig: &ret.globalNodeConfig, - ChainConfigs: ret.chainConfigs, - UpgradeConfigs: ret.upgradeConfigs, - ChainConfigFiles: ret.chainConfigs, + Name: name, + ExecPath: execPath, + NodeConfig: &ret.globalNodeConfig, + ChainConfigs: ret.chainConfigs, + UpgradeConfigs: ret.upgradeConfigs, + ChainConfigFiles: ret.chainConfigs, } if ret.pluginDir != "" { @@ -354,10 +354,10 @@ func (c *client) LoadSnapshot(ctx context.Context, snapshotName string, opts ... ret := &Op{} ret.applyOpts(opts) req := rpcpb.LoadSnapshotRequest{ - SnapshotName: snapshotName, - ChainConfigs: ret.chainConfigs, - UpgradeConfigs: ret.upgradeConfigs, - ChainConfigFiles: ret.chainConfigs, + SnapshotName: snapshotName, + ChainConfigs: ret.chainConfigs, + UpgradeConfigs: ret.upgradeConfigs, + ChainConfigFiles: ret.chainConfigs, } if ret.execPath != "" { req.ExecPath = &ret.execPath @@ -399,13 +399,13 @@ func (c *client) Close() error { type Op struct { numNodes uint32 execPath string - trackChains string + trackChains string globalNodeConfig string rootDataDir string pluginDir string - chainSpecs []*rpcpb.BlockchainSpec + chainSpecs []*rpcpb.BlockchainSpec customNodeConfigs map[string]string - numChains uint32 + numChains uint32 chainConfigs map[string]string upgradeConfigs map[string]string pChainConfigs map[string]string diff --git a/cmd/check-last-block/main.go b/cmd/check-last-block/main.go index 8e044686..187f6321 100644 --- a/cmd/check-last-block/main.go +++ b/cmd/check-last-block/main.go @@ -40,7 +40,7 @@ func main() { return nil }) }) - + if err != nil { fmt.Printf("Error: %v\n", err) } else { @@ -54,7 +54,7 @@ func main() { opts.PrefetchValues = false it := txn.NewIterator(opts) defer it.Close() - + prefix := []byte("n") for it.Seek(prefix); it.ValidForPrefix(prefix); it.Next() { key := it.Item().Key() diff --git a/cmd/control/control.go b/cmd/control/control.go index cd523255..9c2e7a12 100644 --- a/cmd/control/control.go +++ b/cmd/control/control.go @@ -14,14 +14,14 @@ import ( "syscall" "time" + "github.com/luxfi/log" + "github.com/luxfi/log/level" "github.com/luxfi/netrunner/client" "github.com/luxfi/netrunner/local" "github.com/luxfi/netrunner/rpcpb" "github.com/luxfi/netrunner/utils" "github.com/luxfi/netrunner/utils/constants" "github.com/luxfi/netrunner/ux" - "github.com/luxfi/log" - "github.com/luxfi/log/level" "github.com/spf13/cobra" ) @@ -34,11 +34,11 @@ const clientRootDirPrefix = "client" var ( logLevel string logDir string - trackChains string + trackChains string endpoint string dialTimeout time.Duration requestTimeout time.Duration - logger log.Logger + logger log.Logger ) // NOTE: Naming convention for node names is currently `node` + number, i.e. `node1,node2,node3,...node101` @@ -86,12 +86,12 @@ func NewCommand() *cobra.Command { } var ( - luxdBinPath string + luxdBinPath string numNodes uint32 pluginDir string globalNodeConfig string addNodeConfig string - chainSpecsStr string + chainSpecsStr string customNodeConfigs string rootDataDir string chainConfigs string diff --git a/cmd/genkeys/main.go b/cmd/genkeys/main.go index a8cc7772..f5acf31b 100644 --- a/cmd/genkeys/main.go +++ b/cmd/genkeys/main.go @@ -51,14 +51,14 @@ func main() { for i, vk := range validators { name := fmt.Sprintf("node%d", i) - + fmt.Printf("--- Validator %d (%s) ---\n", i, name) fmt.Printf(" NodeID: %s\n", vk.NodeID.String()) fmt.Printf(" P-Chain Addr: %s\n", vk.PChainAddr.String()) fmt.Printf(" C-Chain Addr: %s\n", vk.CChainAddrHex()) fmt.Printf(" BLS PubKey: %s\n", vk.BLSPublicKeyHex()) fmt.Printf(" BLS PoP: %s\n", vk.BLSPoPHex()) - + // Save to disk if err := ks.Save(name, vk); err != nil { fmt.Printf(" ERROR saving: %v\n", err) @@ -117,6 +117,12 @@ func main() { "proofOfPossession": "%s" } }%s -`, vk.NodeID.String(), vk.BLSPublicKeyHex(), vk.BLSPoPHex(), func() string { if i < 4 { return "," } else { return "" } }()) +`, vk.NodeID.String(), vk.BLSPublicKeyHex(), vk.BLSPoPHex(), func() string { + if i < 4 { + return "," + } else { + return "" + } + }()) } } diff --git a/cmd/netrunner/commands/bridge.go b/cmd/netrunner/commands/bridge.go index e9c97cb6..6e8916f9 100644 --- a/cmd/netrunner/commands/bridge.go +++ b/cmd/netrunner/commands/bridge.go @@ -4,9 +4,10 @@ package commands import ( - "github.com/luxfi/log" "fmt" + "github.com/luxfi/log" + "github.com/spf13/cobra" ) @@ -16,13 +17,13 @@ func NewBridgeCmd(logger log.Logger) *cobra.Command { Use: "bridge", Short: "Manage cross-chain bridges", } - + cmd.AddCommand( newBridgeCreateCmd(logger), newBridgeStatusCmd(logger), newBridgeStopCmd(logger), ) - + return cmd } @@ -32,16 +33,16 @@ func newBridgeCreateCmd(logger log.Logger) *cobra.Command { source string dest string ) - + return &cobra.Command{ Use: "create", Short: "Create a bridge between two chains", RunE: func(cmd *cobra.Command, args []string) error { fmt.Printf("๐ŸŒ‰ Creating %s bridge from %s to %s...\n", bridgeType, source, dest) - + // TODO: Implement bridge creation fmt.Println("(Bridge creation not yet implemented)") - + return nil }, } @@ -54,12 +55,12 @@ func newBridgeStatusCmd(logger log.Logger) *cobra.Command { Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { bridgeID := args[0] - + fmt.Printf("๐Ÿ“Š Status of bridge '%s':\n", bridgeID) - + // TODO: Implement bridge status fmt.Println("(Bridge status not yet implemented)") - + return nil }, } @@ -72,13 +73,13 @@ func newBridgeStopCmd(logger log.Logger) *cobra.Command { Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { bridgeID := args[0] - + fmt.Printf("โน Stopping bridge '%s'...\n", bridgeID) - + // TODO: Implement bridge stopping fmt.Println("(Bridge stopping not yet implemented)") - + return nil }, } -} \ No newline at end of file +} diff --git a/cmd/netrunner/commands/engine.go b/cmd/netrunner/commands/engine.go index e6952709..376b0330 100644 --- a/cmd/netrunner/commands/engine.go +++ b/cmd/netrunner/commands/engine.go @@ -4,7 +4,6 @@ package commands import ( - "github.com/luxfi/log" "context" "fmt" "os" @@ -12,8 +11,9 @@ import ( "text/tabwriter" "time" + "github.com/luxfi/log" + "github.com/luxfi/netrunner/engines" - _ "github.com/luxfi/netrunner/engines/lux" _ "github.com/luxfi/netrunner/engines/geth" _ "github.com/luxfi/netrunner/engines/lux" "github.com/spf13/cobra" @@ -48,11 +48,11 @@ func newEngineStartCmd(logger log.Logger) *cobra.Command { dataDir string binary string logLevel string - + // OP Stack specific l1RPC string sequencer bool - + // Eth2 specific consensusClient string executionClient string @@ -65,7 +65,7 @@ func newEngineStartCmd(logger log.Logger) *cobra.Command { Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { name := args[0] - + // Create engine configuration config := &engines.NodeConfig{ NetworkID: networkID, @@ -76,7 +76,7 @@ func newEngineStartCmd(logger log.Logger) *cobra.Command { LogLevel: logLevel, Extra: make(map[string]interface{}), } - + // Add engine-specific configuration switch engineType { case "op": @@ -87,39 +87,39 @@ func newEngineStartCmd(logger log.Logger) *cobra.Command { config.Extra["execution_client"] = executionClient config.Extra["validator_enabled"] = validator } - + // Create the engine engine, err := createEngine(engineType, name, binary) if err != nil { return fmt.Errorf("failed to create engine: %w", err) } - + // Start the engine ctx := cmd.Context() - logger.Info("Starting engine", + logger.Info("Starting engine", log.String("name", name), log.String("type", engineType), log.Uint32("network_id", networkID)) - + if err := engine.Start(ctx, config); err != nil { return fmt.Errorf("failed to start engine: %w", err) } - + // Wait for engine to be healthy logger.Info("Waiting for engine to be healthy...") ticker := time.NewTicker(2 * time.Second) defer ticker.Stop() - + timeout := time.After(2 * time.Minute) for { select { case <-ticker.C: health, err := engine.Health(ctx) if err == nil && health.Healthy { - logger.Info("Engine is healthy", + logger.Info("Engine is healthy", log.String("name", name), log.String("version", health.Version)) - + fmt.Printf("โœ… Engine '%s' started successfully\n", name) fmt.Printf(" Type: %s\n", engineType) fmt.Printf(" RPC: %s\n", engine.RPCEndpoint()) @@ -137,7 +137,7 @@ func newEngineStartCmd(logger log.Logger) *cobra.Command { } }, } - + // General flags cmd.Flags().StringVarP(&engineType, "type", "t", "lux", "Engine type (lux, lux, geth, op, eth2)") cmd.Flags().Uint32Var(&networkID, "network-id", 1337, "Network ID") @@ -147,16 +147,16 @@ func newEngineStartCmd(logger log.Logger) *cobra.Command { cmd.Flags().StringVar(&dataDir, "data-dir", "", "Data directory (auto-generated if empty)") cmd.Flags().StringVar(&binary, "binary", "", "Path to engine binary") cmd.Flags().StringVar(&logLevel, "log-level", "info", "Log level") - + // OP Stack flags cmd.Flags().StringVar(&l1RPC, "l1-rpc", "", "L1 RPC endpoint (OP Stack only)") cmd.Flags().BoolVar(&sequencer, "sequencer", false, "Enable sequencer mode (OP Stack only)") - + // Eth2 flags cmd.Flags().StringVar(&consensusClient, "consensus-client", "lighthouse", "Consensus client (eth2 only)") cmd.Flags().StringVar(&executionClient, "execution-client", "geth", "Execution client (eth2 only)") cmd.Flags().BoolVar(&validator, "validator", false, "Enable validator (eth2 only)") - + return cmd } @@ -167,10 +167,10 @@ func newEngineStopCmd(logger log.Logger) *cobra.Command { Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { name := args[0] - + // TODO: Implement engine registry to track running engines fmt.Printf("โน Stopping engine '%s'...\n", name) - + return nil }, } @@ -183,10 +183,10 @@ func newEngineStatusCmd(logger log.Logger) *cobra.Command { Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { name := args[0] - + // TODO: Implement engine registry fmt.Printf("๐Ÿ“Š Status of engine '%s':\n", name) - + return nil }, } @@ -199,7 +199,7 @@ func newEngineListCmd(logger log.Logger) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { fmt.Println("Available engine types:") fmt.Println() - + w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) fmt.Fprintln(w, "TYPE\tDESCRIPTION\tSTATUS") fmt.Fprintln(w, "----\t-----------\t------") @@ -209,7 +209,7 @@ func newEngineListCmd(logger log.Logger) *cobra.Command { fmt.Fprintln(w, "op\tOP Stack L2\tโœ… Ready") fmt.Fprintln(w, "eth2\tEthereum 2.0\tโœ… Ready") w.Flush() - + return nil }, } @@ -223,7 +223,7 @@ func newEngineTestCmd(logger log.Logger) *cobra.Command { Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { engineType := args[0] - + // Test configuration config := &engines.NodeConfig{ NetworkID: 99999, @@ -234,7 +234,7 @@ func newEngineTestCmd(logger log.Logger) *cobra.Command { LogLevel: "debug", Extra: make(map[string]interface{}), } - + // Add type-specific test config switch engineType { case "op": @@ -246,24 +246,24 @@ func newEngineTestCmd(logger log.Logger) *cobra.Command { config.Extra["execution_client"] = "geth" config.Extra["network_name"] = "testnet" } - + // Create test engine name := fmt.Sprintf("test-%s-%d", engineType, time.Now().Unix()) engine, err := createEngine(engineType, name, "") if err != nil { return fmt.Errorf("failed to create engine: %w", err) } - + // Start engine ctx := cmd.Context() - logger.Info("Starting test engine", + logger.Info("Starting test engine", log.String("type", engineType), log.String("name", name)) - + if err := engine.Start(ctx, config); err != nil { return fmt.Errorf("failed to start engine: %w", err) } - + // Defer cleanup defer func() { logger.Info("Cleaning up test engine") @@ -272,11 +272,11 @@ func newEngineTestCmd(logger log.Logger) *cobra.Command { } os.RemoveAll(config.DataDir) }() - + // Test health check fmt.Printf("๐Ÿงช Testing %s engine...\n", engineType) fmt.Println(" โณ Waiting for engine to be healthy...") - + // Poll for health maxAttempts := 30 for i := 0; i < maxAttempts; i++ { @@ -287,7 +287,7 @@ func newEngineTestCmd(logger log.Logger) *cobra.Command { fmt.Printf(" ๐Ÿ”— RPC: %s\n", engine.RPCEndpoint()) fmt.Printf(" ๐ŸŒ WebSocket: %s\n", engine.WSEndpoint()) fmt.Printf(" โฑ๏ธ Uptime: %s\n", engine.Uptime()) - + // Type-specific tests switch engineType { case "lux": @@ -306,14 +306,14 @@ func newEngineTestCmd(logger log.Logger) *cobra.Command { fmt.Printf(" โš™๏ธ Execution: %s\n", ec) } } - + fmt.Println("\nโœ… Test passed!") return nil } - + time.Sleep(2 * time.Second) } - + return fmt.Errorf("engine failed to become healthy after %d attempts", maxAttempts) }, } @@ -336,7 +336,7 @@ func createEngine(engineType, name, binary string) (engines.Engine, error) { return nil, fmt.Errorf("unknown engine type: %s", engineType) } } - + // For multi-binary engines, validate format if strings.Contains(engineType, "op") || strings.Contains(engineType, "eth2") { parts := strings.Split(binary, ":") @@ -344,7 +344,7 @@ func createEngine(engineType, name, binary string) (engines.Engine, error) { return nil, fmt.Errorf("%s requires two binaries separated by colon (e.g., 'op-node:op-geth')", engineType) } } - + // Create engine using factory return engines.New(engines.EngineType(engineType), name, binary) -} \ No newline at end of file +} diff --git a/cmd/netrunner/commands/logs.go b/cmd/netrunner/commands/logs.go index 5223e485..c3b6e4bb 100644 --- a/cmd/netrunner/commands/logs.go +++ b/cmd/netrunner/commands/logs.go @@ -4,9 +4,10 @@ package commands import ( - "github.com/luxfi/log" "fmt" + "github.com/luxfi/log" + "github.com/spf13/cobra" ) @@ -16,25 +17,25 @@ func NewLogsCmd(logger log.Logger) *cobra.Command { follow bool tail int ) - + cmd := &cobra.Command{ Use: "logs [engine]", Short: "View engine logs", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { engineName := args[0] - + fmt.Printf("๐Ÿ“œ Logs for engine '%s':\n", engineName) - + // TODO: Implement log streaming fmt.Println("(Log streaming not yet implemented)") - + return nil }, } - + cmd.Flags().BoolVarP(&follow, "follow", "f", false, "Follow log output") cmd.Flags().IntVar(&tail, "tail", 100, "Number of lines to show") - + return cmd -} \ No newline at end of file +} diff --git a/cmd/netrunner/commands/stack.go b/cmd/netrunner/commands/stack.go index b781db4e..216f1329 100644 --- a/cmd/netrunner/commands/stack.go +++ b/cmd/netrunner/commands/stack.go @@ -4,13 +4,14 @@ package commands import ( - "github.com/luxfi/log" "fmt" "os" "path/filepath" "text/tabwriter" "time" + "github.com/luxfi/log" + "github.com/luxfi/netrunner/orchestrator" "github.com/spf13/cobra" ) @@ -47,38 +48,38 @@ func newStackDeployCmd(logger log.Logger) *cobra.Command { Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { stackName := args[0] - + // Load manifest manifest, err := orchestrator.LoadManifest(manifestPath) if err != nil { return fmt.Errorf("failed to load manifest: %w", err) } - + // Validate manifest if err := manifest.Validate(); err != nil { return fmt.Errorf("invalid manifest: %w", err) } - + // Create orchestrator host host := orchestrator.NewHost() - + ctx := cmd.Context() - logger.Info("Deploying stack", + logger.Info("Deploying stack", log.String("name", stackName), log.String("manifest", manifestPath), log.Int("engines", len(manifest.Engines))) - + fmt.Printf("๐Ÿš€ Deploying stack '%s' with %d engines...\n", stackName, len(manifest.Engines)) - + // Deploy engines in dependency order deployed := make(map[string]bool) remaining := make([]orchestrator.EngineConfig, len(manifest.Engines)) copy(remaining, manifest.Engines) - + for len(remaining) > 0 { progress := false newRemaining := []orchestrator.EngineConfig{} - + for _, engineCfg := range remaining { // Check if dependencies are satisfied ready := true @@ -88,15 +89,15 @@ func newStackDeployCmd(logger log.Logger) *cobra.Command { break } } - + if !ready { newRemaining = append(newRemaining, engineCfg) continue } - + // Deploy this engine fmt.Printf(" โš™๏ธ Starting %s engine '%s'...\n", engineCfg.Type, engineCfg.Name) - + if err := host.StartEngine(ctx, engineCfg.Name, engineCfg.Type, &orchestrator.EngineOptions{ NetworkID: engineCfg.NetworkID, HTTPPort: engineCfg.HTTPPort, @@ -109,7 +110,7 @@ func newStackDeployCmd(logger log.Logger) *cobra.Command { }); err != nil { return fmt.Errorf("failed to start engine %s: %w", engineCfg.Name, err) } - + // Wait for health if requested if engineCfg.WaitHealthy && wait { fmt.Printf(" โณ Waiting for health check...\n") @@ -118,43 +119,43 @@ func newStackDeployCmd(logger log.Logger) *cobra.Command { } fmt.Printf(" โœ… Healthy!\n") } - + deployed[engineCfg.Name] = true progress = true } - + if !progress && len(newRemaining) > 0 { return fmt.Errorf("circular dependency detected or unsatisfied dependencies") } - + remaining = newRemaining } - + // Deploy bridge if configured if manifest.Bridge != nil { - fmt.Printf(" ๐ŸŒ‰ Setting up bridge from %s to %s...\n", + fmt.Printf(" ๐ŸŒ‰ Setting up bridge from %s to %s...\n", manifest.Bridge.Source, manifest.Bridge.Destination) - + if err := host.SetupBridge(ctx, manifest.Bridge); err != nil { return fmt.Errorf("failed to setup bridge: %w", err) } } - + fmt.Printf("\nโœ… Stack '%s' deployed successfully!\n", stackName) - + // Print endpoints fmt.Println("\n๐Ÿ“Š Engine Endpoints:") w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) fmt.Fprintln(w, "ENGINE\tTYPE\tRPC\tWEBSOCKET") fmt.Fprintln(w, "------\t----\t---\t---------") - + for _, engine := range manifest.Engines { rpc := fmt.Sprintf("http://localhost:%d", engine.HTTPPort) ws := fmt.Sprintf("ws://localhost:%d", engine.WSPort) fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", engine.Name, engine.Type, rpc, ws) } w.Flush() - + // Save stack info stackDir := filepath.Join(os.Getenv("HOME"), ".netrunner", "stacks", stackName) if err := os.MkdirAll(stackDir, 0755); err != nil { @@ -165,23 +166,23 @@ func newStackDeployCmd(logger log.Logger) *cobra.Command { if err := manifest.Save(manifestCopy); err != nil { logger.Warn("Failed to save manifest copy", log.Err(err)) } - + // Save host state stateFile := filepath.Join(stackDir, "state.json") if err := host.SaveState(stateFile); err != nil { logger.Warn("Failed to save host state", log.Err(err)) } } - + return nil }, } - + cmd.Flags().StringVarP(&manifestPath, "manifest", "f", "", "Path to stack manifest file") cmd.Flags().BoolVarP(&wait, "wait", "w", true, "Wait for engines to be healthy") cmd.Flags().DurationVar(&timeout, "timeout", 5*time.Minute, "Deployment timeout") cmd.MarkFlagRequired("manifest") - + return cmd } @@ -192,29 +193,29 @@ func newStackDestroyCmd(logger log.Logger) *cobra.Command { Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { stackName := args[0] - + // Load stack state stackDir := filepath.Join(os.Getenv("HOME"), ".netrunner", "stacks", stackName) stateFile := filepath.Join(stackDir, "state.json") - + host := orchestrator.NewHost() if err := host.LoadState(stateFile); err != nil { return fmt.Errorf("failed to load stack state: %w", err) } - + ctx := cmd.Context() fmt.Printf("๐Ÿ›‘ Destroying stack '%s'...\n", stackName) - + // Stop all engines if err := host.StopAll(ctx); err != nil { return fmt.Errorf("failed to stop engines: %w", err) } - + // Clean up stack directory if err := os.RemoveAll(stackDir); err != nil { logger.Warn("Failed to remove stack directory", log.Err(err)) } - + fmt.Printf("โœ… Stack '%s' destroyed\n", stackName) return nil }, @@ -228,35 +229,35 @@ func newStackStatusCmd(logger log.Logger) *cobra.Command { Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { stackName := args[0] - + // Load stack state stackDir := filepath.Join(os.Getenv("HOME"), ".netrunner", "stacks", stackName) stateFile := filepath.Join(stackDir, "state.json") - + host := orchestrator.NewHost() if err := host.LoadState(stateFile); err != nil { return fmt.Errorf("failed to load stack state: %w", err) } - + ctx := cmd.Context() fmt.Printf("๐Ÿ“Š Status of stack '%s':\n\n", stackName) - + // Get status of all engines statuses, err := host.GetAllStatus(ctx) if err != nil { return fmt.Errorf("failed to get status: %w", err) } - + w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) fmt.Fprintln(w, "ENGINE\tTYPE\tSTATUS\tBLOCK HEIGHT\tPEERS\tVERSION") fmt.Fprintln(w, "------\t----\t------\t------------\t-----\t-------") - + for name, status := range statuses { health := "โŒ Unhealthy" if status.Health.Healthy { health = "โœ… Healthy" } - + fmt.Fprintf(w, "%s\t%s\t%s\t%d\t%d\t%s\n", name, status.Type, @@ -266,7 +267,7 @@ func newStackStatusCmd(logger log.Logger) *cobra.Command { status.Health.Version) } w.Flush() - + // Show metrics fmt.Println("\n๐Ÿ“ˆ Metrics:") metrics := host.GetMetrics() @@ -276,7 +277,7 @@ func newStackStatusCmd(logger log.Logger) *cobra.Command { fmt.Printf(" %s: %v\n", k, v) } } - + return nil }, } @@ -288,50 +289,50 @@ func newStackListCmd(logger log.Logger) *cobra.Command { Short: "List all deployed stacks", RunE: func(cmd *cobra.Command, args []string) error { stacksDir := filepath.Join(os.Getenv("HOME"), ".netrunner", "stacks") - + // Ensure directory exists if _, err := os.Stat(stacksDir); os.IsNotExist(err) { fmt.Println("No stacks deployed") return nil } - + // List stack directories entries, err := os.ReadDir(stacksDir) if err != nil { return fmt.Errorf("failed to list stacks: %w", err) } - + if len(entries) == 0 { fmt.Println("No stacks deployed") return nil } - + fmt.Println("Deployed stacks:") w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) fmt.Fprintln(w, "NAME\tCREATED\tENGINES") fmt.Fprintln(w, "----\t-------\t-------") - + for _, entry := range entries { if !entry.IsDir() { continue } - + info, _ := entry.Info() - + // Try to load manifest to get engine count manifestPath := filepath.Join(stacksDir, entry.Name(), "manifest.yaml") engineCount := "?" if manifest, err := orchestrator.LoadManifest(manifestPath); err == nil { engineCount = fmt.Sprintf("%d", len(manifest.Engines)) } - - fmt.Fprintf(w, "%s\t%s\t%s\n", + + fmt.Fprintf(w, "%s\t%s\t%s\n", entry.Name(), info.ModTime().Format("2006-01-02 15:04"), engineCount) } w.Flush() - + return nil }, } @@ -344,39 +345,39 @@ func newStackValidateCmd(logger log.Logger) *cobra.Command { Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { manifestPath := args[0] - + // Load manifest manifest, err := orchestrator.LoadManifest(manifestPath) if err != nil { return fmt.Errorf("failed to load manifest: %w", err) } - + // Validate if err := manifest.Validate(); err != nil { fmt.Printf("โŒ Manifest validation failed: %v\n", err) return err } - + fmt.Printf("โœ… Manifest is valid\n") fmt.Printf("\n๐Ÿ“‹ Summary:\n") fmt.Printf(" Name: %s\n", manifest.Name) fmt.Printf(" Version: %s\n", manifest.Version) fmt.Printf(" Engines: %d\n", len(manifest.Engines)) - + if manifest.Bridge != nil { fmt.Printf(" Bridge: %s โ†’ %s\n", manifest.Bridge.Source, manifest.Bridge.Destination) } - + // Check for dependency cycles fmt.Printf("\n๐Ÿ” Checking dependencies...\n") visited := make(map[string]bool) recStack := make(map[string]bool) - + var hasCycle func(string) bool hasCycle = func(name string) bool { visited[name] = true recStack[name] = true - + // Find engine config var engine *orchestrator.EngineConfig for i := range manifest.Engines { @@ -385,7 +386,7 @@ func newStackValidateCmd(logger log.Logger) *cobra.Command { break } } - + if engine != nil { for _, dep := range engine.DependsOn { if !visited[dep] { @@ -397,11 +398,11 @@ func newStackValidateCmd(logger log.Logger) *cobra.Command { } } } - + recStack[name] = false return false } - + for _, engine := range manifest.Engines { if !visited[engine.Name] { if hasCycle(engine.Name) { @@ -410,10 +411,10 @@ func newStackValidateCmd(logger log.Logger) *cobra.Command { } } } - + fmt.Printf(" โœ… No circular dependencies\n") - + return nil }, } -} \ No newline at end of file +} diff --git a/cmd/netrunner/commands/status.go b/cmd/netrunner/commands/status.go index 3ca0f87e..810bb300 100644 --- a/cmd/netrunner/commands/status.go +++ b/cmd/netrunner/commands/status.go @@ -4,11 +4,12 @@ package commands import ( - "github.com/luxfi/log" "fmt" "os" "text/tabwriter" + "github.com/luxfi/log" + "github.com/spf13/cobra" ) @@ -20,22 +21,22 @@ func NewStatusCmd(logger log.Logger) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { fmt.Println("๐Ÿ” Netrunner System Status") fmt.Println() - + // Show running engines fmt.Println("Running Engines:") w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) fmt.Fprintln(w, "NAME\tTYPE\tSTATUS\tUPTIME\tRPC") fmt.Fprintln(w, "----\t----\t------\t------\t---") - + // TODO: Implement global registry fmt.Fprintln(w, "(No engines running)") w.Flush() - + fmt.Println() fmt.Println("Deployed Stacks: 0") fmt.Println("Active Bridges: 0") - + return nil }, } -} \ No newline at end of file +} diff --git a/cmd/netrunner/commands/test.go b/cmd/netrunner/commands/test.go index 548d79d0..98da9225 100644 --- a/cmd/netrunner/commands/test.go +++ b/cmd/netrunner/commands/test.go @@ -4,11 +4,12 @@ package commands import ( - "github.com/luxfi/log" "context" "fmt" "time" + "github.com/luxfi/log" + "github.com/luxfi/netrunner/engines" "github.com/spf13/cobra" ) @@ -20,13 +21,13 @@ func NewTestCmd(logger log.Logger) *cobra.Command { Short: "Run integration tests", Long: "Run various integration tests to verify engine functionality", } - + cmd.AddCommand( newTestAllCmd(logger), newTestEngineCmd(logger), newTestStackCmd(logger), ) - + return cmd } @@ -36,23 +37,23 @@ func newTestAllCmd(logger log.Logger) *cobra.Command { Short: "Test all engine types", RunE: func(cmd *cobra.Command, args []string) error { ctx := cmd.Context() - + fmt.Println("๐Ÿงช Testing all engine types...") fmt.Println() - + engineTypes := []string{"lux", "lux", "geth", "op", "eth2"} results := make(map[string]bool) - + for _, engineType := range engineTypes { fmt.Printf("Testing %s...\n", engineType) - + // Skip engines that need additional setup if engineType == "op" { fmt.Println(" โญ๏ธ Skipping (requires L1)") results[engineType] = false continue } - + if err := testEngine(ctx, logger, engineType); err != nil { fmt.Printf(" โŒ Failed: %v\n", err) results[engineType] = false @@ -62,7 +63,7 @@ func newTestAllCmd(logger log.Logger) *cobra.Command { } fmt.Println() } - + // Summary fmt.Println("๐Ÿ“Š Test Summary:") passed := 0 @@ -74,13 +75,13 @@ func newTestAllCmd(logger log.Logger) *cobra.Command { } fmt.Printf(" %s: %s\n", engine, status) } - + fmt.Printf("\nTotal: %d/%d passed\n", passed, len(engineTypes)) - + if passed < len(engineTypes) { return fmt.Errorf("some tests failed") } - + return nil }, } @@ -94,14 +95,14 @@ func newTestEngineCmd(logger log.Logger) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { engineType := args[0] ctx := cmd.Context() - + fmt.Printf("๐Ÿงช Testing %s engine...\n", engineType) - + if err := testEngine(ctx, logger, engineType); err != nil { fmt.Printf("โŒ Test failed: %v\n", err) return err } - + fmt.Println("โœ… Test passed!") return nil }, @@ -114,10 +115,10 @@ func newTestStackCmd(logger log.Logger) *cobra.Command { Short: "Test multi-engine stack deployment", RunE: func(cmd *cobra.Command, args []string) error { ctx := cmd.Context() - + fmt.Println("๐Ÿงช Testing multi-engine stack...") fmt.Println() - + // Test simple two-engine stack fmt.Println("1. Testing Lux + Geth stack...") if err := testSimpleStack(ctx, logger); err != nil { @@ -126,7 +127,7 @@ func newTestStackCmd(logger log.Logger) *cobra.Command { } fmt.Println("โœ… Passed") fmt.Println() - + // Test dependency ordering fmt.Println("2. Testing dependency ordering...") if err := testDependencyStack(ctx, logger); err != nil { @@ -134,7 +135,7 @@ func newTestStackCmd(logger log.Logger) *cobra.Command { return err } fmt.Println("โœ… Passed") - + fmt.Println("\nโœ… All stack tests passed!") return nil }, @@ -153,7 +154,7 @@ func testEngine(ctx context.Context, logger log.Logger, engineType string) error LogLevel: "info", Extra: make(map[string]interface{}), } - + // Type-specific config var binary string switch engineType { @@ -172,26 +173,26 @@ func testEngine(ctx context.Context, logger log.Logger, engineType string) error default: return fmt.Errorf("unknown engine type: %s", engineType) } - + // Create engine name := fmt.Sprintf("test-%s", engineType) engine, err := engines.New(engines.EngineType(engineType), name, binary) if err != nil { return fmt.Errorf("failed to create engine: %w", err) } - + // Start engine if err := engine.Start(ctx, config); err != nil { return fmt.Errorf("failed to start engine: %w", err) } - + // Defer cleanup defer func() { if err := engine.Stop(context.Background()); err != nil { logger.Error("Failed to stop test engine", log.Err(err)) } }() - + // Wait for health maxAttempts := 30 for i := 0; i < maxAttempts; i++ { @@ -207,10 +208,10 @@ func testEngine(ctx context.Context, logger log.Logger, engineType string) error if engine.NetworkID() != config.NetworkID { return fmt.Errorf("network ID mismatch") } - + return nil } - + select { case <-ctx.Done(): return ctx.Err() @@ -218,7 +219,7 @@ func testEngine(ctx context.Context, logger log.Logger, engineType string) error // Continue } } - + return fmt.Errorf("engine failed to become healthy") } @@ -232,4 +233,4 @@ func testSimpleStack(ctx context.Context, logger log.Logger) error { func testDependencyStack(ctx context.Context, logger log.Logger) error { // TODO: Implement dependency stack test return nil -} \ No newline at end of file +} diff --git a/cmd/netrunner/main.go b/cmd/netrunner/main.go index 0d70b57c..713d4c1f 100644 --- a/cmd/netrunner/main.go +++ b/cmd/netrunner/main.go @@ -66,4 +66,4 @@ OP Stack, and more.`, logger.Error("Command failed", log.Err(err)) os.Exit(1) } -} \ No newline at end of file +} diff --git a/cmd/ping/ping.go b/cmd/ping/ping.go index c8af49a3..a05a1ea1 100644 --- a/cmd/ping/ping.go +++ b/cmd/ping/ping.go @@ -7,11 +7,11 @@ import ( "context" "time" + "github.com/luxfi/log" + "github.com/luxfi/log/level" "github.com/luxfi/netrunner/client" "github.com/luxfi/netrunner/utils/constants" "github.com/luxfi/netrunner/ux" - "github.com/luxfi/log" - "github.com/luxfi/log/level" "github.com/spf13/cobra" ) diff --git a/cmd/server/server.go b/cmd/server/server.go index c7b436ff..b9e34ec4 100644 --- a/cmd/server/server.go +++ b/cmd/server/server.go @@ -11,11 +11,11 @@ import ( "syscall" "time" + "github.com/luxfi/log" + "github.com/luxfi/log/level" "github.com/luxfi/netrunner/server" "github.com/luxfi/netrunner/utils" "github.com/luxfi/netrunner/utils/constants" - "github.com/luxfi/log" - "github.com/luxfi/log/level" "github.com/spf13/cobra" ) diff --git a/engines/avalanche/engine.go b/engines/avalanche/engine.go index 00e3c7ae..c2693e79 100644 --- a/engines/avalanche/engine.go +++ b/engines/avalanche/engine.go @@ -25,15 +25,15 @@ func init() { // LuxEngine wraps luxd node management type LuxEngine struct { - name string - binary string - dataDir string - config *engines.NodeConfig - process *exec.Cmd - httpClient *http.Client - rpcEndpoint string - startTime time.Time - + name string + binary string + dataDir string + config *engines.NodeConfig + process *exec.Cmd + httpClient *http.Client + rpcEndpoint string + startTime time.Time + // Cached info networkID uint32 chainID ids.ID @@ -50,26 +50,26 @@ func NewLuxEngine(name string, binary string) (engines.Engine, error) { }, nil } -func (e *LuxEngine) Name() string { return e.name } -func (e *LuxEngine) Type() engines.EngineType { return engines.EngineLux } -func (e *LuxEngine) NetworkID() uint32 { return e.networkID } -func (e *LuxEngine) ChainID() ids.ID { return e.chainID } +func (e *LuxEngine) Name() string { return e.name } +func (e *LuxEngine) Type() engines.EngineType { return engines.EngineLux } +func (e *LuxEngine) NetworkID() uint32 { return e.networkID } +func (e *LuxEngine) ChainID() ids.ID { return e.chainID } func (e *LuxEngine) ParentChain() *engines.ChainInfo { return nil } // L1 func (e *LuxEngine) Start(ctx context.Context, config *engines.NodeConfig) error { e.config = config e.networkID = config.NetworkID - + // Setup data directory if config.DataDir == "" { config.DataDir = filepath.Join(os.TempDir(), "lux", e.name) } e.dataDir = config.DataDir - + if err := os.MkdirAll(e.dataDir, 0755); err != nil { return fmt.Errorf("failed to create data dir: %w", err) } - + // Build command arguments args := []string{ fmt.Sprintf("--network-id=%s", getLuxNetwork(config.NetworkID)), @@ -81,23 +81,23 @@ func (e *LuxEngine) Start(ctx context.Context, config *engines.NodeConfig) error "--http-allowed-hosts=*", "--http-allowed-origins=*", } - + // Add bootstrap nodes if custom network if len(config.BootstrapIPs) > 0 { for _, ip := range config.BootstrapIPs { args = append(args, fmt.Sprintf("--bootstrap-ips=%s", ip)) } } - + // Add extra configs for k, v := range config.Extra { args = append(args, fmt.Sprintf("--%s=%v", k, v)) } - + // Start the process e.process = exec.CommandContext(ctx, e.binary, args...) e.process.Dir = e.dataDir - + // Setup logs logFile, err := os.Create(filepath.Join(e.dataDir, "node.log")) if err != nil { @@ -105,19 +105,19 @@ func (e *LuxEngine) Start(ctx context.Context, config *engines.NodeConfig) error } e.process.Stdout = logFile e.process.Stderr = logFile - + if err := e.process.Start(); err != nil { return fmt.Errorf("failed to start luxd: %w", err) } - + e.startTime = time.Now() e.rpcEndpoint = e.RPCEndpoint() e.httpClient = &http.Client{Timeout: 10 * time.Second} - + // Wait for node to be responsive ticker := time.NewTicker(500 * time.Millisecond) defer ticker.Stop() - + timeout := time.After(60 * time.Second) // Lux can take longer for { select { @@ -141,17 +141,17 @@ func (e *LuxEngine) Stop(ctx context.Context) error { if e.process == nil || e.process.Process == nil { return nil } - + // Try graceful shutdown first if err := e.process.Process.Signal(os.Interrupt); err != nil { return e.process.Process.Kill() } - + done := make(chan error, 1) go func() { done <- e.process.Wait() }() - + select { case <-done: return nil @@ -174,23 +174,23 @@ func (e *LuxEngine) Health(ctx context.Context) (*engines.HealthStatus, error) { if e.httpClient == nil { return &engines.HealthStatus{Healthy: false}, nil } - + // Get node health healthy, err := e.getHealth(ctx) if err != nil { return &engines.HealthStatus{Healthy: false}, nil } - + // Get peers peerCount, _ := e.getPeerCount(ctx) - + // Get version version, _ := e.getNodeVersion(ctx) - + return &engines.HealthStatus{ - Healthy: healthy, - PeerCount: peerCount, - Version: version, + Healthy: healthy, + PeerCount: peerCount, + Version: version, // BlockHeight from C-Chain would require eth client }, nil } @@ -262,29 +262,29 @@ func (e *LuxEngine) callRPC(ctx context.Context, endpoint string, method string, "method": method, "params": params, } - + data, err := json.Marshal(reqBody) if err != nil { return nil, err } - + req, err := http.NewRequestWithContext(ctx, "POST", e.rpcEndpoint+endpoint, bytes.NewReader(data)) if err != nil { return nil, err } req.Header.Set("Content-Type", "application/json") - + resp, err := e.httpClient.Do(req) if err != nil { return nil, err } defer resp.Body.Close() - + body, err := io.ReadAll(resp.Body) if err != nil { return nil, err } - + var result struct { Result json.RawMessage `json:"result"` Error *struct { @@ -292,15 +292,15 @@ func (e *LuxEngine) callRPC(ctx context.Context, endpoint string, method string, Message string `json:"message"` } `json:"error"` } - + if err := json.Unmarshal(body, &result); err != nil { return nil, err } - + if result.Error != nil { return nil, fmt.Errorf("RPC error %d: %s", result.Error.Code, result.Error.Message) } - + return result.Result, nil } @@ -309,14 +309,14 @@ func (e *LuxEngine) getNodeID(ctx context.Context) (string, error) { if err != nil { return "", err } - + var resp struct { NodeID string `json:"nodeID"` } if err := json.Unmarshal(result, &resp); err != nil { return "", err } - + return resp.NodeID, nil } @@ -325,14 +325,14 @@ func (e *LuxEngine) getBlockchainID(ctx context.Context, alias string) (ids.ID, if err != nil { return ids.Empty, err } - + var resp struct { BlockchainID string `json:"blockchainID"` } if err := json.Unmarshal(result, &resp); err != nil { return ids.Empty, err } - + return ids.FromString(resp.BlockchainID) } @@ -341,14 +341,14 @@ func (e *LuxEngine) getHealth(ctx context.Context) (bool, error) { if err != nil { return false, err } - + var resp struct { Healthy bool `json:"healthy"` } if err := json.Unmarshal(result, &resp); err != nil { return false, err } - + return resp.Healthy, nil } @@ -357,9 +357,9 @@ func (e *LuxEngine) getPeerCount(ctx context.Context) (int, error) { if err != nil { return 0, err } - + var resp struct { - NumPeers int `json:"numPeers"` + NumPeers int `json:"numPeers"` Peers []interface{} `json:"peers"` } if err := json.Unmarshal(result, &resp); err != nil { @@ -370,7 +370,7 @@ func (e *LuxEngine) getPeerCount(ctx context.Context) (int, error) { } return len(peers), nil } - + if resp.NumPeers > 0 { return resp.NumPeers, nil } @@ -382,13 +382,13 @@ func (e *LuxEngine) getNodeVersion(ctx context.Context) (string, error) { if err != nil { return "", err } - + var resp struct { Version string `json:"version"` } if err := json.Unmarshal(result, &resp); err != nil { return "", err } - + return resp.Version, nil -} \ No newline at end of file +} diff --git a/engines/engine_test.go b/engines/engine_test.go index 471b3dba..777d5003 100644 --- a/engines/engine_test.go +++ b/engines/engine_test.go @@ -53,18 +53,20 @@ type mockEngine struct { name string } -func (m *mockEngine) Name() string { return m.name } -func (m *mockEngine) Type() EngineType { return "test" } -func (m *mockEngine) NetworkID() uint32 { return 1337 } -func (m *mockEngine) ChainID() ids.ID { return ids.Empty } -func (m *mockEngine) ParentChain() *ChainInfo { return nil } -func (m *mockEngine) Start(_ context.Context, _ *NodeConfig) error { return nil } -func (m *mockEngine) Stop(_ context.Context) error { return nil } -func (m *mockEngine) Restart(_ context.Context) error { return nil } -func (m *mockEngine) Health(_ context.Context) (*HealthStatus, error) { return &HealthStatus{Healthy: true}, nil } -func (m *mockEngine) IsRunning() bool { return true } -func (m *mockEngine) Uptime() time.Duration { return 0 } -func (m *mockEngine) RPCEndpoint() string { return "http://localhost:8545" } -func (m *mockEngine) WSEndpoint() string { return "ws://localhost:8546" } -func (m *mockEngine) P2PEndpoint() string { return "localhost:30303" } -func (m *mockEngine) Metrics() map[string]interface{} { return map[string]interface{}{} } \ No newline at end of file +func (m *mockEngine) Name() string { return m.name } +func (m *mockEngine) Type() EngineType { return "test" } +func (m *mockEngine) NetworkID() uint32 { return 1337 } +func (m *mockEngine) ChainID() ids.ID { return ids.Empty } +func (m *mockEngine) ParentChain() *ChainInfo { return nil } +func (m *mockEngine) Start(_ context.Context, _ *NodeConfig) error { return nil } +func (m *mockEngine) Stop(_ context.Context) error { return nil } +func (m *mockEngine) Restart(_ context.Context) error { return nil } +func (m *mockEngine) Health(_ context.Context) (*HealthStatus, error) { + return &HealthStatus{Healthy: true}, nil +} +func (m *mockEngine) IsRunning() bool { return true } +func (m *mockEngine) Uptime() time.Duration { return 0 } +func (m *mockEngine) RPCEndpoint() string { return "http://localhost:8545" } +func (m *mockEngine) WSEndpoint() string { return "ws://localhost:8546" } +func (m *mockEngine) P2PEndpoint() string { return "localhost:30303" } +func (m *mockEngine) Metrics() map[string]interface{} { return map[string]interface{}{} } diff --git a/engines/eth2.go b/engines/eth2.go index 4ec6b596..eacbb3c0 100644 --- a/engines/eth2.go +++ b/engines/eth2.go @@ -23,17 +23,17 @@ import ( // Eth2Engine implements Ethereum 2.0 consensus (Beacon Chain + Execution Layer) type Eth2Engine struct { - mu sync.RWMutex - name string - beaconBinary string // Consensus client (e.g., Prysm, Lighthouse, Teku) - executionBinary string // Execution client (e.g., Geth, Erigon, Nethermind) - beaconCmd *exec.Cmd - executionCmd *exec.Cmd - config *NodeConfig - startTime time.Time - running bool - dataDir string - + mu sync.RWMutex + name string + beaconBinary string // Consensus client (e.g., Prysm, Lighthouse, Teku) + executionBinary string // Execution client (e.g., Geth, Erigon, Nethermind) + beaconCmd *exec.Cmd + executionCmd *exec.Cmd + config *NodeConfig + startTime time.Time + running bool + dataDir string + // Eth2 specific chainID uint64 networkName string @@ -66,27 +66,27 @@ const ( // Eth2Config contains Ethereum 2.0 specific configuration type Eth2Config struct { - ChainID uint64 `json:"chain_id"` - NetworkName string `json:"network_name"` - ConsensusClient ConsensusClient `json:"consensus_client"` - ExecutionClient ExecutionClient `json:"execution_client"` - ValidatorEnabled bool `json:"validator_enabled"` - ValidatorKeys []string `json:"validator_keys,omitempty"` - InitialValidators []string `json:"initial_validators,omitempty"` - TerminalTotalDifficulty string `json:"terminal_total_difficulty"` - GenesisTime uint64 `json:"genesis_time"` + ChainID uint64 `json:"chain_id"` + NetworkName string `json:"network_name"` + ConsensusClient ConsensusClient `json:"consensus_client"` + ExecutionClient ExecutionClient `json:"execution_client"` + ValidatorEnabled bool `json:"validator_enabled"` + ValidatorKeys []string `json:"validator_keys,omitempty"` + InitialValidators []string `json:"initial_validators,omitempty"` + TerminalTotalDifficulty string `json:"terminal_total_difficulty"` + GenesisTime uint64 `json:"genesis_time"` } // BeaconNodeStatus represents beacon chain status type BeaconNodeStatus struct { - HeadSlot uint64 `json:"head_slot"` - SyncDistance uint64 `json:"sync_distance"` - IsSyncing bool `json:"is_syncing"` - IsOptimistic bool `json:"is_optimistic"` - ElOffline bool `json:"el_offline"` - CurrentEpoch uint64 `json:"current_epoch"` - FinalizedEpoch uint64 `json:"finalized_epoch"` - JustifiedEpoch uint64 `json:"justified_epoch"` + HeadSlot uint64 `json:"head_slot"` + SyncDistance uint64 `json:"sync_distance"` + IsSyncing bool `json:"is_syncing"` + IsOptimistic bool `json:"is_optimistic"` + ElOffline bool `json:"el_offline"` + CurrentEpoch uint64 `json:"current_epoch"` + FinalizedEpoch uint64 `json:"finalized_epoch"` + JustifiedEpoch uint64 `json:"justified_epoch"` } // NewEth2Engine creates a new Ethereum 2.0 engine @@ -185,7 +185,7 @@ func (e *Eth2Engine) loadEth2Config() error { func (e *Eth2Engine) generateJWTSecret() error { jwtPath := filepath.Join(e.dataDir, "jwt.hex") - + // Check if already exists if _, err := os.Stat(jwtPath); err == nil { data, err := os.ReadFile(jwtPath) @@ -201,14 +201,14 @@ func (e *Eth2Engine) generateJWTSecret() error { if _, err := rand.Read(secret); err != nil { return fmt.Errorf("failed to generate random JWT secret: %w", err) } - + e.jwtSecret = hex.EncodeToString(secret) return os.WriteFile(jwtPath, []byte(e.jwtSecret), 0600) } func (e *Eth2Engine) startExecutionClient(ctx context.Context) error { var args []string - + switch e.executionClient { case Geth: args = e.getGethArgs() @@ -305,7 +305,7 @@ func (e *Eth2Engine) getBesuArgs() []string { func (e *Eth2Engine) startConsensusClient(ctx context.Context) error { var args []string - + switch e.consensusClient { case Prysm: args = e.getPrysmArgs() @@ -424,7 +424,7 @@ func (e *Eth2Engine) Stop(ctx context.Context) error { go func() { done <- e.beaconCmd.Wait() }() - + select { case <-done: case <-time.After(10 * time.Second): @@ -446,7 +446,7 @@ func (e *Eth2Engine) stopExecutionClient() { go func() { done <- e.executionCmd.Wait() }() - + select { case <-done: case <-time.After(10 * time.Second): @@ -499,7 +499,7 @@ func (e *Eth2Engine) Health(ctx context.Context) (*HealthStatus, error) { func (e *Eth2Engine) getBeaconNodeStatus() (*BeaconNodeStatus, error) { url := fmt.Sprintf("http://localhost:%d/eth/v1/node/syncing", e.config.HTTPPort+2000) - + client := &http.Client{Timeout: 5 * time.Second} resp, err := client.Get(url) if err != nil { @@ -578,7 +578,7 @@ func (e *Eth2Engine) ParentChain() *ChainInfo { func (e *Eth2Engine) Metrics() map[string]interface{} { e.mu.RLock() defer e.mu.RUnlock() - + metrics := map[string]interface{}{ "running": e.running, "uptime": e.Uptime().Seconds(), @@ -589,7 +589,7 @@ func (e *Eth2Engine) Metrics() map[string]interface{} { "network": e.networkName, "validator": e.validatorEnabled, } - + return metrics } @@ -605,4 +605,4 @@ func Eth2Factory(name string, binary string) (Engine, error) { func init() { Register(EngineEth2, Eth2Factory) -} \ No newline at end of file +} diff --git a/engines/geth/engine.go b/engines/geth/engine.go index 515712bb..ac8b69c4 100644 --- a/engines/geth/engine.go +++ b/engines/geth/engine.go @@ -33,7 +33,7 @@ type GethEngine struct { config *engines.NodeConfig process *exec.Cmd startTime time.Time - + // Cached info networkID uint32 chainID ids.ID @@ -50,32 +50,32 @@ func NewGethEngine(name string, binary string) (engines.Engine, error) { }, nil } -func (e *GethEngine) Name() string { return e.name } -func (e *GethEngine) Type() engines.EngineType { return engines.EngineGeth } -func (e *GethEngine) NetworkID() uint32 { return e.networkID } -func (e *GethEngine) ChainID() ids.ID { return e.chainID } +func (e *GethEngine) Name() string { return e.name } +func (e *GethEngine) Type() engines.EngineType { return engines.EngineGeth } +func (e *GethEngine) NetworkID() uint32 { return e.networkID } +func (e *GethEngine) ChainID() ids.ID { return e.chainID } func (e *GethEngine) ParentChain() *engines.ChainInfo { return nil } // L1 func (e *GethEngine) Start(ctx context.Context, config *engines.NodeConfig) error { e.config = config e.networkID = config.NetworkID - + // Generate chain ID from network ID chainIDBytes := make([]byte, 32) chainIDBig := big.NewInt(int64(config.NetworkID)) chainIDBig.FillBytes(chainIDBytes) copy(e.chainID[:], chainIDBytes) - + // Setup data directory if config.DataDir == "" { config.DataDir = filepath.Join(os.TempDir(), "geth", e.name) } e.dataDir = config.DataDir - + if err := os.MkdirAll(e.dataDir, 0755); err != nil { return fmt.Errorf("failed to create data dir: %w", err) } - + // Initialize genesis if needed if _, err := os.Stat(filepath.Join(e.dataDir, "geth", "chaindata")); os.IsNotExist(err) { // Create simple genesis @@ -99,19 +99,19 @@ func (e *GethEngine) Start(ctx context.Context, config *engines.NodeConfig) erro "gasLimit": "0x8000000", "alloc": {} }`, config.NetworkID) - + genesisPath := filepath.Join(e.dataDir, "genesis.json") if err := os.WriteFile(genesisPath, []byte(genesis), 0644); err != nil { return fmt.Errorf("failed to write genesis: %w", err) } - + // Initialize geth with genesis initCmd := exec.CommandContext(ctx, e.binary, "init", genesisPath, "--datadir", e.dataDir) if err := initCmd.Run(); err != nil { return fmt.Errorf("failed to init geth: %w", err) } } - + // Build command arguments args := []string{ "--datadir", e.dataDir, @@ -133,17 +133,17 @@ func (e *GethEngine) Start(ctx context.Context, config *engines.NodeConfig) erro "--syncmode", "full", "--gcmode", "archive", } - + // Add dev mode for testing if config.NetworkID > 90000 { args = append(args, "--dev", "--dev.period", "1") } - + // Add bootstrap nodes for _, bootnode := range config.BootstrapIPs { args = append(args, "--bootnodes", bootnode) } - + // Add extra configs for k, v := range config.Extra { switch k { @@ -157,11 +157,11 @@ func (e *GethEngine) Start(ctx context.Context, config *engines.NodeConfig) erro args = append(args, "--password", v.(string)) } } - + // Start the process e.process = exec.CommandContext(ctx, e.binary, args...) e.process.Dir = e.dataDir - + // Setup logs logFile, err := os.Create(filepath.Join(e.dataDir, "geth.log")) if err != nil { @@ -169,17 +169,17 @@ func (e *GethEngine) Start(ctx context.Context, config *engines.NodeConfig) erro } e.process.Stdout = logFile e.process.Stderr = logFile - + if err := e.process.Start(); err != nil { return fmt.Errorf("failed to start geth: %w", err) } - + e.startTime = time.Now() - + // Wait for node to be responsive ticker := time.NewTicker(500 * time.Millisecond) defer ticker.Stop() - + timeout := time.After(30 * time.Second) for { select { @@ -200,17 +200,17 @@ func (e *GethEngine) Stop(ctx context.Context) error { if e.process == nil || e.process.Process == nil { return nil } - + // Try graceful shutdown first if err := e.process.Process.Signal(os.Interrupt); err != nil { return e.process.Process.Kill() } - + done := make(chan error, 1) go func() { done <- e.process.Wait() }() - + select { case <-done: return nil @@ -233,10 +233,10 @@ func (e *GethEngine) Health(ctx context.Context) (*engines.HealthStatus, error) if !e.IsRunning() { return &engines.HealthStatus{Healthy: false}, nil } - + // Check if RPC is responsive healthy := e.checkRPC() - + return &engines.HealthStatus{ Healthy: healthy, PeerCount: 0, // Would need JSON-RPC client to get real peer count @@ -292,11 +292,11 @@ func (e *GethEngine) checkRPC() bool { if e.process == nil || e.process.Process == nil { return false } - + // Check if process is still alive if err := e.process.Process.Signal(os.Signal(nil)); err != nil { return false } - + return true -} \ No newline at end of file +} diff --git a/engines/lux/engine.go b/engines/lux/engine.go index cc33e79a..b2328215 100644 --- a/engines/lux/engine.go +++ b/engines/lux/engine.go @@ -36,7 +36,7 @@ type LuxEngine struct { infoClient *info.Client healthClient *health.Client startTime time.Time - + // Cached info networkID uint32 chainID ids.ID @@ -53,26 +53,26 @@ func NewLuxEngine(name string, binary string) (engines.Engine, error) { }, nil } -func (e *LuxEngine) Name() string { return e.name } -func (e *LuxEngine) Type() engines.EngineType { return engines.EngineLux } -func (e *LuxEngine) NetworkID() uint32 { return e.networkID } -func (e *LuxEngine) ChainID() ids.ID { return e.chainID } +func (e *LuxEngine) Name() string { return e.name } +func (e *LuxEngine) Type() engines.EngineType { return engines.EngineLux } +func (e *LuxEngine) NetworkID() uint32 { return e.networkID } +func (e *LuxEngine) ChainID() ids.ID { return e.chainID } func (e *LuxEngine) ParentChain() *engines.ChainInfo { return nil } // L1 func (e *LuxEngine) Start(ctx context.Context, config *engines.NodeConfig) error { e.config = config e.networkID = config.NetworkID - + // Setup data directory if config.DataDir == "" { config.DataDir = filepath.Join(os.TempDir(), "lux", e.name) } e.dataDir = config.DataDir - + if err := os.MkdirAll(e.dataDir, 0755); err != nil { return fmt.Errorf("failed to create data dir: %w", err) } - + // Build command arguments args := []string{ fmt.Sprintf("--network-id=%d", config.NetworkID), @@ -83,23 +83,23 @@ func (e *LuxEngine) Start(ctx context.Context, config *engines.NodeConfig) error "--http-host=0.0.0.0", "--http-allowed-hosts=*", } - + // Add bootstrap nodes if len(config.BootstrapIPs) > 0 { for _, ip := range config.BootstrapIPs { args = append(args, fmt.Sprintf("--bootstrap-ips=%s", ip)) } } - + // Add extra configs for k, v := range config.Extra { args = append(args, fmt.Sprintf("--%s=%v", k, v)) } - + // Start the process e.process = exec.CommandContext(ctx, e.binary, args...) e.process.Dir = e.dataDir - + // Setup logs logFile, err := os.Create(filepath.Join(e.dataDir, "node.log")) if err != nil { @@ -107,21 +107,21 @@ func (e *LuxEngine) Start(ctx context.Context, config *engines.NodeConfig) error } e.process.Stdout = logFile e.process.Stderr = logFile - + if err := e.process.Start(); err != nil { return fmt.Errorf("failed to start luxd: %w", err) } - + e.startTime = time.Now() - + // Setup RPC clients e.infoClient = info.NewClient(e.RPCEndpoint()) e.healthClient = health.NewClient(e.RPCEndpoint()) - + // Wait for node to be responsive ticker := time.NewTicker(500 * time.Millisecond) defer ticker.Stop() - + timeout := time.After(30 * time.Second) for { select { @@ -145,17 +145,17 @@ func (e *LuxEngine) Stop(ctx context.Context) error { if e.process == nil || e.process.Process == nil { return nil } - + // Try graceful shutdown first if err := e.process.Process.Signal(os.Interrupt); err != nil { return e.process.Process.Kill() } - + done := make(chan error, 1) go func() { done <- e.process.Wait() }() - + select { case <-done: return nil @@ -178,29 +178,29 @@ func (e *LuxEngine) Health(ctx context.Context) (*engines.HealthStatus, error) { if e.healthClient == nil || e.infoClient == nil { return &engines.HealthStatus{Healthy: false}, nil } - + // Get node health healthResp, err := e.healthClient.Health(ctx, nil) if err != nil { return &engines.HealthStatus{Healthy: false}, nil } - + // Get peers peers, err := e.infoClient.Peers(ctx, nil) if err != nil { peers = []info.Peer{} } - + // Get version versions, err := e.infoClient.GetNodeVersion(ctx) if err != nil { versions = &info.GetNodeVersionReply{} } - + return &engines.HealthStatus{ - Healthy: healthResp.Healthy, - PeerCount: len(peers), - Version: versions.Version, + Healthy: healthResp.Healthy, + PeerCount: len(peers), + Version: versions.Version, // BlockHeight from C-Chain would require eth client }, nil } @@ -248,4 +248,4 @@ func (e *LuxEngine) Metrics() map[string]interface{} { "network_id": e.networkID, "chain_id": e.chainID.String(), } -} \ No newline at end of file +} diff --git a/engines/opstack.go b/engines/opstack.go index 3a7da4aa..fda586fe 100644 --- a/engines/opstack.go +++ b/engines/opstack.go @@ -29,7 +29,7 @@ type OPStackEngine struct { startTime time.Time running bool dataDir string - + // OP Stack specific l1RPC string sequencer bool @@ -38,14 +38,14 @@ type OPStackEngine struct { // RollupConfig contains OP Stack rollup configuration type RollupConfig struct { - Genesis RollupGenesis `json:"genesis"` - BlockTime uint64 `json:"block_time"` - MaxSequencerDrift uint64 `json:"max_sequencer_drift"` - SeqWindowSize uint64 `json:"seq_window_size"` - ChannelTimeout uint64 `json:"channel_timeout"` - L1ChainID uint64 `json:"l1_chain_id"` - L2ChainID uint64 `json:"l2_chain_id"` - P2PSequencerAddress string `json:"p2p_sequencer_address"` + Genesis RollupGenesis `json:"genesis"` + BlockTime uint64 `json:"block_time"` + MaxSequencerDrift uint64 `json:"max_sequencer_drift"` + SeqWindowSize uint64 `json:"seq_window_size"` + ChannelTimeout uint64 `json:"channel_timeout"` + L1ChainID uint64 `json:"l1_chain_id"` + L2ChainID uint64 `json:"l2_chain_id"` + P2PSequencerAddress string `json:"p2p_sequencer_address"` } // RollupGenesis contains OP Stack genesis configuration @@ -195,7 +195,7 @@ func (e *OPStackEngine) startNode(ctx context.Context) error { } if e.sequencer { - args = append(args, + args = append(args, "--sequencer.enabled", "--sequencer.l1-confs", "3", "--verifier.l1-confs", "3", @@ -233,14 +233,14 @@ func (e *OPStackEngine) loadRollupConfig() error { L1ChainID: uint64(e.config.NetworkID), L2ChainID: uint64(e.config.NetworkID) + 10000, } - + // Set genesis e.rollupConfig.Genesis.L1.Number = 0 e.rollupConfig.Genesis.L1.Hash = "0x0000000000000000000000000000000000000000000000000000000000000000" e.rollupConfig.Genesis.L2.Number = 0 e.rollupConfig.Genesis.L2.Hash = "0x0000000000000000000000000000000000000000000000000000000000000000" e.rollupConfig.Genesis.L2Time = uint64(time.Now().Unix()) - + // Save config data, err := json.MarshalIndent(e.rollupConfig, "", " ") if err != nil { @@ -254,7 +254,7 @@ func (e *OPStackEngine) loadRollupConfig() error { if err != nil { return err } - + return json.Unmarshal(data, &e.rollupConfig) } @@ -273,7 +273,7 @@ func (e *OPStackEngine) Stop(ctx context.Context) error { go func() { done <- e.nodeCmd.Wait() }() - + select { case <-done: case <-time.After(10 * time.Second): @@ -295,7 +295,7 @@ func (e *OPStackEngine) stopGeth() { go func() { done <- e.gethCmd.Wait() }() - + select { case <-done: case <-time.After(10 * time.Second): @@ -408,20 +408,20 @@ func (e *OPStackEngine) ParentChain() *ChainInfo { func (e *OPStackEngine) Metrics() map[string]interface{} { e.mu.RLock() defer e.mu.RUnlock() - + metrics := map[string]interface{}{ "running": e.running, "uptime": e.Uptime().Seconds(), "type": "op-stack", "sequencer": e.sequencer, } - + if e.rollupConfig != nil { metrics["l1_chain_id"] = e.rollupConfig.L1ChainID metrics["l2_chain_id"] = e.rollupConfig.L2ChainID metrics["block_time"] = e.rollupConfig.BlockTime } - + return metrics } @@ -437,4 +437,4 @@ func OPStackFactory(name string, binary string) (Engine, error) { func init() { Register(EngineOP, OPStackFactory) -} \ No newline at end of file +} diff --git a/engines/types.go b/engines/types.go index 6bc0c95e..96ed318f 100644 --- a/engines/types.go +++ b/engines/types.go @@ -15,10 +15,10 @@ import ( type EngineType string const ( - EngineLux EngineType = "lux" - EngineGeth EngineType = "geth" - EngineOP EngineType = "op" - EngineEth2 EngineType = "eth2" + EngineLux EngineType = "lux" + EngineGeth EngineType = "geth" + EngineOP EngineType = "op" + EngineEth2 EngineType = "eth2" ) // ChainInfo describes a blockchain's network properties @@ -58,22 +58,22 @@ type Engine interface { Start(ctx context.Context, config *NodeConfig) error Stop(ctx context.Context) error Restart(ctx context.Context) error - + // Status Health(ctx context.Context) (*HealthStatus, error) IsRunning() bool Uptime() time.Duration - + // Network info NetworkID() uint32 ChainID() ids.ID RPCEndpoint() string WSEndpoint() string P2PEndpoint() string - + // Chain relationships ParentChain() *ChainInfo // nil for L1s - + // Metrics Metrics() map[string]interface{} } @@ -95,4 +95,4 @@ func New(typ EngineType, name string, binary string) (Engine, error) { return nil, fmt.Errorf("unknown engine type: %s", typ) } return factory(name, binary) -} \ No newline at end of file +} diff --git a/examples/local/fivenodenetwork/main.go b/examples/local/fivenodenetwork/main.go index 689bedf3..5981a1ab 100644 --- a/examples/local/fivenodenetwork/main.go +++ b/examples/local/fivenodenetwork/main.go @@ -9,10 +9,10 @@ import ( "syscall" "time" - "github.com/luxfi/netrunner/local" - "github.com/luxfi/netrunner/network" "github.com/luxfi/log" "github.com/luxfi/log/level" + "github.com/luxfi/netrunner/local" + "github.com/luxfi/netrunner/network" ) const ( diff --git a/examples/local/indepth/main.go b/examples/local/indepth/main.go index 231d0eb9..a814ebf9 100644 --- a/examples/local/indepth/main.go +++ b/examples/local/indepth/main.go @@ -8,13 +8,13 @@ import ( "syscall" "time" + "github.com/luxfi/log" + "github.com/luxfi/log/level" "github.com/luxfi/netrunner/local" "github.com/luxfi/netrunner/network" "github.com/luxfi/netrunner/network/node" "github.com/luxfi/node/config" "github.com/luxfi/node/staking" - "github.com/luxfi/log" - "github.com/luxfi/log/level" ) const ( diff --git a/examples/local/l2chains/main.go b/examples/local/l2chains/main.go index 51049a88..8804ad21 100644 --- a/examples/local/l2chains/main.go +++ b/examples/local/l2chains/main.go @@ -12,11 +12,11 @@ import ( "syscall" "time" + "github.com/luxfi/log" + "github.com/luxfi/log/level" "github.com/luxfi/netrunner/local" "github.com/luxfi/netrunner/network" "github.com/luxfi/node/config" - "github.com/luxfi/log" - "github.com/luxfi/log/level" ) const ( @@ -209,9 +209,9 @@ func run(logger log.Logger) error { createCtx, createCancel := context.WithTimeout(context.Background(), createBlockchainTimeout) chainSpec := []network.ChainSpec{ { - VMName: chain.VMName, - Genesis: genesis, - Alias: chain.Alias, + VMName: chain.VMName, + Genesis: genesis, + Alias: chain.Alias, BlockchainName: chain.Alias, // Use unique name for each chain }, } diff --git a/go.mod b/go.mod index 99fc07c8..0a75e4b1 100644 --- a/go.mod +++ b/go.mod @@ -15,11 +15,11 @@ require ( github.com/luxfi/consensus v1.22.47 github.com/luxfi/const v1.4.0 github.com/luxfi/crypto v1.17.32 - github.com/luxfi/genesis v1.5.18 + github.com/luxfi/genesis v1.5.19 github.com/luxfi/geth v1.16.67 github.com/luxfi/go-bip39 v1.1.2 github.com/luxfi/ids v1.2.6 - github.com/luxfi/keys v1.0.5 + github.com/luxfi/keys v1.0.6 github.com/luxfi/log v1.2.1 github.com/luxfi/math v1.2.0 github.com/luxfi/metric v1.4.8 diff --git a/go.sum b/go.sum index 3e7afda5..00a322aa 100644 --- a/go.sum +++ b/go.sum @@ -249,8 +249,8 @@ github.com/luxfi/crypto v1.17.32 h1:qEKDETNMeU7T82UMEEGYh0bbJ0ZZdx0sAMRwFXt69aQ= github.com/luxfi/crypto v1.17.32/go.mod h1:laZTnEq0BD/fnPhd5ke/79WgFhV3TZjmfPxk+b8zbrE= github.com/luxfi/database v1.2.18 h1:yppRuZf3FKnr3v4o+UHNfSW+cXtUIUibtyki0+EqiIs= github.com/luxfi/database v1.2.18/go.mod h1:dYxaF30KGJQzuxA3VH7+WGU5/dhwRyzLgzSk9F3wje0= -github.com/luxfi/genesis v1.5.18 h1:DDNFDyYT+AtSX78XBV1nGeuvR4404XPAkXnnrlJSV78= -github.com/luxfi/genesis v1.5.18/go.mod h1:888i1w8e67HIpgLuveIdd+3ESWCXHgnz7cijF6dDAKI= +github.com/luxfi/genesis v1.5.19 h1:q9ETV3NXTlJBhhkVKYDidaQqBDb1jgY3TdaLPfBGu0k= +github.com/luxfi/genesis v1.5.19/go.mod h1:888i1w8e67HIpgLuveIdd+3ESWCXHgnz7cijF6dDAKI= github.com/luxfi/geth v1.16.67 h1:ILPG59XNnNKIz1YNO1fJFV+doZXBjcQkb4oXQyz1HUM= github.com/luxfi/geth v1.16.67/go.mod h1:QWPyY3vlGQfm8SX/PE6q4XHTBe7X4Gw+SGDJi5QU1pg= github.com/luxfi/go-bip32 v1.0.2 h1:7vFbb+Wr4Z499q2tuCLdd7wWjtn8sH+HWBlx76mhH9Y= @@ -261,8 +261,8 @@ github.com/luxfi/ids v1.2.6 h1:Ru86pltAcWPhCT7FH/MwwuAYXkeZbt9ZwfuR883c8MI= github.com/luxfi/ids v1.2.6/go.mod h1:svLsj7e6ixJVfRYaQqv9RWjBIW11Vz738+d08BVpeCg= github.com/luxfi/keychain v1.0.1 h1:8tTzPnZSi0M6UtKRBCypnGFrLNry9BvWfS5ol33yDF0= github.com/luxfi/keychain v1.0.1/go.mod h1:X7HLk5QRZ1VHJxovAyBL6z7f/nlosoXlveEtvUGhoaw= -github.com/luxfi/keys v1.0.5 h1:ZzE2wHDxSiIcufkYlp7twmTKGB0mczjE67uM3uAP2Ag= -github.com/luxfi/keys v1.0.5/go.mod h1:hVqfOm6n4/ZN2O6AirlLEpgBhW8bsxNo2EbiNvggGUk= +github.com/luxfi/keys v1.0.6 h1:mKB0xjQoNpJxj59UNfdPfuqUoRT48Itt7PvRXU+X3iY= +github.com/luxfi/keys v1.0.6/go.mod h1:hVqfOm6n4/ZN2O6AirlLEpgBhW8bsxNo2EbiNvggGUk= github.com/luxfi/log v1.2.1 h1:L2JM8MjGPF8rBnY+EXODS9x5OQTGSgeQ1SPFmeB+oPY= github.com/luxfi/log v1.2.1/go.mod h1:0vJzb4Hl9fvgwWDMKcbCD/SYH3bO0iSmgZMcYdfdl1o= github.com/luxfi/math v1.2.0 h1:cZVQQj1PrIWh3Oy6xfbBL44xay4QFuAXZBg9+pWPBmE= diff --git a/local/blockchain.go b/local/blockchain.go index 6a227059..4f23260e 100644 --- a/local/blockchain.go +++ b/local/blockchain.go @@ -26,26 +26,27 @@ import ( "github.com/luxfi/node/vms/components/verify" "github.com/luxfi/node/wallet/chain/x" + "maps" + "slices" + + luxconfig "github.com/luxfi/config" + constants "github.com/luxfi/const" + "github.com/luxfi/crypto/bls" + "github.com/luxfi/crypto/secp256k1" + "github.com/luxfi/ids" + "github.com/luxfi/log" + "github.com/luxfi/math/set" "github.com/luxfi/netrunner/network" "github.com/luxfi/netrunner/network/node" "github.com/luxfi/netrunner/utils" - luxconfig "github.com/luxfi/config" "github.com/luxfi/node/api/admin" "github.com/luxfi/node/config" - "github.com/luxfi/crypto/secp256k1" - "github.com/luxfi/ids" - "github.com/luxfi/const" - "github.com/luxfi/math/set" - "github.com/luxfi/crypto/bls" - "github.com/luxfi/log" "github.com/luxfi/node/vms/platformvm" "github.com/luxfi/node/vms/platformvm/signer" "github.com/luxfi/node/vms/platformvm/txs" "github.com/luxfi/node/vms/secp256k1fx" pwallet "github.com/luxfi/node/wallet/chain/p" pwalletwallet "github.com/luxfi/node/wallet/chain/p/wallet" - "maps" - "slices" pbuilder "github.com/luxfi/node/wallet/chain/p/builder" psigner "github.com/luxfi/node/wallet/chain/p/signer" @@ -73,7 +74,7 @@ const ( // Most P-chain API calls should complete in <5s on localhost defaultTimeout = 30 * time.Second stakingMinimumLeadTime = 25 * time.Second - minStakeDuration = 24 * 14 * time.Hour + minStakeDuration = 24 * 14 * time.Hour // FAIL FAST: Reduced from 45s to 30s for local deployments // For local networks, validators activate quickly (5-10s). If it takes longer, // something is wrong - fail fast with clear error rather than waiting forever. @@ -88,7 +89,7 @@ var ( type blockchainInfo struct { chainName string vmID ids.ID - chainID ids.ID + chainID ids.ID blockchainID ids.ID } @@ -963,16 +964,16 @@ func (ln *localNetwork) installCustomChains( // This ensures P-Chain is synchronized across all nodes after blockchain creation // TEMPORARILY DISABLED - debugging P-Chain sync issue /* - blockchainIDs := make([]ids.ID, len(blockchainTxs)) - for i, tx := range blockchainTxs { - blockchainIDs[i] = tx.ID() - } - if err := ln.waitForBlockchainOnPChain(ctx, blockchainIDs); err != nil { - ln.logger.Error("installCustomChains: P-Chain sync failed", - "error", err.Error(), - ) - return nil, fmt.Errorf("P-Chain sync failed: %w", err) - } + blockchainIDs := make([]ids.ID, len(blockchainTxs)) + for i, tx := range blockchainTxs { + blockchainIDs[i] = tx.ID() + } + if err := ln.waitForBlockchainOnPChain(ctx, blockchainIDs); err != nil { + ln.logger.Error("installCustomChains: P-Chain sync failed", + "error", err.Error(), + ) + return nil, fmt.Errorf("P-Chain sync failed: %w", err) + } */ // Nodes need to be restarted after blockchain creation to discover the new chains @@ -1032,7 +1033,7 @@ func (ln *localNetwork) installCustomChains( // as there is no way to recover VM name from VM ID chainName: chainSpec.VMName, vmID: vmID, - chainID: chainID, + chainID: chainID, blockchainID: blockchainTxs[i].ID(), } } @@ -1333,7 +1334,7 @@ func getDefaultKey() (*secp256k1.PrivateKey, error) { // (keys.DeriveValidatorFromMnemonic uses m/44'/9000'/0'/0/{index}) if mnemonic := os.Getenv("LUX_MNEMONIC"); mnemonic != "" { fmt.Printf("๐Ÿ”‘ getDefaultKey: Using LUX_MNEMONIC (len=%d)\n", len(mnemonic)) - + // Use the SAME derivation function as genesis allocations // This ensures wallet operations use keys that have funds allocated in genesis vk, err := keys.DeriveValidatorFromMnemonic(mnemonic, 0) @@ -1344,11 +1345,11 @@ func getDefaultKey() (*secp256k1.PrivateKey, error) { if err != nil { return nil, fmt.Errorf("failed to convert key: %w", err) } - + pubKey := privKey.PublicKey() walletAddr := ids.ShortID(pubKey.Address()) fmt.Printf("๐Ÿ”‘ Wallet address (from mnemonic m/44'/9000'/0'/0/0): %s\n", walletAddr.String()) - + return privKey, nil } @@ -1435,9 +1436,9 @@ func newWallet( } pTXs[id] = tx } - pUTXOs := common.NewChainUTXOs(constants.PlatformChainID, luxState.UTXOs) + pUTXOs := common.NewChainUTXOs(constants.PlatformChainID, luxState.UTXOs) xChainID := luxState.XCTX.BlockchainID - xUTXOs := common.NewChainUTXOs(xChainID, luxState.UTXOs) + xUTXOs := common.NewChainUTXOs(xChainID, luxState.UTXOs) var w wallet w.addr = privKey.PublicKey().Address() // TODO: Create owners map instead of pTXs diff --git a/local/genesis_config.go b/local/genesis_config.go index 063b2e21..a176cb57 100644 --- a/local/genesis_config.go +++ b/local/genesis_config.go @@ -13,10 +13,11 @@ import ( "path/filepath" "time" - "github.com/luxfi/const" + "maps" + + constants "github.com/luxfi/const" "github.com/luxfi/crypto/bls" "github.com/luxfi/crypto/bls/signer/localsigner" - "github.com/luxfi/node/vms/platformvm/signer" luxcrypto "github.com/luxfi/crypto/secp256k1" "github.com/luxfi/genesis/configs" "github.com/luxfi/ids" @@ -25,7 +26,7 @@ import ( "github.com/luxfi/netrunner/network/node" "github.com/luxfi/netrunner/utils" "github.com/luxfi/node/config" - "maps" + "github.com/luxfi/node/vms/platformvm/signer" "github.com/luxfi/node/staking" "github.com/luxfi/node/utils/formatting/address" @@ -267,12 +268,10 @@ func NewConfigForNetwork(binaryPath string, numNodes uint32, networkID uint32) ( m["allocations"] = mustJSON(newAllocations) } - // Check if we need to generate initialStakers - var existingStakers []interface{} - if raw, ok := m["initialStakers"]; ok { - json.Unmarshal(raw, &existingStakers) - } - if len(existingStakers) == 0 { + // ALWAYS replace initialStakers with embedded validator keys for local network testing. + // The genesis file may have different validators than netrunner's embedded keys, + // so we must replace them to ensure nodes can actually validate. + { // Generate initialStakers from embedded validator keys var generatedStakers []map[string]interface{} for i := uint32(0); i < numEmbeddedToUse; i++ { @@ -423,6 +422,10 @@ func NewCanonicalCustomConfig(binaryPath string, numNodes uint32) (network.Confi // newCanonicalConfig creates a network config with canonical (pre-serialized) genesis bytes // and loads validator keys from ~/.lux/keys/node{0..n-1}/ +// +// IMPORTANT: This function patches the genesis initialStakers with the BLS keys from the +// loaded validator keys. This ensures the validators' BLS Proof of Possession (PoP) in genesis +// matches the actual signer.key files the nodes will use. func newCanonicalConfig(binaryPath string, numNodes uint32, networkID uint32, portBase int) (network.Config, error) { // Load CANONICAL genesis bytes - no parsing/re-serialization originalGenesisBytes, err := configs.GetCanonicalGenesisBytes(networkID) @@ -430,13 +433,61 @@ func newCanonicalConfig(binaryPath string, numNodes uint32, networkID uint32, po return network.Config{}, fmt.Errorf("failed to load canonical genesis: %w", err) } - // For LOCAL TESTING: Update startTime to now so bootstrap starts immediately. + // Load validator keys from ~/.lux/keys/ FIRST so we can patch genesis with them + keysDir := os.Getenv("LUX_KEYS_DIR") + if keysDir == "" { + keysDir = validatorKeysDir() + } + ks := keys.NewKeyStore(keysDir) + + // Load all validator keys and build initialStakers + hrp := constants.GetHRP(networkID) + validatorKeys := make([]*keys.ValidatorKey, numNodes) + initialStakers := make([]map[string]interface{}, numNodes) + + for i := uint32(0); i < numNodes; i++ { + name := fmt.Sprintf("node%d", i) + vk, err := ks.Load(name) + if err != nil { + return network.Config{}, fmt.Errorf("failed to load validator key %s from %s: %w (run 'lux key generate' first)", name, keysDir, err) + } + validatorKeys[i] = vk + + // Build initialStaker entry with BLS keys from loaded validator + rewardAddr, err := address.Format("P", hrp, vk.PChainAddr[:]) + if err != nil { + return network.Config{}, fmt.Errorf("couldn't format reward address for node %d: %w", i, err) + } + + staker := map[string]interface{}{ + "nodeID": vk.NodeID.String(), + "rewardAddress": rewardAddr, + "delegationFee": 20000, + "weight": GigaLux, // 1 billion nLUX = 1000 LUX + } + + if len(vk.BLSPublicKey) > 0 && len(vk.BLSPoP) > 0 { + staker["signer"] = map[string]interface{}{ + "publicKey": vk.BLSPublicKeyHex(), + "proofOfPossession": vk.BLSPoPHex(), + } + fmt.Printf(" Validator %d: %s (BLS: %s...)\n", i, vk.NodeID.String(), vk.BLSPublicKeyHex()[:20]) + } else { + fmt.Printf(" Validator %d: %s (no BLS key)\n", i, vk.NodeID.String()) + } + + initialStakers[i] = staker + } + + // For LOCAL TESTING: Update startTime to now AND patch initialStakers with our keys // CRITICAL: Use patchGenesisPreservingRaw to preserve cChainGenesis exact bytes. now := time.Now().Unix() fmt.Printf("๐Ÿ“… Updated genesis startTime to %d (now) for local testing\n", now) + fmt.Printf("๐Ÿ”‘ Patching genesis initialStakers with %d validators from %s\n", numNodes, keysDir) genesisBytes, err := patchGenesisPreservingRaw(originalGenesisBytes, evmGenesisKeys, func(m map[string]json.RawMessage) error { m["startTime"] = mustJSON(uint64(now)) + m["initialStakers"] = mustJSON(initialStakers) return nil }) if err != nil { @@ -447,22 +498,10 @@ func newCanonicalConfig(binaryPath string, numNodes uint32, networkID uint32, po netConfig := NewDefaultConfig(binaryPath) netConfig.Genesis = string(genesisBytes) - // Load validator keys from ~/.lux/keys/ - keysDir := os.Getenv("LUX_KEYS_DIR") - if keysDir == "" { - keysDir = validatorKeysDir() - } - ks := keys.NewKeyStore(keysDir) - - // Load keys for each node + // Build node configs from loaded keys nodeConfigs := make([]node.Config, numNodes) for i := uint32(0); i < numNodes; i++ { - name := fmt.Sprintf("node%d", i) - vk, err := ks.Load(name) - if err != nil { - return network.Config{}, fmt.Errorf("failed to load validator key %s from %s: %w (run 'lux key generate' first)", name, keysDir, err) - } - + vk := validatorKeys[i] port := portBase + int(i)*2 nodeConfigs[i] = node.Config{ Flags: map[string]interface{}{ @@ -477,11 +516,10 @@ func newCanonicalConfig(binaryPath string, numNodes uint32, networkID uint32, po UpgradeConfigFiles: map[string]string{}, PChainConfigFiles: map[string]string{}, } - fmt.Printf(" Loaded validator %d: %s\n", i, vk.NodeID.String()) } netConfig.NodeConfigs = nodeConfigs - fmt.Printf("โœ… Loaded canonical genesis for network %d with %d validators\n", networkID, numNodes) + fmt.Printf("โœ… Loaded canonical genesis for network %d with %d validators (BLS keys patched)\n", networkID, numNodes) return netConfig, nil } @@ -724,13 +762,13 @@ func NewConfigWithPreExistingKeys(binaryPath string, networkID uint32, keysDir s config.HTTPPortKey: port, config.StakingPortKey: port + 1, }, - StakingKey: string(vk.StakerKey), - StakingCert: string(vk.StakerCert), - StakingSigningKey: base64.StdEncoding.EncodeToString(vk.BLSSecretKey), - IsBeacon: true, - ChainConfigFiles: map[string]string{}, + StakingKey: string(vk.StakerKey), + StakingCert: string(vk.StakerCert), + StakingSigningKey: base64.StdEncoding.EncodeToString(vk.BLSSecretKey), + IsBeacon: true, + ChainConfigFiles: map[string]string{}, UpgradeConfigFiles: map[string]string{}, - PChainConfigFiles: map[string]string{}, + PChainConfigFiles: map[string]string{}, } } @@ -1016,8 +1054,7 @@ func NewConfigFromMnemonic(binaryPath string, networkID uint32, numNodes uint32) // Add per-node specific flags nodeFlags[config.HTTPPortKey] = port nodeFlags[config.StakingPortKey] = port + 1 - // Enable sybil protection for mainnet - validators have proper keys - // The genesis validators have matching NodeIDs from deterministic TLS certs + // Sybil protection MUST be enabled - validators come from genesis initialStakers nodeFlags[config.SybilProtectionEnabledKey] = true netConfig.NodeConfigs[i] = node.Config{ diff --git a/local/helpers.go b/local/helpers.go index c693b910..de28b079 100644 --- a/local/helpers.go +++ b/local/helpers.go @@ -11,7 +11,7 @@ import ( "path/filepath" "time" - "github.com/luxfi/const" + constants "github.com/luxfi/const" "github.com/luxfi/log" "github.com/luxfi/netrunner/network/node" "github.com/luxfi/netrunner/utils" diff --git a/local/mainnet_test.go b/local/mainnet_test.go index 56b70a42..b3fd11a2 100644 --- a/local/mainnet_test.go +++ b/local/mainnet_test.go @@ -13,9 +13,9 @@ import ( "time" "github.com/luxfi/genesis/configs" - "github.com/luxfi/netrunner/local" "github.com/luxfi/log" "github.com/luxfi/log/level" + "github.com/luxfi/netrunner/local" "github.com/stretchr/testify/require" ) diff --git a/local/mnemonic_test.go b/local/mnemonic_test.go index d55f2021..72bc0ef3 100644 --- a/local/mnemonic_test.go +++ b/local/mnemonic_test.go @@ -1,72 +1,72 @@ package local import ( - "fmt" - "os" - "testing" - - "github.com/btcsuite/btcd/btcutil/hdkeychain" - "github.com/btcsuite/btcd/chaincfg" - "github.com/luxfi/go-bip39" - luxcrypto "github.com/luxfi/crypto/secp256k1" - "golang.org/x/crypto/sha3" + "fmt" + "os" + "testing" + + "github.com/btcsuite/btcd/btcutil/hdkeychain" + "github.com/btcsuite/btcd/chaincfg" + luxcrypto "github.com/luxfi/crypto/secp256k1" + "github.com/luxfi/go-bip39" + "golang.org/x/crypto/sha3" ) func TestMnemonicDerivation(t *testing.T) { - mnemonic := os.Getenv("LUX_MNEMONIC") - if mnemonic == "" { - t.Skip("LUX_MNEMONIC not set") - } - - fmt.Printf("Mnemonic: %s...\n", mnemonic[:20]) - - if !bip39.IsMnemonicValid(mnemonic) { - t.Fatal("Invalid mnemonic") - } - - seed := bip39.NewSeed(mnemonic, "") - - // Derive using BIP44 m/44'/60'/0'/0/0 - masterKey, _ := hdkeychain.NewMaster(seed, &chaincfg.MainNetParams) - purpose, _ := masterKey.Derive(hdkeychain.HardenedKeyStart + 44) - coinType, _ := purpose.Derive(hdkeychain.HardenedKeyStart + 60) - account, _ := coinType.Derive(hdkeychain.HardenedKeyStart + 0) - change, _ := account.Derive(0) - addressKey, _ := change.Derive(0) - - ecPrivKey, _ := addressKey.ECPrivKey() - privKeyBytes := ecPrivKey.Serialize() - - privKey, err := luxcrypto.ToPrivateKey(privKeyBytes) - if err != nil { - t.Fatalf("Error: %v", err) - } - pubKey := privKey.PublicKey() - shortID := pubKey.Address() - - // Get Ethereum address - pubKeyBytes := pubKey.Bytes() - hash := sha3.NewLegacyKeccak256() - hash.Write(pubKeyBytes[1:]) // skip the 0x04 prefix - ethAddr := hash.Sum(nil)[12:] - - fmt.Printf("\n=== Key Derivation Results (m/44'/60'/0'/0/0) ===\n") - fmt.Printf("Lux Short ID: %s\n", shortID.String()) - fmt.Printf("Ethereum Address: 0x%x\n", ethAddr) - fmt.Printf("Expected Treasury: 0x9011E888251AB053B7bD1cdB598Db4f9DEd94714\n") - - // Also derive using coin type 9000 for comparison - coinType9000, _ := purpose.Derive(hdkeychain.HardenedKeyStart + 9000) - account9000, _ := coinType9000.Derive(hdkeychain.HardenedKeyStart + 0) - change9000, _ := account9000.Derive(0) - addressKey9000, _ := change9000.Derive(0) - ecPrivKey9000, _ := addressKey9000.ECPrivKey() - privKey9000, _ := luxcrypto.ToPrivateKey(ecPrivKey9000.Serialize()) - shortID9000 := privKey9000.PublicKey().Address() - - fmt.Printf("\n=== Comparison: Coin Type 9000 (m/44'/9000'/0'/0/0) ===\n") - fmt.Printf("Lux Short ID (9000): %s\n", shortID9000.String()) - - fmt.Printf("\n=== Genesis Treasury P-chain Address ===\n") - fmt.Printf("Expected: P-lux1jqg73zp9r2c98daarnd4nrd5l80dj3c5eha5fl\n") + mnemonic := os.Getenv("LUX_MNEMONIC") + if mnemonic == "" { + t.Skip("LUX_MNEMONIC not set") + } + + fmt.Printf("Mnemonic: %s...\n", mnemonic[:20]) + + if !bip39.IsMnemonicValid(mnemonic) { + t.Fatal("Invalid mnemonic") + } + + seed := bip39.NewSeed(mnemonic, "") + + // Derive using BIP44 m/44'/60'/0'/0/0 + masterKey, _ := hdkeychain.NewMaster(seed, &chaincfg.MainNetParams) + purpose, _ := masterKey.Derive(hdkeychain.HardenedKeyStart + 44) + coinType, _ := purpose.Derive(hdkeychain.HardenedKeyStart + 60) + account, _ := coinType.Derive(hdkeychain.HardenedKeyStart + 0) + change, _ := account.Derive(0) + addressKey, _ := change.Derive(0) + + ecPrivKey, _ := addressKey.ECPrivKey() + privKeyBytes := ecPrivKey.Serialize() + + privKey, err := luxcrypto.ToPrivateKey(privKeyBytes) + if err != nil { + t.Fatalf("Error: %v", err) + } + pubKey := privKey.PublicKey() + shortID := pubKey.Address() + + // Get Ethereum address + pubKeyBytes := pubKey.Bytes() + hash := sha3.NewLegacyKeccak256() + hash.Write(pubKeyBytes[1:]) // skip the 0x04 prefix + ethAddr := hash.Sum(nil)[12:] + + fmt.Printf("\n=== Key Derivation Results (m/44'/60'/0'/0/0) ===\n") + fmt.Printf("Lux Short ID: %s\n", shortID.String()) + fmt.Printf("Ethereum Address: 0x%x\n", ethAddr) + fmt.Printf("Expected Treasury: 0x9011E888251AB053B7bD1cdB598Db4f9DEd94714\n") + + // Also derive using coin type 9000 for comparison + coinType9000, _ := purpose.Derive(hdkeychain.HardenedKeyStart + 9000) + account9000, _ := coinType9000.Derive(hdkeychain.HardenedKeyStart + 0) + change9000, _ := account9000.Derive(0) + addressKey9000, _ := change9000.Derive(0) + ecPrivKey9000, _ := addressKey9000.ECPrivKey() + privKey9000, _ := luxcrypto.ToPrivateKey(ecPrivKey9000.Serialize()) + shortID9000 := privKey9000.PublicKey().Address() + + fmt.Printf("\n=== Comparison: Coin Type 9000 (m/44'/9000'/0'/0/0) ===\n") + fmt.Printf("Lux Short ID (9000): %s\n", shortID9000.String()) + + fmt.Printf("\n=== Genesis Treasury P-chain Address ===\n") + fmt.Printf("Expected: P-lux1jqg73zp9r2c98daarnd4nrd5l80dj3c5eha5fl\n") } diff --git a/local/network.go b/local/network.go index 4de936f2..9e643f85 100644 --- a/local/network.go +++ b/local/network.go @@ -19,6 +19,9 @@ import ( "sync" "time" + "maps" + "slices" + luxconfig "github.com/luxfi/config" "github.com/luxfi/crypto/bls" luxcrypto "github.com/luxfi/crypto/secp256k1" @@ -36,8 +39,6 @@ import ( "github.com/luxfi/node/network/peer" "github.com/luxfi/node/staking" "github.com/luxfi/node/utils/beacon" - "maps" - "slices" "github.com/luxfi/node/utils/formatting/address" "github.com/luxfi/node/utils/wrappers" @@ -55,12 +56,12 @@ const ( genesisFileName = "genesis.json" stopTimeout = 30 * time.Second // healthCheckFreq reduced from 3s to 1s for faster health polling - healthCheckFreq = 1 * time.Second - DefaultNumNodes = 5 - snapshotPrefix = "lux-snapshot-" - networkRootDirPrefix = "network" - defaultDBSubdir = "db" - defaultLogsSubdir = "logs" + healthCheckFreq = 1 * time.Second + DefaultNumNodes = 5 + snapshotPrefix = "lux-snapshot-" + networkRootDirPrefix = "network" + defaultDBSubdir = "db" + defaultLogsSubdir = "logs" // difference between unlock schedule locktime and startime in original genesis genesisLocktimeStartimeDelta = 2836800 ) @@ -75,8 +76,8 @@ var ( config.BootstrapIPsKey: {}, config.BootstrapIDsKey: {}, } - chainConfigSubDir = "chainConfigs" - pChainConfigSubDir = "pChainConfigs" + chainConfigSubDir = "chainConfigs" + pChainConfigSubDir = "pChainConfigs" snapshotsRelPath = filepath.Join(".netrunner", "snapshots") @@ -85,7 +86,7 @@ var ( // network keeps information uses for network management, and accessing all the nodes type localNetwork struct { - lock sync.RWMutex + lock sync.RWMutex logger log.Logger // This network's ID. networkID uint32 @@ -261,7 +262,7 @@ func init() { "C": string(cChainConfig), }, UpgradeConfigFiles: map[string]string{}, - PChainConfigFiles: map[string]string{}, + PChainConfigFiles: map[string]string{}, } for i := 0; i < len(defaultNetworkConfig.NodeConfigs); i++ { @@ -366,16 +367,16 @@ func newNetwork( } // Create the network net := &localNetwork{ - nextNodeSuffix: 1, - nodes: map[string]*localNode{}, - onStopCh: make(chan struct{}), - logger: logger, - bootstraps: beacon.NewSet(), - newAPIClientF: newAPIClientF, - nodeProcessCreator: nodeProcessCreator, - rootDir: rootDir, - snapshotsDir: snapshotsDir, - reassignPortsIfUsed: reassignPortsIfUsed, + nextNodeSuffix: 1, + nodes: map[string]*localNode{}, + onStopCh: make(chan struct{}), + logger: logger, + bootstraps: beacon.NewSet(), + newAPIClientF: newAPIClientF, + nodeProcessCreator: nodeProcessCreator, + rootDir: rootDir, + snapshotsDir: snapshotsDir, + reassignPortsIfUsed: reassignPortsIfUsed, chainID2ElasticChainID: map[ids.ID]ids.ID{}, } return net, nil diff --git a/local/network_test.go b/local/network_test.go index 49a88047..31a331c5 100644 --- a/local/network_test.go +++ b/local/network_test.go @@ -13,6 +13,8 @@ import ( "testing" "time" + "github.com/luxfi/ids" + "github.com/luxfi/log" "github.com/luxfi/netrunner/api" apimocks "github.com/luxfi/netrunner/api/mocks" "github.com/luxfi/netrunner/local/mocks" @@ -23,10 +25,8 @@ import ( "github.com/luxfi/netrunner/utils" "github.com/luxfi/node/api/health" "github.com/luxfi/node/config" - "github.com/luxfi/ids" "github.com/luxfi/node/message" "github.com/luxfi/node/network/peer" - "github.com/luxfi/log" "github.com/luxfi/node/utils/rpc" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -38,12 +38,12 @@ const ( ) var ( - _ NodeProcessCreator = &localTestSuccessfulNodeProcessCreator{} - _ NodeProcessCreator = &localTestFailedStartProcessCreator{} - _ NodeProcessCreator = &localTestProcessUndefNodeProcessCreator{} - _ NodeProcessCreator = &localTestFlagCheckProcessCreator{} - _ api.NewAPIClientF = newMockAPISuccessful - _ api.NewAPIClientF = newMockAPIUnhealthy + _ NodeProcessCreator = &localTestSuccessfulNodeProcessCreator{} + _ NodeProcessCreator = &localTestFailedStartProcessCreator{} + _ NodeProcessCreator = &localTestProcessUndefNodeProcessCreator{} + _ NodeProcessCreator = &localTestFlagCheckProcessCreator{} + _ api.NewAPIClientF = newMockAPISuccessful + _ api.NewAPIClientF = newMockAPIUnhealthy _ peer.InboundHandler = &noOpInboundHandler{} ) @@ -1185,9 +1185,9 @@ func TestWriteFiles(t *testing.T) { config.StakingTLSKeyPathKey: stakingKeyPath, config.StakingCertPathKey: stakingCertPath, config.StakingSignerKeyPathKey: stakingSigningKeyPath, - config.GenesisFileKey: genesisPath, + config.GenesisFileKey: genesisPath, config.ChainConfigDirKey: chainConfigDir, - config.NetConfigDirKey: subnetConfigDir, + config.NetConfigDirKey: subnetConfigDir, }, }, { @@ -1203,9 +1203,9 @@ func TestWriteFiles(t *testing.T) { config.StakingTLSKeyPathKey: stakingKeyPath, config.StakingCertPathKey: stakingCertPath, config.StakingSignerKeyPathKey: stakingSigningKeyPath, - config.GenesisFileKey: genesisPath, + config.GenesisFileKey: genesisPath, config.ChainConfigDirKey: chainConfigDir, - config.NetConfigDirKey: subnetConfigDir, + config.NetConfigDirKey: subnetConfigDir, config.ConfigFileKey: configFilePath, }, }, @@ -1223,9 +1223,9 @@ func TestWriteFiles(t *testing.T) { config.StakingTLSKeyPathKey: stakingKeyPath, config.StakingCertPathKey: stakingCertPath, config.StakingSignerKeyPathKey: stakingSigningKeyPath, - config.GenesisFileKey: genesisPath, + config.GenesisFileKey: genesisPath, config.ChainConfigDirKey: chainConfigDir, - config.NetConfigDirKey: subnetConfigDir, + config.NetConfigDirKey: subnetConfigDir, config.ConfigFileKey: configFilePath, }, }, diff --git a/local/node.go b/local/node.go index 1578425b..91942bd3 100644 --- a/local/node.go +++ b/local/node.go @@ -9,23 +9,23 @@ import ( "net/netip" "time" + validators "github.com/luxfi/consensus/validator" // package name is validators + constants "github.com/luxfi/const" + "github.com/luxfi/crypto/bls" + "github.com/luxfi/ids" + "github.com/luxfi/log" + "github.com/luxfi/math/set" + "github.com/luxfi/metric" "github.com/luxfi/netrunner/api" "github.com/luxfi/netrunner/network/node" "github.com/luxfi/netrunner/network/node/status" - "github.com/luxfi/const" - "github.com/luxfi/ids" "github.com/luxfi/node/message" "github.com/luxfi/node/network/peer" "github.com/luxfi/node/network/throttling" "github.com/luxfi/node/network/tracker" - "github.com/luxfi/node/utils/compression" - "github.com/luxfi/consensus/validator" // package name is validators "github.com/luxfi/node/staking" "github.com/luxfi/node/utils" - "github.com/luxfi/crypto/bls" - "github.com/luxfi/log" - "github.com/luxfi/math/set" - "github.com/luxfi/metric" + "github.com/luxfi/node/utils/compression" "github.com/luxfi/node/version" "github.com/prometheus/client_golang/prometheus" ) diff --git a/local/node_process.go b/local/node_process.go index d701e4a0..d5547c8a 100644 --- a/local/node_process.go +++ b/local/node_process.go @@ -8,10 +8,10 @@ import ( "os/exec" "sync" + "github.com/luxfi/log" "github.com/luxfi/netrunner/network/node" "github.com/luxfi/netrunner/network/node/status" "github.com/luxfi/netrunner/utils" - "github.com/luxfi/log" "github.com/shirou/gopsutil/process" ) @@ -81,10 +81,10 @@ func (npc *nodeProcessCreator) NewNodeProcess(config node.Config, args ...string } type nodeProcess struct { - name string - logger log.Logger - lock sync.RWMutex - cmd *exec.Cmd + name string + logger log.Logger + lock sync.RWMutex + cmd *exec.Cmd // Process status state status.Status // Closed when the process exits. diff --git a/local/node_test.go b/local/node_test.go index 22fd548c..4c7f88b2 100644 --- a/local/node_test.go +++ b/local/node_test.go @@ -15,18 +15,16 @@ import ( "testing" "time" - "github.com/luxfi/netrunner/network/node" + constants "github.com/luxfi/const" + "github.com/luxfi/geth/metrics" "github.com/luxfi/ids" + "github.com/luxfi/netrunner/network/node" "github.com/luxfi/node/message" "github.com/luxfi/node/network/peer" "github.com/luxfi/node/staking" - "github.com/luxfi/const" "github.com/luxfi/node/utils/ips" - luxlog "github.com/luxfi/log" "github.com/luxfi/node/utils/wrappers" "github.com/luxfi/node/version" - "github.com/luxfi/metric" - "github.com/prometheus/client_golang/prometheus" "github.com/stretchr/testify/require" ) diff --git a/local/snapshot.go b/local/snapshot.go index 2c8e2555..7519f03d 100644 --- a/local/snapshot.go +++ b/local/snapshot.go @@ -9,16 +9,17 @@ import ( "path/filepath" "strings" + "maps" + "slices" + luxconfig "github.com/luxfi/config" - "github.com/luxfi/const" + constants "github.com/luxfi/const" "github.com/luxfi/ids" "github.com/luxfi/log" "github.com/luxfi/netrunner/api" "github.com/luxfi/netrunner/network" "github.com/luxfi/netrunner/network/node" "github.com/luxfi/netrunner/utils" - "maps" - "slices" "github.com/luxfi/node/config" dircopy "github.com/otiai10/copy" diff --git a/local/wallet_client.go b/local/wallet_client.go index 6c368994..a0afa05f 100644 --- a/local/wallet_client.go +++ b/local/wallet_client.go @@ -31,11 +31,11 @@ func (w *walletClient) IssueTx(tx *txs.Tx, options ...common.Option) error { if ctx == nil { ctx = context.Background() } - + // Serialize the transaction txBytes := tx.Bytes() - + // Issue through platform client _, err := w.platformClient.IssueTx(ctx, txBytes) return err -} \ No newline at end of file +} diff --git a/multinet/manager.go b/multinet/manager.go index b0d99d13..7b4d06e5 100644 --- a/multinet/manager.go +++ b/multinet/manager.go @@ -9,9 +9,9 @@ import ( "time" "github.com/dgraph-io/badger/v4" - "github.com/luxfi/netrunner/network" "github.com/luxfi/ids" "github.com/luxfi/log" + "github.com/luxfi/netrunner/network" ) // NetworkType represents the type of network @@ -19,29 +19,29 @@ type NetworkType string const ( NetworkTypePrimary NetworkType = "primary" // Mainnet or Testnet - NetworkTypeChain NetworkType = "chain" // L1/L2 chains + NetworkTypeChain NetworkType = "chain" // L1/L2 chains ) // NetworkConfig defines configuration for a single network type NetworkConfig struct { - NetworkID uint32 `json:"networkID"` - Name string `json:"name"` - Type NetworkType `json:"type"` - ParentID uint32 `json:"parentID,omitempty"` // For chains - HTTPPort int `json:"httpPort"` - StakingPort int `json:"stakingPort"` - DataDir string `json:"dataDir"` - Validators int `json:"validators"` + NetworkID uint32 `json:"networkID"` + Name string `json:"name"` + Type NetworkType `json:"type"` + ParentID uint32 `json:"parentID,omitempty"` // For chains + HTTPPort int `json:"httpPort"` + StakingPort int `json:"stakingPort"` + DataDir string `json:"dataDir"` + Validators int `json:"validators"` ChainConfig []ChainConfig `json:"chains,omitempty"` } // ChainConfig defines configuration for a chain within a network type ChainConfig struct { - ChainID string `json:"chainID"` - VMID string `json:"vmID"` - PChainID string `json:"pChainID,omitempty"` - Genesis []byte `json:"genesis,omitempty"` - IsEVM bool `json:"isEVM"` + ChainID string `json:"chainID"` + VMID string `json:"vmID"` + PChainID string `json:"pChainID,omitempty"` + Genesis []byte `json:"genesis,omitempty"` + IsEVM bool `json:"isEVM"` } // MultiNetworkManager manages multiple networks running in parallel @@ -73,7 +73,7 @@ type NetworkInstance struct { Config NetworkConfig Network network.Network Status NetworkStatus - Chains map[string]*ChainInstance + Chains map[string]*ChainInstance StartTime time.Time } @@ -107,13 +107,13 @@ type ConsensusManager struct { // ConsensusParams defines consensus parameters type ConsensusParams struct { - K int `json:"k"` - Alpha int `json:"alpha"` - BetaVirtuous int `json:"betaVirtuous"` - BetaRogue int `json:"betaRogue"` - ConcurrentPolls int `json:"concurrentPolls"` - OptimalProcessing int `json:"optimalProcessing"` - MaxProcessing int `json:"maxProcessing"` + K int `json:"k"` + Alpha int `json:"alpha"` + BetaVirtuous int `json:"betaVirtuous"` + BetaRogue int `json:"betaRogue"` + ConcurrentPolls int `json:"concurrentPolls"` + OptimalProcessing int `json:"optimalProcessing"` + MaxProcessing int `json:"maxProcessing"` MaxTimeProcessing time.Duration `json:"maxTimeProcessing"` } @@ -146,10 +146,10 @@ type CrossChainTx struct { type TxStatus string const ( - TxStatusPending TxStatus = "pending" + TxStatusPending TxStatus = "pending" TxStatusValidating TxStatus = "validating" - TxStatusCommitted TxStatus = "committed" - TxStatusFailed TxStatus = "failed" + TxStatusCommitted TxStatus = "committed" + TxStatusFailed TxStatus = "failed" ) // NewMultiNetworkManager creates a new multi-network manager @@ -170,7 +170,7 @@ func NewMultiNetworkManager(logger log.Logger, sharedDBPath string) (*MultiNetwo networks: make(map[uint32]*NetworkInstance), consensusManagers: make(map[uint32]*ConsensusManager), txCoordinator: NewCrossChainTxCoordinator(sharedDB), - logger: logger, + logger: logger, ctx: ctx, cancel: cancel, }, nil @@ -195,8 +195,8 @@ func (m *MultiNetworkManager) AddNetwork(config NetworkConfig) error { } instance := &NetworkInstance{ - Config: config, - Status: NetworkStatusStopped, + Config: config, + Status: NetworkStatusStopped, Chains: make(map[string]*ChainInstance), } @@ -471,4 +471,4 @@ func (m *MultiNetworkManager) Shutdown() error { m.logger.Info("Multi-network manager shutdown complete") return nil -} \ No newline at end of file +} diff --git a/network/config.go b/network/config.go index a56af56a..7903a2e0 100644 --- a/network/config.go +++ b/network/config.go @@ -8,11 +8,12 @@ import ( "strconv" "time" - "github.com/luxfi/const" + "maps" + + constants "github.com/luxfi/const" "github.com/luxfi/genesis/pkg/genesis" "github.com/luxfi/ids" "github.com/luxfi/netrunner/network/node" - "maps" "github.com/luxfi/netrunner/utils" "github.com/luxfi/node/utils/formatting/address" @@ -152,8 +153,8 @@ func NewGenesis( NetworkID: networkID, Allocations: []genesis.UnparsedAllocation{ { - ETHAddr: zeroEthAddr, // Zero address as hex string - LUXAddr: genesisVdrStakeAddr, // Bech32 formatted address + ETHAddr: zeroEthAddr, // Zero address as hex string + LUXAddr: genesisVdrStakeAddr, // Bech32 formatted address InitialAmount: 0, UnlockSchedule: []genesis.LockedAmount{ // Provides stake to validators { diff --git a/network/network.go b/network/network.go index 032e32b3..ae8a4a61 100644 --- a/network/network.go +++ b/network/network.go @@ -5,8 +5,8 @@ import ( "errors" "time" - "github.com/luxfi/netrunner/network/node" "github.com/luxfi/ids" + "github.com/luxfi/netrunner/network/node" ) var ( diff --git a/network/node/node.go b/network/node/node.go index e7f90bfd..db4ded0c 100644 --- a/network/node/node.go +++ b/network/node/node.go @@ -6,10 +6,10 @@ import ( "errors" "fmt" + "github.com/luxfi/ids" "github.com/luxfi/netrunner/api" "github.com/luxfi/netrunner/network/node/status" "github.com/luxfi/node/config" - "github.com/luxfi/ids" "github.com/luxfi/node/network/peer" ) diff --git a/orchestrator/host.go b/orchestrator/host.go index 880e3ae6..95657a9c 100644 --- a/orchestrator/host.go +++ b/orchestrator/host.go @@ -16,12 +16,12 @@ import ( // Host manages multiple consensus engines type Host struct { - mu sync.RWMutex - engines map[string]engines.Engine - ports *PortManager - indexer *Indexer - metrics *MetricsCollector - networks map[string]*NetworkInfo + mu sync.RWMutex + engines map[string]engines.Engine + ports *PortManager + indexer *Indexer + metrics *MetricsCollector + networks map[string]*NetworkInfo } // NetworkInfo describes a network's properties @@ -62,12 +62,12 @@ type EngineOptions struct { func (h *Host) StartEngine(ctx context.Context, name string, typ string, options *EngineOptions) error { h.mu.Lock() defer h.mu.Unlock() - + // Check if already running if _, exists := h.engines[name]; exists { return fmt.Errorf("engine %s already running", name) } - + // Convert options to NodeConfig config := &engines.NodeConfig{ NetworkID: options.NetworkID, @@ -79,7 +79,7 @@ func (h *Host) StartEngine(ctx context.Context, name string, typ string, options BootstrapIPs: options.BootstrapIPs, Extra: options.Extra, } - + // Allocate ports if not specified if config.HTTPPort == 0 { config.HTTPPort = h.ports.AllocateHTTP() @@ -90,22 +90,22 @@ func (h *Host) StartEngine(ctx context.Context, name string, typ string, options if config.StakingPort == 0 { config.StakingPort = h.ports.AllocateP2P() } - + // Create engine using factory engine, err := engines.New(engines.EngineType(typ), name, options.Binary) if err != nil { return fmt.Errorf("failed to create engine: %w", err) } - + // Start engine if err := engine.Start(ctx, config); err != nil { h.ports.Release(config.HTTPPort) h.ports.Release(config.StakingPort) return fmt.Errorf("failed to start engine: %w", err) } - + h.engines[name] = engine - + // Register network info h.networks[name] = &NetworkInfo{ Name: name, @@ -115,10 +115,10 @@ func (h *Host) StartEngine(ctx context.Context, name string, typ string, options RPC: engine.RPCEndpoint(), WS: engine.WSEndpoint(), } - + // Start metrics collection go h.collectMetrics(name, engine) - + return nil } @@ -126,19 +126,19 @@ func (h *Host) StartEngine(ctx context.Context, name string, typ string, options func (h *Host) StopEngine(ctx context.Context, name string) error { h.mu.Lock() defer h.mu.Unlock() - + engine, exists := h.engines[name] if !exists { return fmt.Errorf("engine %s not found", name) } - + if err := engine.Stop(ctx); err != nil { return fmt.Errorf("failed to stop engine: %w", err) } - + delete(h.engines, name) delete(h.networks, name) - + return nil } @@ -146,7 +146,7 @@ func (h *Host) StopEngine(ctx context.Context, name string) error { func (h *Host) ListEngines() []EngineInfo { h.mu.RLock() defer h.mu.RUnlock() - + var infos []EngineInfo for name, engine := range h.engines { health, _ := engine.Health(context.Background()) @@ -182,7 +182,7 @@ func (h *Host) StartStack(ctx context.Context, manifest *StackManifest) error { if err := manifest.Validate(); err != nil { return fmt.Errorf("invalid manifest: %w", err) } - + // Start engines in order (L1 first, then L2, then L3) for _, engine := range manifest.Engines { config := &engines.NodeConfig{ @@ -194,7 +194,7 @@ func (h *Host) StartStack(ctx context.Context, manifest *StackManifest) error { BootstrapIPs: engine.BootstrapIPs, Extra: engine.Extra, } - + options := &EngineOptions{ NetworkID: config.NetworkID, HTTPPort: config.HTTPPort, @@ -207,26 +207,26 @@ func (h *Host) StartStack(ctx context.Context, manifest *StackManifest) error { } if err := h.StartEngine(ctx, engine.Name, engine.Type, options); err != nil { // Rollback on failure - h.StopStack(ctx, manifest.Name) + _ = h.StopStack(ctx, manifest.Name) return fmt.Errorf("failed to start %s: %w", engine.Name, err) } - + // Wait for health before starting dependent engines if engine.WaitHealthy { if err := h.waitHealthy(ctx, engine.Name, 30*time.Second); err != nil { - h.StopStack(ctx, manifest.Name) + _ = h.StopStack(ctx, manifest.Name) return fmt.Errorf("engine %s failed health check: %w", engine.Name, err) } } } - + // Configure bridges if specified if manifest.Bridge != nil { if err := h.configureBridge(ctx, manifest.Bridge); err != nil { return fmt.Errorf("failed to configure bridge: %w", err) } } - + return nil } @@ -240,7 +240,7 @@ func (h *Host) StopStack(ctx context.Context, stackName string) error { engines = append(engines, name) } h.mu.Unlock() - + var lastErr error for _, name := range engines { if err := h.StopEngine(ctx, name); err != nil { @@ -255,15 +255,15 @@ func (h *Host) waitHealthy(ctx context.Context, name string, timeout time.Durati h.mu.RLock() engine, exists := h.engines[name] h.mu.RUnlock() - + if !exists { return fmt.Errorf("engine %s not found", name) } - + deadline := time.Now().Add(timeout) ticker := time.NewTicker(1 * time.Second) defer ticker.Stop() - + for { select { case <-ticker.C: @@ -294,7 +294,7 @@ func (h *Host) configureBridge(ctx context.Context, bridge *BridgeConfig) error func (h *Host) collectMetrics(name string, engine engines.Engine) { ticker := time.NewTicker(10 * time.Second) defer ticker.Stop() - + for range ticker.C { h.mu.RLock() if _, exists := h.engines[name]; !exists { @@ -302,7 +302,7 @@ func (h *Host) collectMetrics(name string, engine engines.Engine) { return // Engine stopped } h.mu.RUnlock() - + metrics := engine.Metrics() h.metrics.Record(name, metrics) } @@ -328,7 +328,7 @@ func (h *Host) StopAll(ctx context.Context) error { engines = append(engines, name) } h.mu.Unlock() - + var lastErr error for _, name := range engines { if err := h.StopEngine(ctx, name); err != nil { @@ -348,7 +348,7 @@ type EngineStatus struct { func (h *Host) GetAllStatus(ctx context.Context) (map[string]*EngineStatus, error) { h.mu.RLock() defer h.mu.RUnlock() - + statuses := make(map[string]*EngineStatus) for name, engine := range h.engines { health, err := engine.Health(ctx) @@ -375,12 +375,12 @@ func (h *Host) GetMetrics() map[string]*EngineMetrics { func (h *Host) SaveState(path string) error { h.mu.RLock() defer h.mu.RUnlock() - + state := &HostState{ Engines: make(map[string]*EngineState), Networks: h.networks, } - + for name, engine := range h.engines { state.Engines[name] = &EngineState{ Name: name, @@ -392,12 +392,12 @@ func (h *Host) SaveState(path string) error { Uptime: engine.Uptime().Seconds(), } } - + data, err := json.MarshalIndent(state, "", " ") if err != nil { return err } - + return os.WriteFile(path, data, 0644) } @@ -407,25 +407,25 @@ func (h *Host) LoadState(path string) error { if err != nil { return err } - + var state HostState if err := json.Unmarshal(data, &state); err != nil { return err } - + h.mu.Lock() defer h.mu.Unlock() - + h.networks = state.Networks // Note: Engines need to be restarted separately - + return nil } // HostState represents the persistent state of the host type HostState struct { - Engines map[string]*EngineState `json:"engines"` - Networks map[string]*NetworkInfo `json:"networks"` + Engines map[string]*EngineState `json:"engines"` + Networks map[string]*NetworkInfo `json:"networks"` } // EngineState represents the state of an engine @@ -437,4 +437,4 @@ type EngineState struct { RPC string `json:"rpc"` WS string `json:"ws"` Uptime float64 `json:"uptime_seconds"` -} \ No newline at end of file +} diff --git a/orchestrator/host_test.go b/orchestrator/host_test.go index a660d787..d0112d09 100644 --- a/orchestrator/host_test.go +++ b/orchestrator/host_test.go @@ -169,7 +169,7 @@ func TestPortManager(t *testing.T) { t.Run("ReservePorts", func(t *testing.T) { pm := orchestrator.NewPortManager() - + // Reserve specific ports err := pm.Reserve(20000, 20001, 20002) require.NoError(t, err) @@ -231,4 +231,4 @@ func TestMetricsCollector(t *testing.T) { require.Equal(t, 350.0, summary["total_uptime_seconds"]) require.Equal(t, 2, summary["healthy_engines"]) }) -} \ No newline at end of file +} diff --git a/orchestrator/indexer.go b/orchestrator/indexer.go index 8b00568f..bc04f934 100644 --- a/orchestrator/indexer.go +++ b/orchestrator/indexer.go @@ -10,8 +10,8 @@ import ( // Indexer provides cross-engine blockchain indexing type Indexer struct { - mu sync.RWMutex - data map[string]*IndexData + mu sync.RWMutex + data map[string]*IndexData } // IndexData contains indexed blockchain data @@ -35,7 +35,7 @@ func NewIndexer() *Indexer { func (i *Indexer) Update(engine string, data *IndexData) { i.mu.Lock() defer i.mu.Unlock() - + data.Engine = engine data.LastUpdate = time.Now() i.data[engine] = data @@ -45,7 +45,7 @@ func (i *Indexer) Update(engine string, data *IndexData) { func (i *Indexer) Get(engine string) *IndexData { i.mu.RLock() defer i.mu.RUnlock() - + return i.data[engine] } @@ -53,7 +53,7 @@ func (i *Indexer) Get(engine string) *IndexData { func (i *Indexer) GetAll() map[string]*IndexData { i.mu.RLock() defer i.mu.RUnlock() - + result := make(map[string]*IndexData) for k, v := range i.data { result[k] = v @@ -65,6 +65,6 @@ func (i *Indexer) GetAll() map[string]*IndexData { func (i *Indexer) Clear(engine string) { i.mu.Lock() defer i.mu.Unlock() - + delete(i.data, engine) -} \ No newline at end of file +} diff --git a/orchestrator/manifest.go b/orchestrator/manifest.go index 576aa813..c5a569d0 100644 --- a/orchestrator/manifest.go +++ b/orchestrator/manifest.go @@ -15,12 +15,12 @@ import ( // StackManifest defines a multi-engine stack configuration type StackManifest struct { - Name string `yaml:"name" json:"name"` - Version string `yaml:"version" json:"version"` - Description string `yaml:"description" json:"description"` - Engines []EngineConfig `yaml:"engines" json:"engines"` - Bridge *BridgeConfig `yaml:"bridge,omitempty" json:"bridge,omitempty"` - Networks []NetworkConfig `yaml:"networks,omitempty" json:"networks,omitempty"` + Name string `yaml:"name" json:"name"` + Version string `yaml:"version" json:"version"` + Description string `yaml:"description" json:"description"` + Engines []EngineConfig `yaml:"engines" json:"engines"` + Bridge *BridgeConfig `yaml:"bridge,omitempty" json:"bridge,omitempty"` + Networks []NetworkConfig `yaml:"networks,omitempty" json:"networks,omitempty"` } // EngineConfig defines an engine configuration @@ -38,23 +38,23 @@ type EngineConfig struct { Extra map[string]interface{} `yaml:"extra,omitempty" json:"extra,omitempty"` WaitHealthy bool `yaml:"wait_healthy,omitempty" json:"wait_healthy,omitempty"` DependsOn []string `yaml:"depends_on,omitempty" json:"depends_on,omitempty"` - + // OP Stack specific - L1RPC string `yaml:"l1_rpc,omitempty" json:"l1_rpc,omitempty"` - Sequencer bool `yaml:"sequencer,omitempty" json:"sequencer,omitempty"` - + L1RPC string `yaml:"l1_rpc,omitempty" json:"l1_rpc,omitempty"` + Sequencer bool `yaml:"sequencer,omitempty" json:"sequencer,omitempty"` + // Eth2 specific - ConsensusClient string `yaml:"consensus_client,omitempty" json:"consensus_client,omitempty"` - ExecutionClient string `yaml:"execution_client,omitempty" json:"execution_client,omitempty"` - ValidatorEnabled bool `yaml:"validator_enabled,omitempty" json:"validator_enabled,omitempty"` + ConsensusClient string `yaml:"consensus_client,omitempty" json:"consensus_client,omitempty"` + ExecutionClient string `yaml:"execution_client,omitempty" json:"execution_client,omitempty"` + ValidatorEnabled bool `yaml:"validator_enabled,omitempty" json:"validator_enabled,omitempty"` } // BridgeConfig defines bridge configuration between engines type BridgeConfig struct { - Type string `yaml:"type" json:"type"` // awm, ibc, ccip - Source string `yaml:"source" json:"source"` - Destination string `yaml:"destination" json:"destination"` - RelayerKey string `yaml:"relayer_key,omitempty" json:"relayer_key,omitempty"` + Type string `yaml:"type" json:"type"` // awm, ibc, ccip + Source string `yaml:"source" json:"source"` + Destination string `yaml:"destination" json:"destination"` + RelayerKey string `yaml:"relayer_key,omitempty" json:"relayer_key,omitempty"` Contracts map[string]string `yaml:"contracts,omitempty" json:"contracts,omitempty"` } @@ -73,11 +73,11 @@ func (m *StackManifest) Validate() error { if m.Name == "" { return fmt.Errorf("manifest name is required") } - + if len(m.Engines) == 0 { return fmt.Errorf("at least one engine is required") } - + // Check engine names are unique names := make(map[string]bool) for _, e := range m.Engines { @@ -88,7 +88,7 @@ func (m *StackManifest) Validate() error { return fmt.Errorf("duplicate engine name: %s", e.Name) } names[e.Name] = true - + // Validate engine type switch e.Type { case "lux", "geth", "op", "eth2": @@ -96,7 +96,7 @@ func (m *StackManifest) Validate() error { default: return fmt.Errorf("invalid engine type: %s", e.Type) } - + // Check dependencies exist for _, dep := range e.DependsOn { depFound := false @@ -111,13 +111,13 @@ func (m *StackManifest) Validate() error { } } } - + // Validate bridge if present if m.Bridge != nil { if m.Bridge.Source == "" || m.Bridge.Destination == "" { return fmt.Errorf("bridge source and destination are required") } - + // Check source and destination exist sourceFound := false destFound := false @@ -136,7 +136,7 @@ func (m *StackManifest) Validate() error { return fmt.Errorf("bridge destination %s not found in engines", m.Bridge.Destination) } } - + return nil } @@ -146,19 +146,19 @@ func LoadManifest(path string) (*StackManifest, error) { if err != nil { return nil, fmt.Errorf("failed to read manifest: %w", err) } - + var manifest StackManifest - + // Try YAML first if err := yaml.Unmarshal(data, &manifest); err == nil { return &manifest, nil } - + // Try JSON if err := json.Unmarshal(data, &manifest); err == nil { return &manifest, nil } - + return nil, fmt.Errorf("failed to parse manifest as YAML or JSON") } @@ -169,11 +169,11 @@ func (m *StackManifest) Save(path string) error { if err := os.MkdirAll(dir, 0755); err != nil { return fmt.Errorf("failed to create directory: %w", err) } - + // Marshal based on extension var data []byte var err error - + ext := filepath.Ext(path) switch ext { case ".json": @@ -184,14 +184,14 @@ func (m *StackManifest) Save(path string) error { // Default to YAML data, err = yaml.Marshal(m) } - + if err != nil { return fmt.Errorf("failed to marshal manifest: %w", err) } - + if err := ioutil.WriteFile(path, data, 0644); err != nil { return fmt.Errorf("failed to write manifest: %w", err) } - + return nil -} \ No newline at end of file +} diff --git a/orchestrator/metrics.go b/orchestrator/metrics.go index e9c21122..e95e9db8 100644 --- a/orchestrator/metrics.go +++ b/orchestrator/metrics.go @@ -39,7 +39,7 @@ func NewMetricsCollector() *MetricsCollector { func (mc *MetricsCollector) Record(engine string, values map[string]interface{}) { mc.mu.Lock() defer mc.mu.Unlock() - + em, exists := mc.metrics[engine] if !exists { em = &EngineMetrics{ @@ -48,10 +48,10 @@ func (mc *MetricsCollector) Record(engine string, values map[string]interface{}) } mc.metrics[engine] = em } - + em.LastUpdated = time.Now() em.Values = values - + // Add to history (keep last 100 snapshots) snapshot := MetricSnapshot{ Timestamp: em.LastUpdated, @@ -67,7 +67,7 @@ func (mc *MetricsCollector) Record(engine string, values map[string]interface{}) func (mc *MetricsCollector) Get(engine string) *EngineMetrics { mc.mu.RLock() defer mc.mu.RUnlock() - + return mc.metrics[engine] } @@ -75,7 +75,7 @@ func (mc *MetricsCollector) Get(engine string) *EngineMetrics { func (mc *MetricsCollector) GetAll() map[string]*EngineMetrics { mc.mu.RLock() defer mc.mu.RUnlock() - + result := make(map[string]*EngineMetrics) for k, v := range mc.metrics { result[k] = v @@ -87,7 +87,7 @@ func (mc *MetricsCollector) GetAll() map[string]*EngineMetrics { func (mc *MetricsCollector) Clear(engine string) { mc.mu.Lock() defer mc.mu.Unlock() - + delete(mc.metrics, engine) } @@ -95,13 +95,13 @@ func (mc *MetricsCollector) Clear(engine string) { func (mc *MetricsCollector) Summary() map[string]interface{} { mc.mu.RLock() defer mc.mu.RUnlock() - + summary := make(map[string]interface{}) summary["engines"] = len(mc.metrics) - + var totalUptime float64 var healthyCount int - + for _, em := range mc.metrics { if uptime, ok := em.Values["uptime_seconds"].(float64); ok { totalUptime += uptime @@ -110,9 +110,9 @@ func (mc *MetricsCollector) Summary() map[string]interface{} { healthyCount++ } } - + summary["total_uptime_seconds"] = totalUptime summary["healthy_engines"] = healthyCount - + return summary -} \ No newline at end of file +} diff --git a/orchestrator/ports.go b/orchestrator/ports.go index 0cf07148..95d14da0 100644 --- a/orchestrator/ports.go +++ b/orchestrator/ports.go @@ -11,10 +11,10 @@ import ( // PortManager manages port allocation for engines type PortManager struct { - mu sync.Mutex - allocated map[uint16]bool - httpBase uint16 - p2pBase uint16 + mu sync.Mutex + allocated map[uint16]bool + httpBase uint16 + p2pBase uint16 } // NewPortManager creates a new port manager @@ -30,12 +30,12 @@ func NewPortManager() *PortManager { func (pm *PortManager) AllocateHTTP() uint16 { pm.mu.Lock() defer pm.mu.Unlock() - + port := pm.httpBase for pm.isInUse(port) || pm.allocated[port] { port += 10 // Skip by 10 to avoid conflicts with P2P ports } - + pm.allocated[port] = true return port } @@ -44,12 +44,12 @@ func (pm *PortManager) AllocateHTTP() uint16 { func (pm *PortManager) AllocateP2P() uint16 { pm.mu.Lock() defer pm.mu.Unlock() - + port := pm.p2pBase for pm.isInUse(port) || pm.allocated[port] { port += 10 } - + pm.allocated[port] = true return port } @@ -75,7 +75,7 @@ func (pm *PortManager) isInUse(port uint16) bool { func (pm *PortManager) Reserve(ports ...uint16) error { pm.mu.Lock() defer pm.mu.Unlock() - + // Check all ports are available first for _, port := range ports { if pm.allocated[port] { @@ -85,11 +85,11 @@ func (pm *PortManager) Reserve(ports ...uint16) error { return fmt.Errorf("port %d already in use", port) } } - + // Reserve all ports for _, port := range ports { pm.allocated[port] = true } - + return nil -} \ No newline at end of file +} diff --git a/rpcpb/rpc.pb.go b/rpcpb/rpc.pb.go index 5aeacb0b..36c02dc1 100644 --- a/rpcpb/rpc.pb.go +++ b/rpcpb/rpc.pb.go @@ -7,12 +7,13 @@ package rpcpb import ( - _ "google.golang.org/genproto/googleapis/api/annotations" - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" + + _ "google.golang.org/genproto/googleapis/api/annotations" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" ) const ( diff --git a/rpcpb/rpc_grpc.pb.go b/rpcpb/rpc_grpc.pb.go index 912ae9fb..0f713f5c 100644 --- a/rpcpb/rpc_grpc.pb.go +++ b/rpcpb/rpc_grpc.pb.go @@ -8,6 +8,7 @@ package rpcpb import ( context "context" + grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" diff --git a/server/network.go b/server/network.go index def7d0d4..e247a209 100644 --- a/server/network.go +++ b/server/network.go @@ -13,6 +13,10 @@ import ( "strings" "sync" + "maps" + "slices" + + "github.com/luxfi/ids" "github.com/luxfi/netrunner/local" "github.com/luxfi/netrunner/network" "github.com/luxfi/netrunner/network/node" @@ -20,9 +24,6 @@ import ( "github.com/luxfi/netrunner/utils/constants" "github.com/luxfi/netrunner/ux" "github.com/luxfi/node/config" - "github.com/luxfi/ids" - "maps" - "slices" luxd_constants "github.com/luxfi/const" "github.com/luxfi/log" @@ -80,7 +81,7 @@ type localNetwork struct { type chainInfo struct { info *rpcpb.CustomChainInfo - chainID ids.ID + chainID ids.ID blockchainID ids.ID } @@ -88,7 +89,7 @@ type localNetworkOptions struct { execPath string rootDataDir string numNodes uint32 - trackChains string + trackChains string redirectNodesOutput bool globalNodeConfig string @@ -132,7 +133,7 @@ func newLocalNetwork(opts localNetworkOptions) (*localNetwork, error) { customChainIDToInfo: make(map[ids.ID]chainInfo), stopCh: make(chan struct{}), nodeInfos: make(map[string]*rpcpb.NodeInfo), - chains: make(map[string]*rpcpb.ChainInfo), + chains: make(map[string]*rpcpb.ChainInfo), }, nil } @@ -705,12 +706,12 @@ func (lc *localNetwork) updateChainInfo(ctx context.Context) error { } lc.customChainIDToInfo[blockchain.ID] = chainInfo{ info: &rpcpb.CustomChainInfo{ - ChainName: blockchain.Name, - VmId: blockchain.VMID.String(), - PchainId: blockchain.NetID.String(), - BlockchainId: blockchain.ID.String(), + ChainName: blockchain.Name, + VmId: blockchain.VMID.String(), + PchainId: blockchain.NetID.String(), + BlockchainId: blockchain.ID.String(), }, - chainID: blockchain.NetID, + chainID: blockchain.NetID, blockchainID: blockchain.ID, } } @@ -758,7 +759,7 @@ func (lc *localNetwork) updateChainInfo(ctx context.Context) error { } lc.chains[chainIDStr] = &rpcpb.ChainInfo{ - IsElastic: isElastic, + IsElastic: isElastic, ElasticChainId: elasticChainID.String(), ChainParticipants: &rpcpb.ChainParticipants{NodeNames: nodeNameList}, } @@ -858,16 +859,16 @@ func (lc *localNetwork) updateNodeInfo() error { } lc.nodeInfos[name] = &rpcpb.NodeInfo{ - Name: node.GetName(), - Uri: node.GetURL(), - Id: node.GetNodeID().String(), - ExecPath: node.GetBinaryPath(), - LogDir: node.GetLogsDir(), - DbDir: node.GetDbDir(), - Config: []byte(node.GetConfigFile()), - PluginDir: node.GetPluginDir(), + Name: node.GetName(), + Uri: node.GetURL(), + Id: node.GetNodeID().String(), + ExecPath: node.GetBinaryPath(), + LogDir: node.GetLogsDir(), + DbDir: node.GetDbDir(), + Config: []byte(node.GetConfigFile()), + PluginDir: node.GetPluginDir(), WhitelistedChains: trackChains, - Paused: node.GetPaused(), + Paused: node.GetPaused(), } // update default exec and pluginDir if empty (snapshots started without these params) diff --git a/server/server.go b/server/server.go index 0fe0090e..b490ed9b 100644 --- a/server/server.go +++ b/server/server.go @@ -21,6 +21,11 @@ import ( "go.uber.org/multierr" + "maps" + "slices" + + "github.com/luxfi/consensus/core" + "github.com/luxfi/ids" "github.com/luxfi/netrunner/network" "github.com/luxfi/netrunner/network/node" "github.com/luxfi/netrunner/rpcpb" @@ -28,15 +33,11 @@ import ( "github.com/luxfi/netrunner/utils/constants" "github.com/luxfi/node/config" "github.com/luxfi/node/message" - "github.com/luxfi/consensus/core" "github.com/luxfi/node/network/peer" - "github.com/luxfi/ids" - "maps" - "slices" + "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" "github.com/luxfi/log" "github.com/luxfi/math/set" - "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/credentials/insecure" @@ -51,8 +52,8 @@ const ( MinNodes uint32 = 1 DefaultNodes uint32 = 5 - stopTimeout = 5 * time.Second - defaultStartTimeout = 5 * time.Minute + stopTimeout = 5 * time.Second + defaultStartTimeout = 5 * time.Minute // waitForHealthyTimeout - 60s for local 5-node network bootstrap // First startup takes longer as nodes need to establish consensus // Subsequent restarts are faster (<10s) but initial bootstrap needs time @@ -75,9 +76,9 @@ var ( ErrNodeNotFound = errors.New("node not found") ErrPeerNotFound = errors.New("peer not found") ErrStatusCanceled = errors.New("gRPC stream status canceled") - ErrNoChainSpec = errors.New("no blockchain spec was provided") - ErrNoChainID = errors.New("chainID is missing") - ErrNoElasticChainSpec = errors.New("no elastic chain 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") ) @@ -175,7 +176,7 @@ type Server interface { type server struct { mu *sync.RWMutex - cfg Config + cfg Config logger log.Logger rootCtx context.Context @@ -221,7 +222,7 @@ func New(cfg Config, logger log.Logger) (Server, error) { s := &server{ cfg: cfg, - logger: logger, + logger: logger, closed: make(chan struct{}), ln: listener, gRPCServer: grpc.NewServer(), @@ -381,7 +382,7 @@ func (s *server) Start(_ context.Context, req *rpcpb.StartRequest) (*rpcpb.Start var ( execPath = req.GetExecPath() numNodes = req.GetNumNodes() - trackChains = req.GetWhitelistedChains() + trackChains = req.GetWhitelistedChains() rootDataDir = req.GetRootDataDir() pid = int32(os.Getpid()) globalNodeConfig = req.GetGlobalNodeConfig() @@ -410,7 +411,7 @@ func (s *server) Start(_ context.Context, req *rpcpb.StartRequest) (*rpcpb.Start } else { // CLI provided a specific rootDataDir - use it directly // Trust the CLI to provide a properly structured path - networkName = getNetworkNameFromRootDir(rootDataDir) + _ = getNetworkNameFromRootDir(rootDataDir) // Ensure the provided directory exists if err = os.MkdirAll(rootDataDir, os.ModePerm); err != nil { @@ -432,7 +433,7 @@ func (s *server) Start(_ context.Context, req *rpcpb.StartRequest) (*rpcpb.Start execPath: execPath, rootDataDir: rootDataDir, numNodes: numNodes, - trackChains: trackChains, + trackChains: trackChains, redirectNodesOutput: s.cfg.RedirectNodesOutput, pluginDir: pluginDir, globalNodeConfig: globalNodeConfig, @@ -1278,7 +1279,7 @@ var _ peer.InboundHandler = &loggingInboundHandler{} type loggingInboundHandler struct { nodeName string - logger log.Logger + logger log.Logger } func (lh *loggingInboundHandler) HandleInbound(_ context.Context, msg message.InboundMessage) { @@ -1601,7 +1602,7 @@ func getNetworkElasticChainSpec( maxStakeDuration := time.Duration(spec.MaxStakeDuration) * time.Hour elasticParticipantsSpec := network.ElasticChainSpec{ - ChainID: &spec.ChainId, + ChainID: &spec.ChainId, AssetName: spec.AssetName, AssetSymbol: spec.AssetSymbol, InitialSupply: spec.InitialSupply, @@ -1638,7 +1639,7 @@ func getPermissionlessValidatorSpec( stakeDuration := time.Duration(spec.StakeDuration) * time.Hour validatorSpec := network.PermissionlessValidatorSpec{ - ChainID: spec.ChainId, + ChainID: spec.ChainId, AssetID: spec.AssetId, NodeName: spec.NodeName, StakedAmount: spec.StakedTokenAmount, @@ -1652,7 +1653,7 @@ func getRemoveChainValidatorSpec( spec *rpcpb.RemoveChainValidatorSpec, ) network.RemoveChainValidatorSpec { validatorSpec := network.RemoveChainValidatorSpec{ - ChainID: spec.ChainId, + ChainID: spec.ChainId, NodeNames: spec.GetNodeNames(), } return validatorSpec @@ -1730,13 +1731,13 @@ func getNetworkChainSpec( PerNodeChainConfig: perNodeChainConfig, // Use BlockchainAlias as the blockchain name for the P-Chain transaction // This allows multiple chains to use the same VM (e.g., multiple EVM chains) - BlockchainName: spec.BlockchainAlias, + BlockchainName: spec.BlockchainAlias, } if spec.ChainSpec != nil { participantsSpec := network.ParticipantsSpec{ Participants: spec.ChainSpec.Participants, - ChainConfig: chainConfigBytes, + ChainConfig: chainConfigBytes, } chainSpec.ParticipantsSpec = &participantsSpec } diff --git a/tests/e2e/e2e_test.go b/tests/e2e/e2e_test.go index 554bd6d2..4232a509 100644 --- a/tests/e2e/e2e_test.go +++ b/tests/e2e/e2e_test.go @@ -17,23 +17,24 @@ import ( "testing" "time" - "github.com/luxfi/node/api/admin" - "github.com/luxfi/node/message" - luxd_constants "github.com/luxfi/const" "maps" "slices" + luxd_constants "github.com/luxfi/const" + "github.com/luxfi/node/api/admin" + "github.com/luxfi/node/message" + "github.com/luxfi/node/vms/platformvm" + "github.com/luxfi/ids" + "github.com/luxfi/log" + "github.com/luxfi/metric" "github.com/luxfi/netrunner/client" "github.com/luxfi/netrunner/rpcpb" "github.com/luxfi/netrunner/server" "github.com/luxfi/netrunner/utils" "github.com/luxfi/netrunner/utils/constants" "github.com/luxfi/netrunner/ux" - "github.com/luxfi/ids" - "github.com/luxfi/log" - "github.com/luxfi/metric" ginkgo "github.com/onsi/ginkgo/v2" "github.com/onsi/gomega" ) @@ -55,7 +56,7 @@ var ( gRPCGatewayEp string execPath1 string execPath2 string - evmPath string + evmPath string genesisPath string genesisContents string @@ -332,8 +333,8 @@ var _ = ginkgo.Describe("[Start/Remove/Restart/Add/Stop]", func() { resp, err := cli.CreateChains(ctx, []*rpcpb.BlockchainSpec{ { - VmName: "evm", - Genesis: genesisContents, + VmName: "evm", + Genesis: genesisContents, ParticipantsSpec: &rpcpb.ParticipantsSpec{Participants: subnetParticipants}, }, }, @@ -364,8 +365,8 @@ var _ = ginkgo.Describe("[Start/Remove/Restart/Add/Stop]", func() { resp, err := cli.CreateChains(ctx, []*rpcpb.BlockchainSpec{ { - VmName: "evm", - Genesis: genesisContents, + VmName: "evm", + Genesis: genesisContents, ParticipantsSpec: &rpcpb.ParticipantsSpec{Participants: subnetParticipants2}, }, }, @@ -423,13 +424,13 @@ var _ = ginkgo.Describe("[Start/Remove/Restart/Add/Stop]", func() { resp, err := cli.CreateChains(ctx, []*rpcpb.BlockchainSpec{ { - VmName: "evm", - Genesis: genesisContents, + VmName: "evm", + Genesis: genesisContents, ParticipantsSpec: &rpcpb.ParticipantsSpec{Participants: disjointNewSubnetParticipants[0]}, }, { - VmName: "evm", - Genesis: genesisContents, + VmName: "evm", + Genesis: genesisContents, ParticipantsSpec: &rpcpb.ParticipantsSpec{Participants: disjointNewSubnetParticipants[1]}, }, }, diff --git a/utils/constants/constants.go b/utils/constants/constants.go index bec7d728..5a97b6c4 100644 --- a/utils/constants/constants.go +++ b/utils/constants/constants.go @@ -12,16 +12,16 @@ const ( // Network run directory structure (flat, no nesting) // Structure: ~/.lux/runs//run_/node1/, node2/, ... - RunsDir = "runs" // Top-level runs directory - RunDirPrefix = "run" // Prefix for timestamped run directories - DefaultNetwork = "local" // Default network name + RunsDir = "runs" // Top-level runs directory + RunDirPrefix = "run" // Prefix for timestamped run directories + DefaultNetwork = "local" // Default network name // Unified chain config directory (shared across all nodes) // Structure: ~/.lux/chains//genesis.json, config.json, upgrade.json - ChainsDir = "chains" - ChainGenesisFile = "genesis.json" - ChainConfigFile = "config.json" - ChainUpgradeFile = "upgrade.json" + ChainsDir = "chains" + ChainGenesisFile = "genesis.json" + ChainConfigFile = "config.json" + ChainUpgradeFile = "upgrade.json" // Unified plugins directory // Structure: ~/.lux/plugins/current/ (symlinks to actual binaries) diff --git a/utils/utils.go b/utils/utils.go index e931f8c1..c1a550ab 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -8,11 +8,11 @@ import ( "os" "time" + "github.com/luxfi/ids" + "github.com/luxfi/log" rpcb "github.com/luxfi/netrunner/rpcpb" "github.com/luxfi/netrunner/ux" - "github.com/luxfi/ids" "github.com/luxfi/node/staking" - "github.com/luxfi/log" ) const (