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