From 262e36bf8a367a58812cc202ebd51f6b6cc43088 Mon Sep 17 00:00:00 2001 From: Matee Ullah Malik Date: Wed, 13 Aug 2025 21:17:35 +0500 Subject: [PATCH] Refactor configuration to use consisten network interface --- README.md | 3 +- p2p/DEVDOCS.md | 135 ------------------ supernode/cmd/config.go | 2 +- supernode/cmd/config_update.go | 6 +- supernode/cmd/helpers.go | 11 ++ supernode/cmd/init.go | 2 +- supernode/cmd/list_config.go | 2 +- supernode/cmd/start.go | 18 +-- supernode/config.yml | 3 +- supernode/config/config.go | 7 +- supernode/config/save.go | 7 +- supernode/node/supernode/gateway/server.go | 11 +- supernode/node/supernode/server/config.go | 6 +- .../node/supernode/server/config_test.go | 2 +- .../node/supernode/server/server_test.go | 2 + .../services/common/supernode/service.go | 2 +- tests/system/config.test-1.yml | 3 +- tests/system/config.test-2.yml | 3 +- tests/system/config.test-3.yml | 3 +- 19 files changed, 46 insertions(+), 182 deletions(-) delete mode 100644 p2p/DEVDOCS.md diff --git a/README.md b/README.md index 3de40c60..583b57b1 100644 --- a/README.md +++ b/README.md @@ -355,7 +355,7 @@ The configuration file is located at `~/.supernode/config.yml` and contains the supernode: key_name: "mykey" # Name of the key for signing transactions identity: "lumera15t2e8gjgmuqtj..." # Lumera address for this supernode - ip_address: "0.0.0.0" # IP address to bind the service + host: "0.0.0.0" # Host address to bind the service (IP or hostname) port: 4444 # Port for the supernode service gateway_port: 8002 # Port for the HTTP gateway service ``` @@ -373,7 +373,6 @@ keyring: ### P2P Configuration ```yaml p2p: - listen_address: "0.0.0.0" # IP address for P2P networking port: 4445 # P2P communication port (do not change) data_dir: "data/p2p" # Directory for P2P data storage ``` diff --git a/p2p/DEVDOCS.md b/p2p/DEVDOCS.md deleted file mode 100644 index 99419c46..00000000 --- a/p2p/DEVDOCS.md +++ /dev/null @@ -1,135 +0,0 @@ -# Lumera P2P Service - -A Kademlia-based distributed hash table (DHT) implementation that provides decentralized storage and retrieval capabilities for the Lumera network. - -## Overview - -The P2P service enables supernodes to: -- Store and retrieve data in a distributed network -- Auto-discover other nodes via the Lumera blockchain -- Securely communicate using ALTS (Application Layer Transport Security) -- Replicate data across the network for redundancy - -## Architecture - -``` -┌─────────────┐ ┌─────────────┐ ┌─────────────┐ -│ P2P API │────▶│ DHT │────▶│ Network │ -└─────────────┘ └─────────────┘ └─────────────┘ - │ │ │ - │ │ │ - ▼ ▼ ▼ -┌─────────────┐ ┌─────────────┐ ┌─────────────┐ -│ Local Store │ │ Hash Table │ │ Conn Pool │ -└─────────────┘ └─────────────┘ └─────────────┘ -``` - -- **P2P API**: Public interface for store/retrieve operations -- **DHT**: Core DHT implementation using Kademlia algorithm -- **Network**: Handles peer connections, messaging, and encryption -- **Local Store**: SQLite database for persistent storage -- **Hash Table**: Manages routing table of known peers -- **Conn Pool**: Manages network connections to peers - -## Configuration - -Key configuration parameters in the YAML config: - -```yaml -p2p: - listen_address: "0.0.0.0" # Network interface to listen on - port: 4445 # Port for P2P communication - data_dir: "~/.lumera/p2p" # Directory for DHT data storage - bootstrap_nodes: "" # Optional comma-separated list of bootstrap nodes - external_ip: "" # Optional override for auto-detected external IP -``` - -### Configuration Field Details - -| Field | Description | Default | Required | -|-------|-------------|---------|----------| -| `listen_address` | Network interface to bind to | `0.0.0.0` | Yes | -| `port` | Port to listen for P2P connections | `4445` | Yes | -| `data_dir` | Storage directory for P2P data | N/A | Yes | -| `bootstrap_nodes` | Format: `identity@host:port,identity2@host2:port2` | Auto-fetched from blockchain | No | -| `external_ip` | Public IP address for the node | Auto-detected | No | - -The **Node ID** is derived from the Lumera account in your keyring specified by the `key_name` in the supernode config. - -## Usage Example - -Initializing the P2P service: - -```go -// Create P2P configuration -p2pConfig := &p2p.Config{ - ListenAddress: "0.0.0.0", - Port: 4445, - DataDir: "/path/to/data", - ID: supernodeAddress, // Lumera account address -} - -// Initialize P2P service -p2pService, err := p2p.New(ctx, p2pConfig, lumeraClient, keyring, rqStore, nil, nil) -if err != nil { - return err -} - -// Start P2P service -if err := p2pService.Run(ctx); err != nil { - return err -} -``` - -Storing and retrieving data: - -```go -// Store data - returns base58-encoded key -key, err := p2pService.Store(ctx, []byte("Hello, world!"), 0) -if err != nil { - return err -} - -// Retrieve data -data, err := p2pService.Retrieve(ctx, key) -if err != nil { - return err -} -``` - -## Key Components - -### Keyring Integration - -The P2P service uses the Cosmos SDK keyring for: -- Secure node identity (derived from your Lumera account) -- Cryptographic signatures for secure communication -- Authentication between peers - -### Bootstrap Process - -When a node starts: -1. It checks for configured bootstrap nodes -2. If none provided, queries the Lumera blockchain for active supernodes -3. Connects to bootstrap nodes and performs iterative `FIND_NODE` queries -4. Builds its routing table based on responses -5. Becomes a full participant in the network - -### Data Replication - -Data stored in the network is: -1. Stored locally in SQLite -2. Replicated to the closest `Alpha` (6) nodes in the DHT -3. Periodically checked and re-replicated as nodes come and go - -## Troubleshooting - -- **Can't connect to network**: Verify your `external_ip` is correct or remove it to use auto-detection -- **Bootstrap fails**: Ensure the Lumera client is connected or specify manual bootstrap nodes -- **Storage issues**: Check `data_dir` path and permissions - -## Development Notes - -- Use `localOnly: true` with `Retrieve()` to only check local storage -- DHT operations use a modified Kademlia with `Alpha=6` for parallelism -- Key format is base58-encoded Blake3 hash of the data \ No newline at end of file diff --git a/supernode/cmd/config.go b/supernode/cmd/config.go index 6d857d32..dd1e5dbb 100644 --- a/supernode/cmd/config.go +++ b/supernode/cmd/config.go @@ -13,4 +13,4 @@ var configCmd = &cobra.Command{ func init() { rootCmd.AddCommand(configCmd) -} \ No newline at end of file +} diff --git a/supernode/cmd/config_update.go b/supernode/cmd/config_update.go index 16bf9176..80b56aa0 100644 --- a/supernode/cmd/config_update.go +++ b/supernode/cmd/config_update.go @@ -65,14 +65,14 @@ func promptParameterSelection() (string, error) { func updateSupernodeIP() error { var newIP string prompt := &survey.Input{ - Message: "Enter new supernode IP address:", - Default: appConfig.SupernodeConfig.IpAddress, + Message: "Enter new supernode host address:", + Default: appConfig.SupernodeConfig.Host, } if err := survey.AskOne(prompt, &newIP); err != nil { return err } - appConfig.SupernodeConfig.IpAddress = newIP + appConfig.SupernodeConfig.Host = newIP return saveConfig() } diff --git a/supernode/cmd/helpers.go b/supernode/cmd/helpers.go index bb939586..64084ae3 100644 --- a/supernode/cmd/helpers.go +++ b/supernode/cmd/helpers.go @@ -4,6 +4,7 @@ import ( "fmt" "strings" + "github.com/LumeraProtocol/supernode/p2p" "github.com/LumeraProtocol/supernode/pkg/keyring" "github.com/LumeraProtocol/supernode/supernode/config" cKeyring "github.com/cosmos/cosmos-sdk/crypto/keyring" @@ -58,3 +59,13 @@ func isValidBIP39WordCount(wordCount int) bool { } return false } + +// createP2PConfig creates a P2P config from the app config and address +func createP2PConfig(config *config.Config, address string) *p2p.Config { + return &p2p.Config{ + ListenAddress: config.SupernodeConfig.Host, + Port: config.P2PConfig.Port, + DataDir: config.GetP2PDataDir(), + ID: address, + } +} diff --git a/supernode/cmd/init.go b/supernode/cmd/init.go index 2a1ba0a6..67706e95 100644 --- a/supernode/cmd/init.go +++ b/supernode/cmd/init.go @@ -480,7 +480,7 @@ func createNewKey(kr consmoskeyring.Keyring, keyName string) (string, string, er func updateAndSaveConfig(address, supernodeAddr string, supernodePort int, gatewayPort int, lumeraGrpcAddr string, chainID string) error { // Update config with address and network settings appConfig.SupernodeConfig.Identity = address - appConfig.SupernodeConfig.IpAddress = supernodeAddr + appConfig.SupernodeConfig.Host = supernodeAddr appConfig.SupernodeConfig.Port = uint16(supernodePort) appConfig.SupernodeConfig.GatewayPort = uint16(gatewayPort) appConfig.LumeraClientConfig.GRPCAddr = lumeraGrpcAddr diff --git a/supernode/cmd/list_config.go b/supernode/cmd/list_config.go index 8ec0d53f..4d058bca 100644 --- a/supernode/cmd/list_config.go +++ b/supernode/cmd/list_config.go @@ -22,7 +22,7 @@ var configListCmd = &cobra.Command{ // Display configuration parameters fmt.Fprintf(w, "Key Name\t%s\n", appConfig.SupernodeConfig.KeyName) fmt.Fprintf(w, "Address\t%s\n", appConfig.SupernodeConfig.Identity) - fmt.Fprintf(w, "Supernode Address\t%s\n", appConfig.SupernodeConfig.IpAddress) + fmt.Fprintf(w, "Supernode Address\t%s\n", appConfig.SupernodeConfig.Host) fmt.Fprintf(w, "Supernode Port\t%d\n", appConfig.SupernodeConfig.Port) fmt.Fprintf(w, "Keyring Backend\t%s\n", appConfig.KeyringConfig.Backend) fmt.Fprintf(w, "Lumera GRPC Address\t%s\n", appConfig.LumeraClientConfig.GRPCAddr) diff --git a/supernode/cmd/start.go b/supernode/cmd/start.go index bc9e207d..9773a6f3 100644 --- a/supernode/cmd/start.go +++ b/supernode/cmd/start.go @@ -27,16 +27,6 @@ import ( "github.com/spf13/cobra" ) -// createP2PConfig creates a P2P config from the app config and address -func createP2PConfig(config *config.Config, address string) *p2p.Config { - return &p2p.Config{ - ListenAddress: config.P2PConfig.ListenAddress, - Port: config.P2PConfig.Port, - DataDir: config.GetP2PDataDir(), - ID: address, - } -} - // startCmd represents the start command var startCmd = &cobra.Command{ Use: "start", @@ -103,7 +93,7 @@ The supernode will connect to the Lumera network and begin participating in the // Set the version in the status service package supernodeService.Version = Version - + // Create supernode status service statusService := supernodeService.NewSupernodeStatusService(*p2pService, lumeraClient, appConfig) statusService.RegisterTaskProvider(cService) @@ -114,7 +104,7 @@ The supernode will connect to the Lumera network and begin participating in the // Configure server serverConfig := &server.Config{ Identity: appConfig.SupernodeConfig.Identity, - ListenAddresses: appConfig.SupernodeConfig.IpAddress, + ListenAddresses: appConfig.SupernodeConfig.Host, Port: int(appConfig.SupernodeConfig.Port), } @@ -129,7 +119,7 @@ The supernode will connect to the Lumera network and begin participating in the if gatewayPort == 0 { gatewayPort = 8002 // Default fallback } - gatewayServer, err := gateway.NewServer(int(gatewayPort), supernodeServer) + gatewayServer, err := gateway.NewServer(appConfig.SupernodeConfig.Host, int(gatewayPort), supernodeServer) if err != nil { return fmt.Errorf("failed to create gateway server: %w", err) } @@ -177,7 +167,7 @@ func initP2PService(ctx context.Context, config *config.Config, lumeraClient lum // Create P2P config using helper function p2pConfig := createP2PConfig(config, address.String()) - logtrace.Info(ctx, "Initializing P2P service", logtrace.Fields{"listen_address": p2pConfig.ListenAddress, "port": p2pConfig.Port, "data_dir": p2pConfig.DataDir, "supernode_id": address.String()}) + logtrace.Info(ctx, "Initializing P2P service", logtrace.Fields{"address": p2pConfig.ListenAddress, "port": p2pConfig.Port, "data_dir": p2pConfig.DataDir, "supernode_id": address.String()}) p2pService, err := p2p.New(ctx, p2pConfig, lumeraClient, kr, rqStore, cloud, mst) if err != nil { diff --git a/supernode/config.yml b/supernode/config.yml index 5bf122c5..99883d59 100644 --- a/supernode/config.yml +++ b/supernode/config.yml @@ -2,7 +2,7 @@ supernode: key_name: "mykey" # Account name for the supernode in keyring identity: "lumera1ccmw5plzuldntum2rz6kq6uq346vtrhrvwfzsa" # Identity of the supernode, lumera address - ip_address: "0.0.0.0" + host: "0.0.0.0" port: 4444 gateway_port: 8002 # Port for the HTTP gateway @@ -13,7 +13,6 @@ keyring: # P2P Network Configuration p2p: - listen_address: "0.0.0.0" port: 4445 data_dir: "data/p2p" # P2P data directory in home folder diff --git a/supernode/config/config.go b/supernode/config/config.go index 309d3545..3b8a2a89 100644 --- a/supernode/config/config.go +++ b/supernode/config/config.go @@ -13,7 +13,7 @@ import ( type SupernodeConfig struct { KeyName string `yaml:"key_name"` Identity string `yaml:"identity"` - IpAddress string `yaml:"ip_address"` + Host string `yaml:"host"` Port uint16 `yaml:"port"` GatewayPort uint16 `yaml:"gateway_port"` } @@ -27,9 +27,8 @@ type KeyringConfig struct { } type P2PConfig struct { - ListenAddress string `yaml:"listen_address"` - Port uint16 `yaml:"port"` - DataDir string `yaml:"data_dir"` + Port uint16 `yaml:"port"` + DataDir string `yaml:"data_dir"` } type LumeraClientConfig struct { diff --git a/supernode/config/save.go b/supernode/config/save.go index e821f458..52cf5b04 100644 --- a/supernode/config/save.go +++ b/supernode/config/save.go @@ -44,7 +44,7 @@ func CreateDefaultConfig(keyName, identity, chainID string, keyringBackend, keyr SupernodeConfig: SupernodeConfig{ KeyName: keyName, Identity: identity, - IpAddress: "0.0.0.0", + Host: "0.0.0.0", Port: 4444, GatewayPort: 8002, }, @@ -56,9 +56,8 @@ func CreateDefaultConfig(keyName, identity, chainID string, keyringBackend, keyr PassFile: passFile, }, P2PConfig: P2PConfig{ - ListenAddress: "0.0.0.0", - Port: 4445, - DataDir: "data/p2p", + Port: 4445, + DataDir: "data/p2p", }, LumeraClientConfig: LumeraClientConfig{ GRPCAddr: "localhost:9090", diff --git a/supernode/node/supernode/gateway/server.go b/supernode/node/supernode/gateway/server.go index b0f304f3..e72effd5 100644 --- a/supernode/node/supernode/gateway/server.go +++ b/supernode/node/supernode/gateway/server.go @@ -3,7 +3,9 @@ package gateway import ( "context" "fmt" + "net" "net/http" + "strconv" "time" "github.com/grpc-ecosystem/grpc-gateway/runtime" @@ -14,18 +16,20 @@ import ( // Server represents the HTTP gateway server type Server struct { + ipAddress string port int server *http.Server supernodeServer pb.SupernodeServiceServer } // NewServer creates a new HTTP gateway server that directly calls the service -func NewServer(port int, supernodeServer pb.SupernodeServiceServer) (*Server, error) { +func NewServer(ipAddress string, port int, supernodeServer pb.SupernodeServiceServer) (*Server, error) { if supernodeServer == nil { return nil, fmt.Errorf("supernode server is required") } return &Server{ + ipAddress: ipAddress, port: port, supernodeServer: supernodeServer, }, nil @@ -66,7 +70,7 @@ func (s *Server) Run(ctx context.Context) error { // Create HTTP server s.server = &http.Server{ - Addr: fmt.Sprintf(":%d", s.port), + Addr: net.JoinHostPort(s.ipAddress, strconv.Itoa(s.port)), Handler: s.corsMiddleware(httpMux), ReadTimeout: 15 * time.Second, WriteTimeout: 15 * time.Second, @@ -74,7 +78,8 @@ func (s *Server) Run(ctx context.Context) error { } logtrace.Info(ctx, "Starting HTTP gateway server", logtrace.Fields{ - "port": s.port, + "address": s.ipAddress, + "port": s.port, }) // Start server diff --git a/supernode/node/supernode/server/config.go b/supernode/node/supernode/server/config.go index 00728dd2..4e9d0f23 100644 --- a/supernode/node/supernode/server/config.go +++ b/supernode/node/supernode/server/config.go @@ -1,8 +1,7 @@ package server const ( - defaultListenAddresses = "0.0.0.0" - defaultPort = 4444 + defaultPort = 4444 ) // Config contains settings of the supernode server. @@ -15,7 +14,6 @@ type Config struct { // NewConfig returns a new Config instance. func NewConfig() *Config { return &Config{ - ListenAddresses: defaultListenAddresses, - Port: defaultPort, + Port: defaultPort, } } diff --git a/supernode/node/supernode/server/config_test.go b/supernode/node/supernode/server/config_test.go index 80f6f3ba..33e06f68 100644 --- a/supernode/node/supernode/server/config_test.go +++ b/supernode/node/supernode/server/config_test.go @@ -10,7 +10,7 @@ func TestNewConfig_Defaults(t *testing.T) { cfg := NewConfig() assert.NotNil(t, cfg) - assert.Equal(t, "0.0.0.0", cfg.ListenAddresses, "default listen address should be 0.0.0.0") + assert.Equal(t, "", cfg.ListenAddresses, "default listen address should be empty") assert.Equal(t, 4444, cfg.Port, "default port should be 4444") assert.Equal(t, "", cfg.Identity, "default identity should be empty") } diff --git a/supernode/node/supernode/server/server_test.go b/supernode/node/supernode/server/server_test.go index e2a86e4c..419a8cc0 100644 --- a/supernode/node/supernode/server/server_test.go +++ b/supernode/node/supernode/server/server_test.go @@ -31,6 +31,7 @@ func TestNewServer_WithValidConfig(t *testing.T) { mockLumeraClient := lumera.NewMockClient(ctl) cfg := NewConfig() + cfg.ListenAddresses = "127.0.0.1" s, err := New(cfg, "supernode-test", mockKeyring, mockLumeraClient, &mockService{}) assert.NoError(t, err) assert.NotNil(t, s) @@ -56,6 +57,7 @@ func TestSetServiceStatusAndClose(t *testing.T) { mockLumeraClient := lumera.NewMockClient(ctl) cfg := NewConfig() + cfg.ListenAddresses = "127.0.0.1" s, _ := New(cfg, "test", mockKeyring, mockLumeraClient, &mockService{}) _ = s.setupGRPCServer() diff --git a/supernode/services/common/supernode/service.go b/supernode/services/common/supernode/service.go index 9a150d1e..74ec360c 100644 --- a/supernode/services/common/supernode/service.go +++ b/supernode/services/common/supernode/service.go @@ -178,7 +178,7 @@ func (s *SupernodeStatusService) GetStatus(ctx context.Context) (StatusResponse, // Set IP address from config if s.config != nil { - resp.IPAddress = fmt.Sprintf("%s:%d", s.config.SupernodeConfig.IpAddress, s.config.P2PConfig.Port) + resp.IPAddress = fmt.Sprintf("%s:%d", s.config.SupernodeConfig.Host, s.config.P2PConfig.Port) } // Log summary statistics diff --git a/tests/system/config.test-1.yml b/tests/system/config.test-1.yml index 70bd352a..eb214cd4 100644 --- a/tests/system/config.test-1.yml +++ b/tests/system/config.test-1.yml @@ -2,7 +2,7 @@ supernode: key_name: "testkey1" identity: "lumera1em87kgrvgttrkvuamtetyaagjrhnu3vjy44at4" - ip_address: "0.0.0.0" + host: "0.0.0.0" port: 4444 gateway_port: 8002 @@ -14,7 +14,6 @@ keyring: # P2P Network Configuration p2p: - listen_address: "0.0.0.0" port: 4445 data_dir: "data/p2p" # Relative to base_dir diff --git a/tests/system/config.test-2.yml b/tests/system/config.test-2.yml index b3fa9c7e..1b044a89 100644 --- a/tests/system/config.test-2.yml +++ b/tests/system/config.test-2.yml @@ -3,7 +3,7 @@ supernode: key_name: "testkey2" identity: "lumera1cf0ms9ttgdvz6zwlqfty4tjcawhuaq69p40w0c" - ip_address: "0.0.0.0" + host: "0.0.0.0" port: 4446 gateway_port: 8003 @@ -15,7 +15,6 @@ keyring: # P2P Network Configuration p2p: - listen_address: "0.0.0.0" port: 4447 data_dir: "data/p2p" diff --git a/tests/system/config.test-3.yml b/tests/system/config.test-3.yml index 4835feb8..2a259066 100644 --- a/tests/system/config.test-3.yml +++ b/tests/system/config.test-3.yml @@ -3,7 +3,7 @@ supernode: key_name: "testkey3" identity: "lumera1cjyc4ruq739e2lakuhargejjkr0q5vg6x3d7kp" - ip_address: "0.0.0.0" + host: "0.0.0.0" port: 4448 gateway_port: 8004 @@ -15,7 +15,6 @@ keyring: # P2P Network Configuration p2p: - listen_address: "0.0.0.0" port: 4449 data_dir: "data/p2p"