From ac1344373f407c5ad6fdc34b906e1de5e121aeda Mon Sep 17 00:00:00 2001 From: Matee Ullah Malik Date: Wed, 20 Aug 2025 23:47:17 +0500 Subject: [PATCH] feat: add peer connectivity validation in SDK before task creation --- sdk/task/helpers.go | 44 ++++++++++++++++++++++++++++++++++++++++++++ sdk/task/manager.go | 12 ++++++++++++ 2 files changed, 56 insertions(+) diff --git a/sdk/task/helpers.go b/sdk/task/helpers.go index 307b8d03..aacbac75 100644 --- a/sdk/task/helpers.go +++ b/sdk/task/helpers.go @@ -3,16 +3,20 @@ package task import ( "context" "encoding/base64" + "errors" "fmt" "os" "path/filepath" "strings" "github.com/LumeraProtocol/supernode/v2/sdk/adapters/lumera" + "github.com/LumeraProtocol/supernode/v2/sdk/net" ) const maxFileSize = 1 * 1024 * 1024 * 1024 // 1GB limit +var ErrNoPeersConnected = errors.New("no P2P peers connected on available supernodes") + // ValidateFileSize checks if a file size is within the allowed 1GB limit func ValidateFileSize(filePath string) error { fileInfo, err := os.Stat(filePath) @@ -100,6 +104,46 @@ func (m *ManagerImpl) validateSignature(ctx context.Context, action lumera.Actio return nil } +// checkSupernodesPeerConnectivity verifies that at least one supernode has P2P peers connected +func (m *ManagerImpl) checkSupernodesPeerConnectivity(ctx context.Context, blockHeight int64) error { + // Fetch supernodes for the action's block height + supernodes, err := m.lumeraClient.GetSupernodes(ctx, blockHeight) + if err != nil { + return fmt.Errorf("failed to get supernodes: %w", err) + } + + if len(supernodes) == 0 { + return fmt.Errorf("no supernodes available for block height %d", blockHeight) + } + + // Check each supernode for peer connectivity + factoryCfg := net.FactoryConfig{ + LocalCosmosAddress: m.config.Account.LocalCosmosAddress, + PeerType: m.config.Account.PeerType, + } + clientFactory := net.NewClientFactory(ctx, m.logger, m.keyring, m.lumeraClient, factoryCfg) + + for _, sn := range supernodes { + client, err := clientFactory.CreateClient(ctx, sn) + if err != nil { + continue // Skip this supernode if we can't connect + } + + status, err := client.GetSupernodeStatus(ctx) + client.Close(ctx) + if err != nil { + continue // Skip this supernode if we can't get status + } + + // Check if this supernode has peers + if status.Network.PeersCount > 1 { + return nil // Found at least one supernode with peers + } + } + + return ErrNoPeersConnected +} + func (m *ManagerImpl) validateDownloadAction(ctx context.Context, actionID string) (lumera.Action, error) { action, err := m.lumeraClient.GetAction(ctx, actionID) if err != nil { diff --git a/sdk/task/manager.go b/sdk/task/manager.go index 751b90ae..2f8c4aa6 100644 --- a/sdk/task/manager.go +++ b/sdk/task/manager.go @@ -108,6 +108,12 @@ func (m *ManagerImpl) CreateCascadeTask(ctx context.Context, filePath string, ac return "", err } + // Check peer connectivity before creating task + if err := m.checkSupernodesPeerConnectivity(taskCtx, action.Height); err != nil { + cancel() // Clean up if peer check fails + return "", err + } + taskID := uuid.New().String()[:8] m.logger.Debug(taskCtx, "Generated task ID", "taskID", taskID) @@ -275,6 +281,12 @@ func (m *ManagerImpl) CreateDownloadTask(ctx context.Context, actionID string, o return "", fmt.Errorf("no filename found in cascade metadata") } + // Check peer connectivity before creating task + if err := m.checkSupernodesPeerConnectivity(taskCtx, action.Height); err != nil { + cancel() // Clean up if peer check fails + return "", err + } + // Ensure the output path includes the correct filename finalOutputPath := path.Join(outputDir, action.ID, metadata.FileName)