diff --git a/p2p/config.go b/p2p/config.go index c78dc3f2..a330249f 100644 --- a/p2p/config.go +++ b/p2p/config.go @@ -22,8 +22,6 @@ type Config struct { // BootstrapNodes is ONLY used for integration testing to inject a Node's IP address BootstrapNodes string `mapstructure:"bootstrap_nodes" json:"bootstrap_nodes,omitempty"` - // ExternalIP is ONLY used for integration testing to assign a fixed IP address - ExternalIP string `mapstructure:"external_ip" json:"external_ip,omitempty"` // ID of supernode to be used in P2P - Supposed to be the PastelID ID string `mapstructure:"id" json:"-"` diff --git a/p2p/kademlia/bootstrap.go b/p2p/kademlia/bootstrap.go index 0f18a5e4..18669da6 100644 --- a/p2p/kademlia/bootstrap.go +++ b/p2p/kademlia/bootstrap.go @@ -106,11 +106,11 @@ func (s *DHT) ConfigureBootstrapNodes(ctx context.Context, bootstrapNodes string return s.setBootstrapNodesFromConfigVar(ctx, bootstrapNodes) } - selfAddress, err := s.getExternalIP() + supernodeAddr, err := s.getSupernodeAddress(ctx) if err != nil { - return fmt.Errorf("get external ip addr: %s", err) + return fmt.Errorf("get supernode address: %s", err) } - selfAddress = fmt.Sprintf("%s:%d", selfAddress, s.options.Port) + selfAddress := fmt.Sprintf("%s:%d", parseSupernodeAddress(supernodeAddr), s.options.Port) var boostrapNodes []*Node diff --git a/p2p/kademlia/dht.go b/p2p/kademlia/dht.go index 843472f0..f6d93421 100644 --- a/p2p/kademlia/dht.go +++ b/p2p/kademlia/dht.go @@ -6,7 +6,7 @@ import ( "encoding/hex" "fmt" "math" - "os" + "strings" "sync" "sync/atomic" "time" @@ -55,7 +55,7 @@ type DHT struct { done chan struct{} // distributed hash table is done cache storage.KeyValue // store bad bootstrap addresses bsConnected map[string]bool // map of connected bootstrap nodes [identity] -> connected - externalIP string + supernodeAddr string // cached address from chain mtx sync.Mutex ignorelist *BanList replicationMtx sync.RWMutex @@ -81,8 +81,6 @@ type Options struct { // Keyring for credentials Keyring keyring.Keyring - - ExternalIP string } // NewDHT returns a new DHT node @@ -107,10 +105,6 @@ func NewDHT(ctx context.Context, store Store, metaStore MetaStore, options *Opti rqstore: rqstore, } - if options.ExternalIP != "" { - s.externalIP = options.ExternalIP - } - // Check that keyring is provided if options.Keyring == nil { return nil, fmt.Errorf("keyring is required but not provided") @@ -166,28 +160,42 @@ func (s *DHT) NodesLen() int { return len(s.ht.nodes()) } -func (s *DHT) getExternalIP() (string, error) { +func (s *DHT) getSupernodeAddress(ctx context.Context) (string, error) { s.mtx.Lock() defer s.mtx.Unlock() // Return cached value if already determined - if s.externalIP != "" { - return s.externalIP, nil + if s.supernodeAddr != "" { + return s.supernodeAddr, nil } - // Check environment variable to control IP behavior - if useExternal := os.Getenv("P2P_USE_EXTERNAL_IP"); useExternal == "false" || useExternal == "0" { - s.externalIP = s.ht.self.IP - return s.externalIP, nil + // Query chain for supernode info + supernodeInfo, err := s.options.LumeraClient.SuperNode().GetSupernodeWithLatestAddress(ctx, string(s.options.ID)) + if err != nil || supernodeInfo == nil { + // Fallback to local IP if chain query fails + s.supernodeAddr = s.ht.self.IP + return s.supernodeAddr, nil } - externalIP, err := utils.GetExternalIPAddress() - if err != nil { - return "", fmt.Errorf("get external ip addr: %s", err) + s.supernodeAddr = supernodeInfo.LatestAddress + return supernodeInfo.LatestAddress, nil +} + +// parseSupernodeAddress extracts the host from various address formats +func parseSupernodeAddress(address string) string { + // Remove protocol prefixes + if strings.HasPrefix(address, "https://") { + address = strings.TrimPrefix(address, "https://") + } else if strings.HasPrefix(address, "http://") { + address = strings.TrimPrefix(address, "http://") + } + + // Extract host part (remove port if present) + if idx := strings.LastIndex(address, ":"); idx != -1 { + return address[:idx] } - s.externalIP = externalIP - return externalIP, nil + return address } // Start the distributed hash table @@ -365,9 +373,11 @@ func (s *DHT) Stats(ctx context.Context) (map[string]interface{}, error) { // new a message func (s *DHT) newMessage(messageType int, receiver *Node, data interface{}) *Message { - externalIP, _ := s.getExternalIP() + ctx := context.Background() + supernodeAddr, _ := s.getSupernodeAddress(ctx) + hostIP := parseSupernodeAddress(supernodeAddr) sender := &Node{ - IP: externalIP, + IP: hostIP, ID: s.ht.self.ID, Port: s.ht.self.Port, } @@ -574,7 +584,9 @@ func (s *DHT) BatchRetrieve(ctx context.Context, keys []string, required int32, result[key] = nil } - self := &Node{ID: s.ht.self.ID, IP: s.externalIP, Port: s.ht.self.Port} + supernodeAddr, _ := s.getSupernodeAddress(ctx) + hostIP := parseSupernodeAddress(supernodeAddr) + self := &Node{ID: s.ht.self.ID, IP: hostIP, Port: s.ht.self.Port} self.SetHashedID() // populate hexKeys and hashes diff --git a/p2p/kademlia/redundant_data.go b/p2p/kademlia/redundant_data.go index 52b10549..89c028ee 100644 --- a/p2p/kademlia/redundant_data.go +++ b/p2p/kademlia/redundant_data.go @@ -71,7 +71,9 @@ func (s *DHT) cleanupRedundantDataWorker(ctx context.Context) { replicationKeys := s.store.GetKeysForReplication(ctx, from, to) ignores := s.ignorelist.ToNodeList() - self := &Node{ID: s.ht.self.ID, IP: s.externalIP, Port: s.ht.self.Port} + supernodeAddr, _ := s.getSupernodeAddress(ctx) + hostIP := parseSupernodeAddress(supernodeAddr) + self := &Node{ID: s.ht.self.ID, IP: hostIP, Port: s.ht.self.Port} self.SetHashedID() closestContactsMap := make(map[string][][]byte) diff --git a/p2p/kademlia/replication.go b/p2p/kademlia/replication.go index d2c4b582..f7f6f301 100644 --- a/p2p/kademlia/replication.go +++ b/p2p/kademlia/replication.go @@ -166,7 +166,9 @@ func (s *DHT) Replicate(ctx context.Context) { ignores := s.ignorelist.ToNodeList() closestContactsMap := make(map[string][][]byte) - self := &Node{ID: s.ht.self.ID, IP: s.externalIP, Port: s.ht.self.Port} + supernodeAddr, _ := s.getSupernodeAddress(ctx) + hostIP := parseSupernodeAddress(supernodeAddr) + self := &Node{ID: s.ht.self.ID, IP: hostIP, Port: s.ht.self.Port} self.SetHashedID() for i := 0; i < len(replicationKeys); i++ { diff --git a/p2p/kademlia/store/sqlite/replication.go b/p2p/kademlia/store/sqlite/replication.go index efb12ac2..ff7f77ba 100644 --- a/p2p/kademlia/store/sqlite/replication.go +++ b/p2p/kademlia/store/sqlite/replication.go @@ -243,7 +243,7 @@ func (s *Store) StoreBatchRepKeys(values []string, id string, ip string, port ui func (s *Store) GetKeysForReplication(ctx context.Context, from time.Time, to time.Time) domain.KeysWithTimestamp { var results []domain.KeyWithTimestamp query := `SELECT key, createdAt FROM data WHERE createdAt > ? AND createdAt < ? ORDER BY createdAt ASC` - + logtrace.Debug(ctx, "fetching keys for replication", logtrace.Fields{ logtrace.FieldModule: "p2p", "from_time": from, diff --git a/p2p/p2p.go b/p2p/p2p.go index c3822bbe..b037044a 100644 --- a/p2p/p2p.go +++ b/p2p/p2p.go @@ -238,10 +238,6 @@ func (s *p2p) configure(ctx context.Context) error { errors.Errorf("node id is empty") } - // We Set ExternalIP only for integration tests - if s.config.BootstrapNodes != "" && s.config.ExternalIP != "" { - kadOpts.ExternalIP = s.config.ExternalIP - } // new a kademlia distributed hash table dht, err := kademlia.NewDHT(ctx, s.store, s.metaStore, kadOpts, s.rqstore) diff --git a/pkg/lumera/modules/supernode/impl.go b/pkg/lumera/modules/supernode/impl.go index 3ab38d4f..a2249f32 100644 --- a/pkg/lumera/modules/supernode/impl.go +++ b/pkg/lumera/modules/supernode/impl.go @@ -91,3 +91,37 @@ func GetLatestIP(supernode *types.SuperNode) (string, error) { return supernode.PrevIpAddresses[0].Address, nil } + +// GetSupernodeWithLatestAddress gets a supernode by account address and returns comprehensive info +func (m *module) GetSupernodeWithLatestAddress(ctx context.Context, address string) (*SuperNodeInfo, error) { + supernode, err := m.GetSupernodeBySupernodeAddress(ctx, address) + if err != nil { + return nil, fmt.Errorf("failed to get supernode: %w", err) + } + + // Get latest IP address + var latestAddress string + if len(supernode.PrevIpAddresses) > 0 { + sort.Slice(supernode.PrevIpAddresses, func(i, j int) bool { + return supernode.PrevIpAddresses[i].GetHeight() > supernode.PrevIpAddresses[j].GetHeight() + }) + latestAddress = supernode.PrevIpAddresses[0].Address + } + + // Get latest state + var currentState string + if len(supernode.States) > 0 { + sort.Slice(supernode.States, func(i, j int) bool { + return supernode.States[i].Height > supernode.States[j].Height + }) + currentState = supernode.States[0].State.String() + } + + return &SuperNodeInfo{ + SupernodeAccount: supernode.SupernodeAccount, + ValidatorAddress: supernode.ValidatorAddress, + P2PPort: supernode.P2PPort, + LatestAddress: latestAddress, + CurrentState: currentState, + }, nil +} diff --git a/pkg/lumera/modules/supernode/interface.go b/pkg/lumera/modules/supernode/interface.go index 54d1eb39..80e70b53 100644 --- a/pkg/lumera/modules/supernode/interface.go +++ b/pkg/lumera/modules/supernode/interface.go @@ -8,11 +8,21 @@ import ( "google.golang.org/grpc" ) +// SuperNodeInfo contains processed supernode information with latest state and address +type SuperNodeInfo struct { + SupernodeAccount string `json:"supernode_account"` + ValidatorAddress string `json:"validator_address"` + P2PPort string `json:"p2p_port"` + LatestAddress string `json:"latest_address"` + CurrentState string `json:"current_state"` +} + // Module defines the interface for interacting with the supernode module type Module interface { GetTopSuperNodesForBlock(ctx context.Context, blockHeight uint64) (*types.QueryGetTopSuperNodesForBlockResponse, error) GetSuperNode(ctx context.Context, address string) (*types.QueryGetSuperNodeResponse, error) GetSupernodeBySupernodeAddress(ctx context.Context, address string) (*types.SuperNode, error) + GetSupernodeWithLatestAddress(ctx context.Context, address string) (*SuperNodeInfo, error) GetParams(ctx context.Context) (*types.QueryParamsResponse, error) } diff --git a/pkg/lumera/modules/supernode/supernode_mock.go b/pkg/lumera/modules/supernode/supernode_mock.go index 6f443e06..a4247cba 100644 --- a/pkg/lumera/modules/supernode/supernode_mock.go +++ b/pkg/lumera/modules/supernode/supernode_mock.go @@ -86,6 +86,21 @@ func (mr *MockModuleMockRecorder) GetSupernodeBySupernodeAddress(ctx, address an return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSupernodeBySupernodeAddress", reflect.TypeOf((*MockModule)(nil).GetSupernodeBySupernodeAddress), ctx, address) } +// GetSupernodeWithLatestAddress mocks base method. +func (m *MockModule) GetSupernodeWithLatestAddress(ctx context.Context, address string) (*SuperNodeInfo, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetSupernodeWithLatestAddress", ctx, address) + ret0, _ := ret[0].(*SuperNodeInfo) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetSupernodeWithLatestAddress indicates an expected call of GetSupernodeWithLatestAddress. +func (mr *MockModuleMockRecorder) GetSupernodeWithLatestAddress(ctx, address any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSupernodeWithLatestAddress", reflect.TypeOf((*MockModule)(nil).GetSupernodeWithLatestAddress), ctx, address) +} + // GetTopSuperNodesForBlock mocks base method. func (m *MockModule) GetTopSuperNodesForBlock(ctx context.Context, blockHeight uint64) (*types.QueryGetTopSuperNodesForBlockResponse, error) { m.ctrl.T.Helper() diff --git a/pkg/testutil/lumera.go b/pkg/testutil/lumera.go index c179c5fd..c77dd63d 100644 --- a/pkg/testutil/lumera.go +++ b/pkg/testutil/lumera.go @@ -164,6 +164,16 @@ func (m *MockSupernodeModule) GetParams(ctx context.Context) (*supernodeTypes.Qu return &supernodeTypes.QueryParamsResponse{}, nil } +func (m *MockSupernodeModule) GetSupernodeWithLatestAddress(ctx context.Context, address string) (*supernode.SuperNodeInfo, error) { + return &supernode.SuperNodeInfo{ + SupernodeAccount: address, + ValidatorAddress: "validator_" + address, + P2PPort: "4445", + LatestAddress: "127.0.0.1:9000", + CurrentState: "SUPERNODE_STATE_ACTIVE", + }, nil +} + // MockTxModule implements the tx.Module interface for testing type MockTxModule struct{} diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index 541b99a6..81291cb0 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -14,7 +14,6 @@ import ( "math" "math/big" "net" - "net/http" "os" "os/exec" "path/filepath" @@ -38,41 +37,6 @@ const ( highCompressionLevel = 4 ) -var ipEndpoints = []string{ - "https://api.ipify.org", - "https://ifconfig.co/ip", - "https://checkip.amazonaws.com", - "https://ipv4.icanhazip.com", -} - -// GetExternalIPAddress returns the first valid public IP obtained -// from a list of providers, or an error if none work. -// func GetExternalIPAddress() (string, error) { -// client := &http.Client{Timeout: 4 * time.Second} - -// for _, url := range ipEndpoints { -// req, _ := http.NewRequest(http.MethodGet, url, nil) - -// resp, err := client.Do(req) -// if err != nil { -// continue // provider down? try next -// } - -// body, err := io.ReadAll(resp.Body) -// resp.Body.Close() -// if err != nil { -// continue -// } - -// ip := strings.TrimSpace(string(body)) -// if net.ParseIP(ip) != nil { -// return ip, nil -// } -// } - -// return "", errors.New("unable to determine external IP address from any provider") -// } - var sem = semaphore.NewWeighted(maxParallelHighCompressCalls) // DiskStatus cotains info of disk storage @@ -133,27 +97,6 @@ func IsContextErr(err error) bool { return false } -// GetExternalIPAddress returns external IP address -func GetExternalIPAddress() (externalIP string, err error) { - resp, err := http.Get("https://api.ipify.org") - if err != nil { - return "", err - } - - defer resp.Body.Close() - - body, err := io.ReadAll(resp.Body) - if err != nil { - return "", err - } - - if net.ParseIP(string(body)) == nil { - return "", errors.Errorf("invalid IP response from %s", "api.ipify.org") - } - - return string(body), nil -} - // B64Encode base64 encodes func B64Encode(in []byte) (out []byte) { out = make([]byte, base64.StdEncoding.EncodedLen(len(in))) diff --git a/sdk/action/client.go b/sdk/action/client.go index 16242258..fc3c2d7e 100644 --- a/sdk/action/client.go +++ b/sdk/action/client.go @@ -159,24 +159,17 @@ func (c *ClientImpl) GetSupernodeStatus(ctx context.Context, supernodeAddress st c.logger.Debug(ctx, "Getting supernode status", "address", supernodeAddress) - // Get supernode details from blockchain - supernode, err := c.lumeraClient.GetSupernodeBySupernodeAddress(ctx, supernodeAddress) + // Get supernode info including latest address + supernodeInfo, err := c.lumeraClient.GetSupernodeWithLatestAddress(ctx, supernodeAddress) if err != nil { - c.logger.Error(ctx, "Failed to get supernode details", "address", supernodeAddress, "error", err) - return nil, fmt.Errorf("failed to get supernode details: %w", err) + c.logger.Error(ctx, "Failed to get supernode info", "address", supernodeAddress, "error", err) + return nil, fmt.Errorf("failed to get supernode info: %w", err) } - // Get the latest IP address for the supernode - if len(supernode.PrevIpAddresses) == 0 { - return nil, fmt.Errorf("no IP addresses found for supernode %s", supernodeAddress) - } - - ipAddress := supernode.PrevIpAddresses[0].Address - // Create lumera supernode object for network client lumeraSupernode := lumera.Supernode{ CosmosAddress: supernodeAddress, - GrpcEndpoint: ipAddress, + GrpcEndpoint: supernodeInfo.LatestAddress, State: lumera.SUPERNODE_STATE_ACTIVE, // Assume active since we're querying } diff --git a/sdk/adapters/lumera/adapter.go b/sdk/adapters/lumera/adapter.go index 826dc2f1..8fe7a1fb 100644 --- a/sdk/adapters/lumera/adapter.go +++ b/sdk/adapters/lumera/adapter.go @@ -22,10 +22,20 @@ type Client interface { GetAction(ctx context.Context, actionID string) (Action, error) GetSupernodes(ctx context.Context, height int64) ([]Supernode, error) GetSupernodeBySupernodeAddress(ctx context.Context, address string) (*sntypes.SuperNode, error) + GetSupernodeWithLatestAddress(ctx context.Context, address string) (*SuperNodeInfo, error) DecodeCascadeMetadata(ctx context.Context, action Action) (actiontypes.CascadeMetadata, error) VerifySignature(ctx context.Context, accountAddr string, data []byte, signature []byte) error } +// SuperNodeInfo contains supernode information with latest address +type SuperNodeInfo struct { + SupernodeAccount string `json:"supernode_account"` + ValidatorAddress string `json:"validator_address"` + P2PPort string `json:"p2p_port"` + LatestAddress string `json:"latest_address"` + CurrentState string `json:"current_state"` +} + // ConfigParams holds configuration parameters from global config type ConfigParams struct { GRPCAddr string @@ -81,6 +91,54 @@ func (a *Adapter) GetSupernodeBySupernodeAddress(ctx context.Context, address st return resp, nil } +func (a *Adapter) GetSupernodeWithLatestAddress(ctx context.Context, address string) (*SuperNodeInfo, error) { + a.logger.Debug(ctx, "Getting supernode with latest address", "address", address) + + resp, err := a.client.SuperNode().GetSupernodeBySupernodeAddress(ctx, address) + if err != nil { + a.logger.Error(ctx, "Failed to get supernode", "address", address, "error", err) + return nil, fmt.Errorf("failed to get supernode: %w", err) + } + if resp == nil { + a.logger.Error(ctx, "Received nil response for supernode", "address", address) + return nil, fmt.Errorf("received nil response for supernode %s", address) + } + + // Sort PrevIpAddresses by height in descending order + sort.Slice(resp.PrevIpAddresses, func(i, j int) bool { + return resp.PrevIpAddresses[i].Height > resp.PrevIpAddresses[j].Height + }) + + // Sort States by height in descending order + sort.Slice(resp.States, func(i, j int) bool { + return resp.States[i].Height > resp.States[j].Height + }) + + // Extract latest address + latestAddress := "" + if len(resp.PrevIpAddresses) > 0 { + latestAddress = resp.PrevIpAddresses[0].Address + } + + // Extract current state + currentState := "" + if len(resp.States) > 0 { + currentState = resp.States[0].State.String() + } + + info := &SuperNodeInfo{ + SupernodeAccount: resp.SupernodeAccount, + ValidatorAddress: resp.ValidatorAddress, + P2PPort: resp.P2PPort, + LatestAddress: latestAddress, + CurrentState: currentState, + } + + a.logger.Debug(ctx, "Successfully retrieved supernode with latest address", + "address", address, "latestAddress", latestAddress, "currentState", currentState) + return info, nil +} + func (a *Adapter) AccountInfoByAddress(ctx context.Context, addr string) (*authtypes.QueryAccountInfoResponse, error) { a.logger.Debug(ctx, "Getting account info by address", "address", addr) resp, err := a.client.Auth().AccountInfoByAddress(ctx, addr) diff --git a/supernode/services/common/supernode/service.go b/supernode/services/common/supernode/service.go index 0431887b..56dfc5ba 100644 --- a/supernode/services/common/supernode/service.go +++ b/supernode/services/common/supernode/service.go @@ -3,16 +3,12 @@ package supernode import ( "context" "fmt" - "io" - "net/http" - "strings" "time" "github.com/LumeraProtocol/supernode/v2/p2p" "github.com/LumeraProtocol/supernode/v2/p2p/kademlia" "github.com/LumeraProtocol/supernode/v2/pkg/logtrace" "github.com/LumeraProtocol/supernode/v2/pkg/lumera" - snmodule "github.com/LumeraProtocol/supernode/v2/pkg/lumera/modules/supernode" "github.com/LumeraProtocol/supernode/v2/supernode/config" ) @@ -180,20 +176,11 @@ func (s *SupernodeStatusService) GetStatus(ctx context.Context) (StatusResponse, } } - // Set IP address from chain, fallback to external IP if s.config != nil && s.lumeraClient != nil { - supernode, err := s.lumeraClient.SuperNode().GetSupernodeBySupernodeAddress(ctx, s.config.SupernodeConfig.Identity) - if err == nil && supernode != nil { - if latestIP, err := snmodule.GetLatestIP(supernode); err == nil { - resp.IPAddress = latestIP - } + if supernodeInfo, err := s.lumeraClient.SuperNode().GetSupernodeWithLatestAddress(ctx, s.config.SupernodeConfig.Identity); err == nil && supernodeInfo != nil { + resp.IPAddress = supernodeInfo.LatestAddress } - if resp.IPAddress == "" { - if externalIP, err := s.getExternalIP(ctx); err == nil { - resp.IPAddress = fmt.Sprintf("%s:%d", externalIP, s.config.SupernodeConfig.Port) - } - } } // Log summary statistics @@ -204,38 +191,3 @@ func (s *SupernodeStatusService) GetStatus(ctx context.Context) (StatusResponse, return resp, nil } - -// getExternalIP queries an external service to determine the public IP address -func (s *SupernodeStatusService) getExternalIP(ctx context.Context) (string, error) { - // Create HTTP client with timeout - client := &http.Client{ - Timeout: 5 * time.Second, - } - - // Create request with context - req, err := http.NewRequestWithContext(ctx, "GET", "https://api.ipify.org", nil) - if err != nil { - return "", fmt.Errorf("failed to create request: %w", err) - } - - // Make the request - resp, err := client.Do(req) - if err != nil { - return "", fmt.Errorf("failed to get external IP: %w", err) - } - defer resp.Body.Close() - - // Read the response - body, err := io.ReadAll(resp.Body) - if err != nil { - return "", fmt.Errorf("failed to read response: %w", err) - } - - // Clean up the IP address - ip := strings.TrimSpace(string(body)) - if ip == "" { - return "", fmt.Errorf("received empty IP address from external service") - } - - return ip, nil -} diff --git a/supernode/services/verifier/interface.go b/supernode/services/verifier/interface.go index 9ec3d9ab..7414201a 100644 --- a/supernode/services/verifier/interface.go +++ b/supernode/services/verifier/interface.go @@ -13,9 +13,9 @@ type ConfigVerifierService interface { // VerificationResult contains the results of config verification type VerificationResult struct { - Valid bool `json:"valid"` - Errors []ConfigError `json:"errors,omitempty"` - Warnings []ConfigError `json:"warnings,omitempty"` + Valid bool `json:"valid"` + Errors []ConfigError `json:"errors,omitempty"` + Warnings []ConfigError `json:"warnings,omitempty"` } // ConfigError represents a configuration validation error or warning @@ -52,4 +52,4 @@ func (vr *VerificationResult) Summary() string { } return strings.TrimSuffix(summary, "\n") -} \ No newline at end of file +} diff --git a/supernode/services/verifier/verifier.go b/supernode/services/verifier/verifier.go index c52a5f2f..867bd966 100644 --- a/supernode/services/verifier/verifier.go +++ b/supernode/services/verifier/verifier.go @@ -5,9 +5,9 @@ import ( "fmt" "net" - sntypes "github.com/LumeraProtocol/lumera/x/supernode/v1/types" "github.com/LumeraProtocol/supernode/v2/pkg/logtrace" "github.com/LumeraProtocol/supernode/v2/pkg/lumera" + snmodule "github.com/LumeraProtocol/supernode/v2/pkg/lumera/modules/supernode" "github.com/LumeraProtocol/supernode/v2/supernode/config" "github.com/cosmos/cosmos-sdk/crypto/keyring" sdk "github.com/cosmos/cosmos-sdk/types" @@ -59,23 +59,20 @@ func (cv *ConfigVerifier) VerifyConfig(ctx context.Context) (*VerificationResult } // Check 3: Query chain for supernode registration - supernode, err := cv.checkSupernodeExists(ctx, result) + supernodeInfo, err := cv.checkSupernodeExists(ctx, result) if err != nil { return result, err } // If supernode doesn't exist, don't proceed with field comparisons - if supernode == nil { + if supernodeInfo == nil { return result, nil } - // Check 4: Verify P2P port matches - cv.checkP2PPortMatches(result, supernode) + // Check 4: Verify supernode state is active + cv.checkSupernodeState(result, supernodeInfo) - // Check 5: Verify supernode state is active - cv.checkSupernodeState(result, supernode) - - // Check 6: Verify all required ports are available + // Check 5: Verify all required ports are available cv.checkPortsAvailable(result) logtrace.Info(ctx, "Config verification completed", logtrace.Fields{ @@ -128,8 +125,8 @@ func (cv *ConfigVerifier) checkIdentityMatches(result *VerificationResult) error } // checkSupernodeExists queries chain for supernode registration -func (cv *ConfigVerifier) checkSupernodeExists(ctx context.Context, result *VerificationResult) (*sntypes.SuperNode, error) { - sn, err := cv.lumeraClient.SuperNode().GetSupernodeBySupernodeAddress(ctx, cv.config.SupernodeConfig.Identity) +func (cv *ConfigVerifier) checkSupernodeExists(ctx context.Context, result *VerificationResult) (*snmodule.SuperNodeInfo, error) { + sn, err := cv.lumeraClient.SuperNode().GetSupernodeWithLatestAddress(ctx, cv.config.SupernodeConfig.Identity) if err != nil { result.Valid = false result.Errors = append(result.Errors, ConfigError{ @@ -143,9 +140,9 @@ func (cv *ConfigVerifier) checkSupernodeExists(ctx context.Context, result *Veri } // checkP2PPortMatches compares config P2P port with chain -func (cv *ConfigVerifier) checkP2PPortMatches(result *VerificationResult, supernode *sntypes.SuperNode) { +func (cv *ConfigVerifier) checkP2PPortMatches(result *VerificationResult, supernodeInfo *snmodule.SuperNodeInfo) { configPort := fmt.Sprintf("%d", cv.config.P2PConfig.Port) - chainPort := supernode.P2PPort + chainPort := supernodeInfo.P2PPort if chainPort != "" && chainPort != configPort { result.Valid = false @@ -159,18 +156,15 @@ func (cv *ConfigVerifier) checkP2PPortMatches(result *VerificationResult, supern } // checkSupernodeState verifies supernode is in active state -func (cv *ConfigVerifier) checkSupernodeState(result *VerificationResult, supernode *sntypes.SuperNode) { - if len(supernode.States) > 0 { - lastState := supernode.States[len(supernode.States)-1] - if lastState.State.String() != "SUPERNODE_STATE_ACTIVE" { - result.Valid = false - result.Errors = append(result.Errors, ConfigError{ - Field: "state", - Expected: "SUPERNODE_STATE_ACTIVE", - Actual: lastState.State.String(), - Message: fmt.Sprintf("Supernode state is %s (expected ACTIVE)", lastState.State.String()), - }) - } +func (cv *ConfigVerifier) checkSupernodeState(result *VerificationResult, supernodeInfo *snmodule.SuperNodeInfo) { + if supernodeInfo.CurrentState != "" && supernodeInfo.CurrentState != "SUPERNODE_STATE_ACTIVE" { + result.Valid = false + result.Errors = append(result.Errors, ConfigError{ + Field: "state", + Expected: "SUPERNODE_STATE_ACTIVE", + Actual: supernodeInfo.CurrentState, + Message: fmt.Sprintf("Supernode state is %s (expected ACTIVE)", supernodeInfo.CurrentState), + }) } }