mirror of
https://github.com/DeBrosOfficial/network.git
synced 2025-10-06 20:09:07 +00:00
- Regular nodes now join actual bootstrap nodes instead of localhost:4001 - Extract IP from bootstrap peer multiaddrs for RQLite join address - Add fallback to first known bootstrap node (57.129.81.31:4001) - This fixes the 'fatal: http://localhost:4001 is an invalid join address' error Regular nodes will now properly join the RQLite cluster of bootstrap nodes instead of trying to join themselves, which was causing startup failures.
236 lines
6.9 KiB
Go
236 lines
6.9 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"flag"
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"os/signal"
|
|
"path/filepath"
|
|
"strings"
|
|
"syscall"
|
|
|
|
"git.debros.io/DeBros/network/pkg/config"
|
|
"git.debros.io/DeBros/network/pkg/constants"
|
|
"git.debros.io/DeBros/network/pkg/logging"
|
|
"git.debros.io/DeBros/network/pkg/node"
|
|
)
|
|
|
|
func main() {
|
|
var (
|
|
dataDir = flag.String("data", "", "Data directory (auto-detected if not provided)")
|
|
nodeID = flag.String("id", "", "Node identifier (for running multiple local nodes)")
|
|
bootstrap = flag.String("bootstrap", "", "Bootstrap peer address (for manual override)")
|
|
help = flag.Bool("help", false, "Show help")
|
|
)
|
|
flag.Parse()
|
|
|
|
if *help {
|
|
flag.Usage()
|
|
return
|
|
}
|
|
|
|
// Auto-detect if this is a bootstrap node based on configuration
|
|
isBootstrap := isBootstrapNode()
|
|
|
|
// Set default data directory if not provided
|
|
if *dataDir == "" {
|
|
if isBootstrap {
|
|
*dataDir = "./data/bootstrap"
|
|
} else {
|
|
if *nodeID != "" {
|
|
*dataDir = fmt.Sprintf("./data/node-%s", *nodeID)
|
|
} else {
|
|
*dataDir = "./data/node"
|
|
}
|
|
}
|
|
}
|
|
|
|
// All nodes use port 4001 for consistency
|
|
port := 4001
|
|
|
|
// Create logger with appropriate component type
|
|
var logger *logging.StandardLogger
|
|
var err error
|
|
if isBootstrap {
|
|
logger, err = logging.NewStandardLogger(logging.ComponentBootstrap)
|
|
} else {
|
|
logger, err = logging.NewStandardLogger(logging.ComponentNode)
|
|
}
|
|
if err != nil {
|
|
log.Fatalf("Failed to create logger: %v", err)
|
|
}
|
|
|
|
// Load configuration based on node type
|
|
var cfg *config.Config
|
|
if isBootstrap {
|
|
cfg = config.BootstrapConfig()
|
|
logger.Printf("Starting bootstrap node...")
|
|
} else {
|
|
cfg = config.DefaultConfig()
|
|
logger.Printf("Starting regular node...")
|
|
}
|
|
|
|
// Set basic configuration
|
|
cfg.Node.DataDir = *dataDir
|
|
cfg.Node.ListenAddresses = []string{
|
|
fmt.Sprintf("/ip4/0.0.0.0/tcp/%d", port),
|
|
fmt.Sprintf("/ip4/0.0.0.0/udp/%d/quic", port),
|
|
}
|
|
|
|
// All nodes use the same RQLite port (4001) to join the same cluster
|
|
cfg.Database.RQLitePort = 4001
|
|
cfg.Database.RQLiteRaftPort = 4002
|
|
|
|
if isBootstrap {
|
|
// Bootstrap node doesn't join anyone - it starts the cluster
|
|
cfg.Database.RQLiteJoinAddress = ""
|
|
logger.Printf("Bootstrap node - starting new RQLite cluster")
|
|
} else {
|
|
// Configure bootstrap peers for P2P discovery
|
|
var rqliteJoinAddr string
|
|
if *bootstrap != "" {
|
|
// Use command line bootstrap if provided
|
|
cfg.Discovery.BootstrapPeers = []string{*bootstrap}
|
|
// Extract IP from bootstrap peer for RQLite join
|
|
bootstrapHost := parseHostFromMultiaddr(*bootstrap)
|
|
if bootstrapHost != "" {
|
|
rqliteJoinAddr = fmt.Sprintf("http://%s:4001", bootstrapHost)
|
|
} else {
|
|
rqliteJoinAddr = "http://57.129.81.31:4001" // Default fallback
|
|
}
|
|
logger.Printf("Using command line bootstrap peer: %s", *bootstrap)
|
|
} else {
|
|
// Use environment-configured bootstrap peers
|
|
bootstrapPeers := constants.GetBootstrapPeers()
|
|
if len(bootstrapPeers) > 0 {
|
|
cfg.Discovery.BootstrapPeers = bootstrapPeers
|
|
// Use the first bootstrap peer for RQLite join
|
|
bootstrapHost := parseHostFromMultiaddr(bootstrapPeers[0])
|
|
if bootstrapHost != "" {
|
|
rqliteJoinAddr = fmt.Sprintf("http://%s:4001", bootstrapHost)
|
|
} else {
|
|
rqliteJoinAddr = "http://57.129.81.31:4001" // Default fallback
|
|
}
|
|
logger.Printf("Using environment bootstrap peers: %v", bootstrapPeers)
|
|
} else {
|
|
logger.Printf("Warning: No bootstrap peers configured")
|
|
rqliteJoinAddr = "http://57.129.81.31:4001" // Default fallback
|
|
}
|
|
}
|
|
|
|
// Regular nodes join the bootstrap node's RQLite cluster
|
|
cfg.Database.RQLiteJoinAddress = rqliteJoinAddr
|
|
logger.Printf("Regular node - joining RQLite cluster at: %s", cfg.Database.RQLiteJoinAddress)
|
|
}
|
|
|
|
logger.Printf("Data directory: %s", cfg.Node.DataDir)
|
|
logger.Printf("Listen addresses: %v", cfg.Node.ListenAddresses)
|
|
logger.Printf("RQLite HTTP port: %d", cfg.Database.RQLitePort)
|
|
logger.Printf("RQLite Raft port: %d", cfg.Database.RQLiteRaftPort)
|
|
|
|
// Create context for graceful shutdown
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
// Start node in a goroutine
|
|
errChan := make(chan error, 1)
|
|
go func() {
|
|
if err := startNode(ctx, cfg, port, isBootstrap, logger); err != nil {
|
|
errChan <- err
|
|
}
|
|
}()
|
|
|
|
// Wait for interrupt signal or error
|
|
c := make(chan os.Signal, 1)
|
|
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
|
|
|
|
select {
|
|
case err := <-errChan:
|
|
logger.Printf("Failed to start node: %v", err)
|
|
os.Exit(1)
|
|
case <-c:
|
|
logger.Printf("Shutting down node...")
|
|
cancel()
|
|
}
|
|
}
|
|
|
|
// isBootstrapNode determines if this should be a bootstrap node
|
|
// by checking the local machine's configuration and bootstrap peer list
|
|
func isBootstrapNode() bool {
|
|
// Get the bootstrap peer addresses to check if this machine should be a bootstrap
|
|
bootstrapPeers := constants.GetBootstrapPeers()
|
|
|
|
// Check if any bootstrap peer is localhost/127.0.0.1 (development)
|
|
// or if we're running on a production bootstrap server
|
|
hostname, _ := os.Hostname()
|
|
|
|
for _, peerAddr := range bootstrapPeers {
|
|
// Parse the multiaddr to extract the host
|
|
host := parseHostFromMultiaddr(peerAddr)
|
|
|
|
// Check if this is a local bootstrap (development)
|
|
if host == "127.0.0.1" || host == "localhost" {
|
|
return true // In development, assume we're running the bootstrap
|
|
}
|
|
|
|
// Check if this is a production bootstrap server
|
|
// You could add more sophisticated host matching here
|
|
if hostname != "" && strings.Contains(peerAddr, hostname) {
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Default: if no specific match, run as regular node
|
|
return false
|
|
}
|
|
|
|
// parseHostFromMultiaddr extracts the host from a multiaddr
|
|
func parseHostFromMultiaddr(multiaddr string) string {
|
|
// Simple parsing for /ip4/host/tcp/port/p2p/peerid format
|
|
parts := strings.Split(multiaddr, "/")
|
|
|
|
// Look for ip4/ip6/dns host in the multiaddr
|
|
for i, part := range parts {
|
|
if (part == "ip4" || part == "ip6" || part == "dns" || part == "dns4" || part == "dns6") && i+1 < len(parts) {
|
|
return parts[i+1]
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func startNode(ctx context.Context, cfg *config.Config, port int, isBootstrap bool, logger *logging.StandardLogger) error {
|
|
// Create and start node using the unified node implementation
|
|
n, err := node.NewNode(cfg)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create node: %w", err)
|
|
}
|
|
|
|
if err := n.Start(ctx); err != nil {
|
|
return fmt.Errorf("failed to start node: %w", err)
|
|
}
|
|
|
|
// Save the peer ID to a file for CLI access (especially useful for bootstrap)
|
|
if isBootstrap {
|
|
peerID := n.GetPeerID()
|
|
peerInfoFile := filepath.Join(cfg.Node.DataDir, "peer.info")
|
|
peerMultiaddr := fmt.Sprintf("/ip4/127.0.0.1/tcp/%d/p2p/%s", port, peerID)
|
|
|
|
if err := os.WriteFile(peerInfoFile, []byte(peerMultiaddr), 0644); err != nil {
|
|
logger.Printf("Warning: Failed to save peer info: %v", err)
|
|
} else {
|
|
logger.Printf("Peer info saved to: %s", peerInfoFile)
|
|
logger.Printf("Bootstrap multiaddr: %s", peerMultiaddr)
|
|
}
|
|
}
|
|
|
|
logger.Printf("Node started successfully")
|
|
|
|
// Wait for context cancellation
|
|
<-ctx.Done()
|
|
|
|
// Stop node
|
|
return n.Stop()
|
|
}
|