Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions pkg/lumera/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ package lumera

import (
"context"
"fmt"

"github.com/LumeraProtocol/supernode/v2/pkg/lumera/modules/action"
"github.com/LumeraProtocol/supernode/v2/pkg/lumera/modules/action_msg"
"github.com/LumeraProtocol/supernode/v2/pkg/lumera/modules/auth"
"github.com/LumeraProtocol/supernode/v2/pkg/lumera/modules/bank"
"github.com/LumeraProtocol/supernode/v2/pkg/lumera/modules/node"
"github.com/LumeraProtocol/supernode/v2/pkg/lumera/modules/supernode"
"github.com/LumeraProtocol/supernode/v2/pkg/lumera/modules/tx"
Expand All @@ -16,6 +18,7 @@ type lumeraClient struct {
authMod auth.Module
actionMod action.Module
actionMsgMod action_msg.Module
bankMod bank.Module
supernodeMod supernode.Module
txMod tx.Module
nodeMod node.Module
Expand Down Expand Up @@ -53,12 +56,30 @@ func newClient(ctx context.Context, cfg *Config) (Client, error) {
return nil, err
}

bankModule, err := bank.NewModule(conn.GetConn())
if err != nil {
conn.Close()
return nil, err
}

nodeModule, err := node.NewModule(conn.GetConn(), cfg.keyring)
if err != nil {
conn.Close()
return nil, err
}

// Preflight: verify configured ChainID matches node's reported network
if nodeInfo, nerr := nodeModule.GetNodeInfo(ctx); nerr != nil {
conn.Close()
return nil, fmt.Errorf("failed to get node info for chain verification: %w", nerr)
} else if nodeInfo != nil && nodeInfo.DefaultNodeInfo != nil {
// Cosmos SDK exposes chain-id in DefaultNodeInfo.Network
if reported := nodeInfo.DefaultNodeInfo.Network; reported != "" && reported != cfg.ChainID {
conn.Close()
return nil, fmt.Errorf("chain ID mismatch: configured=%s node=%s", cfg.ChainID, reported)
}
}

actionMsgModule, err := action_msg.NewModule(
conn.GetConn(),
authModule, // For account info
Expand All @@ -77,6 +98,7 @@ func newClient(ctx context.Context, cfg *Config) (Client, error) {
authMod: authModule,
actionMod: actionModule,
actionMsgMod: actionMsgModule,
bankMod: bankModule,
supernodeMod: supernodeModule,
txMod: txModule,
nodeMod: nodeModule,
Expand All @@ -96,6 +118,10 @@ func (c *lumeraClient) ActionMsg() action_msg.Module {
return c.actionMsgMod
}

func (c *lumeraClient) Bank() bank.Module {
return c.bankMod
}

func (c *lumeraClient) SuperNode() supernode.Module {
return c.supernodeMod
}
Expand Down
2 changes: 2 additions & 0 deletions pkg/lumera/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/LumeraProtocol/supernode/v2/pkg/lumera/modules/action"
"github.com/LumeraProtocol/supernode/v2/pkg/lumera/modules/action_msg"
"github.com/LumeraProtocol/supernode/v2/pkg/lumera/modules/auth"
"github.com/LumeraProtocol/supernode/v2/pkg/lumera/modules/bank"
"github.com/LumeraProtocol/supernode/v2/pkg/lumera/modules/node"
"github.com/LumeraProtocol/supernode/v2/pkg/lumera/modules/supernode"
"github.com/LumeraProtocol/supernode/v2/pkg/lumera/modules/tx"
Expand All @@ -18,6 +19,7 @@ type Client interface {
Action() action.Module
ActionMsg() action_msg.Module
SuperNode() supernode.Module
Bank() bank.Module
Tx() tx.Module
Node() node.Module

Expand Down
15 changes: 15 additions & 0 deletions pkg/lumera/lumera_mock.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 30 additions & 0 deletions pkg/lumera/modules/bank/impl.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package bank

import (
"context"
"fmt"

banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
"google.golang.org/grpc"
)

type module struct {
client banktypes.QueryClient
}

func newModule(conn *grpc.ClientConn) (Module, error) {
if conn == nil {
return nil, fmt.Errorf("connection cannot be nil")
}
return &module{client: banktypes.NewQueryClient(conn)}, nil
}

func (m *module) Balance(ctx context.Context, address string, denom string) (*banktypes.QueryBalanceResponse, error) {
if address == "" {
return nil, fmt.Errorf("address cannot be empty")
}
if denom == "" {
return nil, fmt.Errorf("denom cannot be empty")
}
return m.client.Balance(ctx, &banktypes.QueryBalanceRequest{Address: address, Denom: denom})
}
18 changes: 18 additions & 0 deletions pkg/lumera/modules/bank/interface.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//go:generate mockgen -destination=bank_mock.go -package=bank -source=interface.go
package bank

import (
"context"

banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
"google.golang.org/grpc"
)

// Module provides access to Cosmos SDK bank queries.
type Module interface {
// Balance returns the balance for a specific denom at an address.
Balance(ctx context.Context, address string, denom string) (*banktypes.QueryBalanceResponse, error)
}

// NewModule constructs a bank Module backed by the given gRPC connection.
func NewModule(conn *grpc.ClientConn) (Module, error) { return newModule(conn) }
24 changes: 22 additions & 2 deletions pkg/testutil/lumera.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,26 @@ import (
"github.com/LumeraProtocol/supernode/v2/pkg/lumera/modules/action"
"github.com/LumeraProtocol/supernode/v2/pkg/lumera/modules/action_msg"
"github.com/LumeraProtocol/supernode/v2/pkg/lumera/modules/auth"
bankmod "github.com/LumeraProtocol/supernode/v2/pkg/lumera/modules/bank"
"github.com/LumeraProtocol/supernode/v2/pkg/lumera/modules/node"
"github.com/LumeraProtocol/supernode/v2/pkg/lumera/modules/supernode"
"github.com/LumeraProtocol/supernode/v2/pkg/lumera/modules/tx"

sdkmath "cosmossdk.io/math"
cmtservice "github.com/cosmos/cosmos-sdk/client/grpc/cmtservice"
"github.com/cosmos/cosmos-sdk/crypto/keyring"
sdktypes "github.com/cosmos/cosmos-sdk/types"
sdktx "github.com/cosmos/cosmos-sdk/types/tx"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
)

// MockLumeraClient implements the lumera.Client interface for testing purposes
type MockLumeraClient struct {
authMod *MockAuthModule
actionMod *MockActionModule
actionMsgMod *MockActionMsgModule
bankMod *MockBankModule
supernodeMod *MockSupernodeModule
txMod *MockTxModule
nodeMod *MockNodeModule
Expand All @@ -36,6 +40,7 @@ type MockLumeraClient struct {
func NewMockLumeraClient(kr keyring.Keyring, addresses []string) (lumera.Client, error) {
actionMod := &MockActionModule{}
actionMsgMod := &MockActionMsgModule{}
bankMod := &MockBankModule{}
supernodeMod := &MockSupernodeModule{addresses: addresses}
txMod := &MockTxModule{}
nodeMod := &MockNodeModule{}
Expand All @@ -44,6 +49,7 @@ func NewMockLumeraClient(kr keyring.Keyring, addresses []string) (lumera.Client,
authMod: &MockAuthModule{},
actionMod: actionMod,
actionMsgMod: actionMsgMod,
bankMod: bankMod,
supernodeMod: supernodeMod,
txMod: txMod,
nodeMod: nodeMod,
Expand All @@ -67,6 +73,11 @@ func (c *MockLumeraClient) ActionMsg() action_msg.Module {
return c.actionMsgMod
}

// Bank returns the Bank module client
func (c *MockLumeraClient) Bank() bankmod.Module {
return c.bankMod
}

// SuperNode returns the SuperNode module client
func (c *MockLumeraClient) SuperNode() supernode.Module {
return c.supernodeMod
Expand All @@ -87,6 +98,15 @@ func (c *MockLumeraClient) Close() error {
return nil
}

// MockBankModule implements the bank.Module interface for testing
type MockBankModule struct{}

// Balance returns a positive balance for any address/denom to pass checks by default
func (m *MockBankModule) Balance(ctx context.Context, address string, denom string) (*banktypes.QueryBalanceResponse, error) {
// Return >= 1 LUME in micro units to satisfy threshold checks
return &banktypes.QueryBalanceResponse{Balance: &sdktypes.Coin{Denom: denom, Amount: sdkmath.NewInt(1_000_000)}}, nil
}

// MockAuthModule implements the auth.Module interface for testing
type MockAuthModule struct{}

Expand Down Expand Up @@ -124,8 +144,8 @@ type MockActionMsgModule struct{}

// RequestAction mocks the behavior of requesting an action.
func (m *MockActionMsgModule) RequestAction(ctx context.Context, actionType, metadata, price, expirationTime string) (*sdktx.BroadcastTxResponse, error) {
// Mock implementation returns success with empty result
return &sdktx.BroadcastTxResponse{}, nil
// Mock implementation returns success with empty result
return &sdktx.BroadcastTxResponse{}, nil
}

// FinalizeCascadeAction implements the required method from action_msg.Module interface
Expand Down
47 changes: 1 addition & 46 deletions sdk/task/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,16 @@ package task
import (
"context"
"encoding/base64"
"errors"
"fmt"
"os"
"path/filepath"
"strings"

"github.com/LumeraProtocol/supernode/v2/sdk/adapters/lumera"
snsvc "github.com/LumeraProtocol/supernode/v2/sdk/adapters/supernodeservice"
"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)
Expand Down Expand Up @@ -105,47 +100,7 @@ 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
}

// Request peer info and P2P metrics to assess connectivity
ctxWithMetrics := snsvc.WithIncludeP2PMetrics(ctx)
status, err := client.GetSupernodeStatus(ctxWithMetrics)
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
}
// (Removed) Peers connectivity preflight is now enforced during discovery in isServing.

func (m *ManagerImpl) validateDownloadAction(ctx context.Context, actionID string) (lumera.Action, error) {
action, err := m.lumeraClient.GetAction(ctx, actionID)
Expand Down
12 changes: 2 additions & 10 deletions sdk/task/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,11 +107,7 @@ 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
}
// Peer connectivity is now validated during discovery health checks

taskID := uuid.New().String()[:8]

Expand Down Expand Up @@ -280,11 +276,7 @@ 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
}
// Peer connectivity is now validated during discovery health checks

// Ensure the output path includes the correct filename
finalOutputPath := path.Join(outputDir, action.ID, metadata.FileName)
Expand Down
Loading
Loading