mirror of
https://github.com/DeBrosOfficial/network.git
synced 2026-01-30 17:43:03 +00:00
- Updated the API gateway documentation to reflect changes in architecture and functionality, emphasizing its role as a multi-functional entry point for decentralized services. - Refactored CLI commands to utilize utility functions for better code organization and maintainability. - Introduced new utility functions for handling peer normalization, service management, and port validation, enhancing the overall CLI experience. - Added a new production installation script to streamline the setup process for users, including detailed dry-run summaries for better visibility. - Enhanced validation mechanisms for configuration files and swarm keys, ensuring robust error handling and user feedback during setup.
303 lines
7.5 KiB
Go
303 lines
7.5 KiB
Go
package node
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/DeBrosOfficial/network/pkg/discovery"
|
|
"github.com/DeBrosOfficial/network/pkg/encryption"
|
|
"github.com/DeBrosOfficial/network/pkg/logging"
|
|
"github.com/DeBrosOfficial/network/pkg/pubsub"
|
|
"github.com/libp2p/go-libp2p"
|
|
libp2ppubsub "github.com/libp2p/go-libp2p-pubsub"
|
|
"github.com/libp2p/go-libp2p/core/crypto"
|
|
"github.com/libp2p/go-libp2p/core/peer"
|
|
noise "github.com/libp2p/go-libp2p/p2p/security/noise"
|
|
"github.com/multiformats/go-multiaddr"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
// startLibP2P initializes the LibP2P host
|
|
func (n *Node) startLibP2P() error {
|
|
n.logger.ComponentInfo(logging.ComponentLibP2P, "Starting LibP2P host")
|
|
|
|
// Load or create persistent identity
|
|
identity, err := n.loadOrCreateIdentity()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to load identity: %w", err)
|
|
}
|
|
|
|
// Create LibP2P host with explicit listen addresses
|
|
var opts []libp2p.Option
|
|
opts = append(opts,
|
|
libp2p.Identity(identity),
|
|
libp2p.Security(noise.ID, noise.New),
|
|
libp2p.DefaultMuxers,
|
|
)
|
|
|
|
// Add explicit listen addresses from config
|
|
if len(n.config.Node.ListenAddresses) > 0 {
|
|
listenAddrs := make([]multiaddr.Multiaddr, 0, len(n.config.Node.ListenAddresses))
|
|
for _, addr := range n.config.Node.ListenAddresses {
|
|
ma, err := multiaddr.NewMultiaddr(addr)
|
|
if err != nil {
|
|
return fmt.Errorf("invalid listen address %s: %w", addr, err)
|
|
}
|
|
listenAddrs = append(listenAddrs, ma)
|
|
}
|
|
opts = append(opts, libp2p.ListenAddrs(listenAddrs...))
|
|
n.logger.ComponentInfo(logging.ComponentLibP2P, "Configured listen addresses",
|
|
zap.Strings("addrs", n.config.Node.ListenAddresses))
|
|
}
|
|
|
|
// For localhost/development, disable NAT services
|
|
isLocalhost := len(n.config.Node.ListenAddresses) > 0 &&
|
|
(strings.Contains(n.config.Node.ListenAddresses[0], "localhost") ||
|
|
strings.Contains(n.config.Node.ListenAddresses[0], "127.0.0.1"))
|
|
|
|
if isLocalhost {
|
|
n.logger.ComponentInfo(logging.ComponentLibP2P, "Localhost detected - disabling NAT services for local development")
|
|
} else {
|
|
n.logger.ComponentInfo(logging.ComponentLibP2P, "Production mode - enabling NAT services")
|
|
opts = append(opts,
|
|
libp2p.EnableNATService(),
|
|
libp2p.EnableAutoNATv2(),
|
|
libp2p.EnableRelay(),
|
|
libp2p.NATPortMap(),
|
|
libp2p.EnableAutoRelayWithPeerSource(
|
|
peerSource(n.config.Discovery.BootstrapPeers, n.logger.Logger),
|
|
),
|
|
)
|
|
}
|
|
|
|
h, err := libp2p.New(opts...)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
n.host = h
|
|
|
|
// Initialize pubsub
|
|
ps, err := libp2ppubsub.NewGossipSub(context.Background(), h,
|
|
libp2ppubsub.WithPeerExchange(true),
|
|
libp2ppubsub.WithFloodPublish(true),
|
|
libp2ppubsub.WithDirectPeers(nil),
|
|
)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create pubsub: %w", err)
|
|
}
|
|
|
|
// Create pubsub adapter
|
|
n.pubsub = pubsub.NewClientAdapter(ps, n.config.Discovery.NodeNamespace)
|
|
n.logger.Info("Initialized pubsub adapter on namespace", zap.String("namespace", n.config.Discovery.NodeNamespace))
|
|
|
|
// Connect to peers
|
|
if err := n.connectToPeers(context.Background()); err != nil {
|
|
n.logger.ComponentWarn(logging.ComponentNode, "Failed to connect to peers", zap.Error(err))
|
|
}
|
|
|
|
// Start reconnection loop
|
|
if len(n.config.Discovery.BootstrapPeers) > 0 {
|
|
peerCtx, cancel := context.WithCancel(context.Background())
|
|
n.peerDiscoveryCancel = cancel
|
|
|
|
go n.peerReconnectionLoop(peerCtx)
|
|
}
|
|
|
|
// Add peers to peerstore
|
|
for _, peerAddr := range n.config.Discovery.BootstrapPeers {
|
|
if ma, err := multiaddr.NewMultiaddr(peerAddr); err == nil {
|
|
if peerInfo, err := peer.AddrInfoFromP2pAddr(ma); err == nil {
|
|
n.host.Peerstore().AddAddrs(peerInfo.ID, peerInfo.Addrs, time.Hour*24)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Initialize discovery manager
|
|
n.discoveryManager = discovery.NewManager(h, nil, n.logger.Logger)
|
|
n.discoveryManager.StartProtocolHandler()
|
|
|
|
n.logger.ComponentInfo(logging.ComponentNode, "LibP2P host started successfully")
|
|
|
|
// Start peer discovery
|
|
n.startPeerDiscovery()
|
|
|
|
return nil
|
|
}
|
|
|
|
func (n *Node) peerReconnectionLoop(ctx context.Context) {
|
|
interval := 5 * time.Second
|
|
consecutiveFailures := 0
|
|
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
return
|
|
default:
|
|
}
|
|
|
|
if !n.hasPeerConnections() {
|
|
if err := n.connectToPeers(context.Background()); err != nil {
|
|
consecutiveFailures++
|
|
jitteredInterval := addJitter(interval)
|
|
|
|
select {
|
|
case <-ctx.Done():
|
|
return
|
|
case <-time.After(jitteredInterval):
|
|
}
|
|
|
|
interval = calculateNextBackoff(interval)
|
|
} else {
|
|
interval = 5 * time.Second
|
|
consecutiveFailures = 0
|
|
|
|
select {
|
|
case <-ctx.Done():
|
|
return
|
|
case <-time.After(30 * time.Second):
|
|
}
|
|
}
|
|
} else {
|
|
select {
|
|
case <-ctx.Done():
|
|
return
|
|
case <-time.After(30 * time.Second):
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (n *Node) connectToPeers(ctx context.Context) error {
|
|
for _, peerAddr := range n.config.Discovery.BootstrapPeers {
|
|
if err := n.connectToPeerAddr(ctx, peerAddr); err != nil {
|
|
continue
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (n *Node) connectToPeerAddr(ctx context.Context, addr string) error {
|
|
ma, err := multiaddr.NewMultiaddr(addr)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
peerInfo, err := peer.AddrInfoFromP2pAddr(ma)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if n.host != nil && peerInfo.ID == n.host.ID() {
|
|
return nil
|
|
}
|
|
return n.host.Connect(ctx, *peerInfo)
|
|
}
|
|
|
|
func (n *Node) hasPeerConnections() bool {
|
|
if n.host == nil || len(n.config.Discovery.BootstrapPeers) == 0 {
|
|
return false
|
|
}
|
|
connectedPeers := n.host.Network().Peers()
|
|
if len(connectedPeers) == 0 {
|
|
return false
|
|
}
|
|
|
|
bootstrapIDs := make(map[peer.ID]bool)
|
|
for _, addr := range n.config.Discovery.BootstrapPeers {
|
|
if ma, err := multiaddr.NewMultiaddr(addr); err == nil {
|
|
if info, err := peer.AddrInfoFromP2pAddr(ma); err == nil {
|
|
bootstrapIDs[info.ID] = true
|
|
}
|
|
}
|
|
}
|
|
|
|
for _, p := range connectedPeers {
|
|
if bootstrapIDs[p] {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (n *Node) loadOrCreateIdentity() (crypto.PrivKey, error) {
|
|
identityFile := filepath.Join(os.ExpandEnv(n.config.Node.DataDir), "identity.key")
|
|
if strings.HasPrefix(identityFile, "~") {
|
|
home, _ := os.UserHomeDir()
|
|
identityFile = filepath.Join(home, identityFile[1:])
|
|
}
|
|
|
|
if _, err := os.Stat(identityFile); err == nil {
|
|
info, err := encryption.LoadIdentity(identityFile)
|
|
if err == nil {
|
|
return info.PrivateKey, nil
|
|
}
|
|
}
|
|
|
|
info, err := encryption.GenerateIdentity()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err := encryption.SaveIdentity(info, identityFile); err != nil {
|
|
return nil, err
|
|
}
|
|
return info.PrivateKey, nil
|
|
}
|
|
|
|
func (n *Node) startPeerDiscovery() {
|
|
if n.discoveryManager == nil {
|
|
return
|
|
}
|
|
discoveryConfig := discovery.Config{
|
|
DiscoveryInterval: n.config.Discovery.DiscoveryInterval,
|
|
MaxConnections: n.config.Node.MaxConnections,
|
|
}
|
|
n.discoveryManager.Start(discoveryConfig)
|
|
}
|
|
|
|
func (n *Node) stopPeerDiscovery() {
|
|
if n.discoveryManager != nil {
|
|
n.discoveryManager.Stop()
|
|
}
|
|
}
|
|
|
|
func (n *Node) GetPeerID() string {
|
|
if n.host == nil {
|
|
return ""
|
|
}
|
|
return n.host.ID().String()
|
|
}
|
|
|
|
func peerSource(peerAddrs []string, logger *zap.Logger) func(context.Context, int) <-chan peer.AddrInfo {
|
|
return func(ctx context.Context, num int) <-chan peer.AddrInfo {
|
|
out := make(chan peer.AddrInfo, num)
|
|
go func() {
|
|
defer close(out)
|
|
count := 0
|
|
for _, s := range peerAddrs {
|
|
if count >= num {
|
|
return
|
|
}
|
|
ma, err := multiaddr.NewMultiaddr(s)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
ai, err := peer.AddrInfoFromP2pAddr(ma)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
select {
|
|
case out <- *ai:
|
|
count++
|
|
case <-ctx.Done():
|
|
return
|
|
}
|
|
}
|
|
}()
|
|
return out
|
|
}
|
|
}
|
|
|