From 882e4c487ecb0a4f116c0a2bc6b847bc23707492 Mon Sep 17 00:00:00 2001 From: Matee Ullah Malik Date: Mon, 28 Jul 2025 20:19:21 +0500 Subject: [PATCH] Update README.md and sdk/README.md --- README.md | 179 +++++++----- sdk/README.md | 770 +++++++++++++++++++------------------------------- 2 files changed, 401 insertions(+), 548 deletions(-) diff --git a/README.md b/README.md index 17a551dd..918a1b15 100644 --- a/README.md +++ b/README.md @@ -10,33 +10,49 @@ The supernode exposes two main gRPC services: Provides system status and monitoring information. -**Service Definition:** ```protobuf service SupernodeService { rpc GetStatus(StatusRequest) returns (StatusResponse); } -``` -**StatusResponse includes:** -- `CPU` - CPU usage and remaining capacity -- `Memory` - Total, used, available memory and usage percentage -- `ServiceTasks` - Task information for each active service -- `available_services` - List of available service names +message StatusRequest {} + +message StatusResponse { + message CPU { + string usage = 1; + string remaining = 2; + } + + message Memory { + uint64 total = 1; + uint64 used = 2; + uint64 available = 3; + double used_perc = 4; + } + + message ServiceTasks { + string service_name = 1; + repeated string task_ids = 2; + int32 task_count = 3; + } + + CPU cpu = 1; + Memory memory = 2; + repeated ServiceTasks services = 3; + repeated string available_services = 4; +} +``` ### CascadeService Handles cascade operations for data storage and retrieval. -**Service Definition:** ```protobuf service CascadeService { rpc Register (stream RegisterRequest) returns (stream RegisterResponse); rpc Download (DownloadRequest) returns (stream DownloadResponse); } -``` -**Register Operation:** -```protobuf message RegisterRequest { oneof request_type { DataChunk chunk = 1; @@ -58,10 +74,7 @@ message RegisterResponse { string message = 2; string tx_hash = 3; } -``` -**Download Operation:** -```protobuf message DownloadRequest { string action_id = 1; string signature = 2; @@ -78,13 +91,23 @@ message DownloadEvent { SupernodeEventType event_type = 1; string message = 2; } -``` -**Event Types:** -- `ACTION_RETRIEVED`, `ACTION_FEE_VERIFIED`, `TOP_SUPERNODE_CHECK_PASSED` -- `METADATA_DECODED`, `DATA_HASH_VERIFIED`, `INPUT_ENCODED` -- `SIGNATURE_VERIFIED`, `RQID_GENERATED`, `RQID_VERIFIED` -- `ARTEFACTS_STORED`, `ACTION_FINALIZED`, `ARTEFACTS_DOWNLOADED` +enum SupernodeEventType { + UNKNOWN = 0; + ACTION_RETRIEVED = 1; + ACTION_FEE_VERIFIED = 2; + TOP_SUPERNODE_CHECK_PASSED = 3; + METADATA_DECODED = 4; + DATA_HASH_VERIFIED = 5; + INPUT_ENCODED = 6; + SIGNATURE_VERIFIED = 7; + RQID_GENERATED = 8; + RQID_VERIFIED = 9; + ARTEFACTS_STORED = 10; + ACTION_FINALIZED = 11; + ARTEFACTS_DOWNLOADED = 12; +} +``` ## CLI Commands @@ -97,14 +120,23 @@ Initialize a new supernode with interactive setup. supernode init # Interactive setup supernode init --force # Override existing installation supernode init -y # Use defaults, skip prompts -supernode init --keyring-backend test # Specify keyring backend with -y +supernode init --keyring-backend os --key-name mykey # Specify keyring and key +supernode init --recover --mnemonic "word1 word2..." # Recover from mnemonic +supernode init --supernode-addr 0.0.0.0 --supernode-port 4444 # Set network +supernode init --lumera-grpc localhost:9090 --chain-id lumera-mainnet-1 # Set chain ``` -**Features:** -- Creates `~/.supernode/config.yml` -- Sets up keyring (test, file, or os backend) -- Key recovery from mnemonic or generates new key -- Network configuration (gRPC address, ports, chain ID) +**Available flags:** +- `--force` - Override existing installation +- `-y`, `--yes` - Skip interactive prompts, use defaults +- `--keyring-backend` - Keyring backend (`os`, `file`, `test`) +- `--key-name` - Name of key to create or recover +- `--recover` - Recover existing key from mnemonic +- `--mnemonic` - Mnemonic phrase for recovery (use with --recover) +- `--supernode-addr` - IP address for supernode service +- `--supernode-port` - Port for supernode service +- `--lumera-grpc` - Lumera gRPC address (host:port) +- `--chain-id` - Lumera blockchain chain ID #### `supernode start` Start the supernode service. @@ -114,12 +146,6 @@ supernode start # Use default config directory supernode start -d /path/to/dir # Use custom base directory ``` -**Initializes:** -- P2P networking service -- gRPC server (default port 4444) -- Cascade service for data operations -- Connection to Lumera validator node - #### `supernode version` Display version information. @@ -136,20 +162,14 @@ List all keys in the keyring with addresses. supernode keys list ``` -**Output format:** -``` -NAME ADDRESS ----- ------- -mykey (selected) lumera15t2e8gjgmuqtj4jzjqfkf3tf5l8vqw69hmrzmr -backup lumera1abc...xyz -``` - -#### `supernode keys add ` -Create a new key (generates mnemonic). - -#### `supernode keys recover ` +#### `supernode keys recover [name]` Recover key from existing mnemonic phrase. +```bash +supernode keys recover mykey +supernode keys recover mykey --mnemonic "word1 word2..." +``` + ### Configuration Management #### `supernode config list` @@ -159,10 +179,6 @@ Display current configuration parameters. supernode config list ``` -**Shows:** -- Key Name, Address, Supernode Address/Port -- Keyring Backend, Lumera gRPC Address, Chain ID - #### `supernode config update` Interactive configuration parameter updates. @@ -170,12 +186,6 @@ Interactive configuration parameter updates. supernode config update ``` -**Updatable parameters:** -- Supernode IP Address and Port -- Lumera gRPC Address and Chain ID -- Key Name (with key selection from keyring) -- Keyring Backend (with key migration) - ### Global Flags #### `--basedir, -d` @@ -186,19 +196,48 @@ supernode start -d /custom/path supernode config list -d /custom/path ``` -## Configuration Parameters - -| Parameter | Description | Required | Default | Example | Notes | -|-----------|-------------|----------|---------|---------|--------| -| `supernode.key_name` | Name of the key for signing transactions | **Yes** | - | `"mykey"` | Must match the name used with `supernode keys add` | -| `supernode.identity` | Lumera address for this supernode | **Yes** | - | `"lumera15t2e8gjgmuqtj4jzjqfkf3tf5l8vqw69hmrzmr"` | Obtained after creating/recovering a key | -| `supernode.ip_address` | IP address to bind the supernode service | **Yes** | - | `"0.0.0.0"` | Use `"0.0.0.0"` to listen on all interfaces | -| `supernode.port` | Port for the supernode service | **Yes** | - | `4444` | Choose an available port | -| `keyring.backend` | Key storage backend type | **Yes** | - | `"test"` | `"test"` for development, `"file"` for encrypted storage, `"os"` for OS keyring | -| `keyring.dir` | Directory to store keyring files | No | `"keys"` | `"keys"` | Relative paths are appended to basedir, absolute paths used as-is | -| `p2p.listen_address` | IP address for P2P networking | **Yes** | - | `"0.0.0.0"` | Use `"0.0.0.0"` to listen on all interfaces | -| `p2p.port` | P2P communication port | **Yes** | - | `4445` | **Do not change this default value** | -| `p2p.data_dir` | Directory for P2P data storage | No | `"data/p2p"` | `"data/p2p"` | Relative paths are appended to basedir, absolute paths used as-is | -| `lumera.grpc_addr` | gRPC endpoint of Lumera validator node | **Yes** | - | `"localhost:9090"` | Must be accessible from supernode | -| `lumera.chain_id` | Lumera blockchain chain identifier | **Yes** | - | `"lumera"` | Must match the actual chain ID | -| `raptorq.files_dir` | Directory to store RaptorQ files | No | `"raptorq_files"` | `"raptorq_files"` | Relative paths are appended to basedir, absolute paths used as-is | +## Configuration + +The configuration file is located at `~/.supernode/config.yml` and contains the following sections: + +### Supernode Configuration +```yaml +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 + port: 4444 # Port for the supernode service +``` + +### Keyring Configuration +```yaml +keyring: + backend: "os" # Key storage backend (os, file, test) + dir: "keys" # Directory to store keyring files (relative to basedir) +``` + +### 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 +``` + +### Lumera Network Configuration +```yaml +lumera: + grpc_addr: "localhost:9090" # gRPC endpoint of Lumera validator node + chain_id: "lumera-mainnet-1" # Lumera blockchain chain identifier +``` + +### RaptorQ Configuration +```yaml +raptorq: + files_dir: "raptorq_files" # Directory to store RaptorQ files +``` + +**Notes:** +- Relative paths are resolved relative to the base directory (`~/.supernode` by default) +- Absolute paths are used as-is +- The P2P port should not be changed from the default value diff --git a/sdk/README.md b/sdk/README.md index 14d777bb..81ac0edf 100644 --- a/sdk/README.md +++ b/sdk/README.md @@ -1,604 +1,418 @@ -# Lumera Supernode SDK +# Supernode SDK -## Overview - -The Lumera Supernode SDK is a comprehensive toolkit for building applications that interact with the Lumera blockchain network. It provides a set of components and interfaces that simplify the process of integrating with Lumera supernodes, managing actions, and processing data through the Lumera Protocol. - -This SDK facilitates: -- Creating and managing cascade operations -- Securely communicating with the Lumera blockchain -- Managing asynchronous tasks with progress tracking -- Subscribing to events for real-time updates -- Keyring management for secure key storage +The Lumera Supernode SDK is a comprehensive toolkit for interacting with the Lumera Protocol's supernode network to perform cascade operations ## Table of Contents -- [Installation](#installation) -- [Getting Started](#getting-started) - [Configuration](#configuration) -- [Key Management](#key-management) -- [Core Components](#core-components) -- [Working with Tasks](#working-with-tasks) +- [Client Initialization](#client-initialization) +- [Action Client Methods](#action-client-methods) + - [StartCascade](#startcascade) + - [DownloadCascade](#downloadcascade) + - [GetTask](#gettask) + - [DeleteTask](#deletetask) + - [GetSupernodeStatus](#getsupernodestatus) + - [SubscribeToEvents](#subscribetoevents) + - [SubscribeToAllEvents](#subscribetoallevents) - [Event System](#event-system) -- [Command Line Interface](#command-line-interface) -- [Advanced Features](#advanced-features) -- [Architecture](#architecture) -- [API Reference](#api-reference) -- [Troubleshooting](#troubleshooting) - -## Installation - -### Prerequisites - -- Go version 1.18 or higher -- Access to a Lumera network node - -### Installing the SDK - -```bash -go get github.com/LumeraProtocol/supernode/sdk -``` +- [Error Handling](#error-handling) -## Getting Started +## Configuration -### Creating a Client +The SDK uses a structured configuration system with two main components: account settings and blockchain connection details. -The first step is to create a client instance that will be used to interact with the Lumera blockchain: +### Configuration Structure ```go -package main - import ( - "context" - "fmt" - "time" - - "github.com/LumeraProtocol/supernode/sdk/action" "github.com/LumeraProtocol/supernode/sdk/config" - "github.com/LumeraProtocol/supernode/sdk/log" "github.com/cosmos/cosmos-sdk/crypto/keyring" + "github.com/LumeraProtocol/lumera/x/lumeraid/securekeyx" ) -func main() { - ctx := context.Background() - - // Initialize keyring - kr, err := keyring.New("lumera", keyring.BackendTest, "/path/to/keyring", nil) - if err != nil { - panic(fmt.Errorf("failed to initialize keyring: %w", err)) - } - - // Create configuration - cfg, err := config.New( - config.WithLocalCosmosAddress("lumera1qv3..."), - config.WithGRPCAddr("127.0.0.1:9090"), - config.WithChainID("lumera-testnet"), - config.WithTimeout(10 * time.Second), - ) - if err != nil { - panic(fmt.Errorf("failed to create config: %w", err)) - } - - // Create logger - logger := log.NewNoopLogger() // Replace with your logger implementation - - // Create action client - client, err := action.NewClient(ctx, *cfg, logger, kr) - if err != nil { - panic(fmt.Errorf("failed to create client: %w", err)) - } - - // Now you can use the client to interact with the Lumera blockchain -} -``` - -### Performing a Cascade Operation - -```go -func performCascade(ctx context.Context, client action.Client, data []byte, actionID string) { - // Start a cascade operation - taskID, err := client.StartCascade(ctx, data, actionID) - if err != nil { - panic(fmt.Errorf("failed to start cascade: %w", err)) - } - - fmt.Printf("Cascade operation started with task ID: %s\n", taskID) - - // Subscribe to events to track progress - client.SubscribeToAllEvents(ctx, func(ctx context.Context, e event.Event) { - if e.TaskID == taskID { - fmt.Printf("Event: %s, Status: %s\n", e.Type, e.Data["message"]) - - // Check if the task is completed - if e.Type == event.TaskCompleted { - fmt.Println("Cascade operation completed successfully") - } else if e.Type == event.TaskFailed { - fmt.Printf("Cascade operation failed: %s\n", e.Data["error"]) - } - } - }) +// Account configuration +accountConfig := config.AccountConfig{ + LocalCosmosAddress: "lumera1abc...", // Your cosmos address + KeyName: "my-key", // Key name in keyring + Keyring: keyring, // Cosmos SDK keyring instance + // PeerType is optional - defaults to SIMPLENODE when omitted (recommended for most users) } -``` - -### Working with Task Events -The SDK provides a robust event system for tracking task progress in real-time: - -```go -func trackTaskEvents(ctx context.Context, client action.Client, taskID string) { - // Subscribe to specific event types - err := client.SubscribeToEvents(ctx, event.TaskProgressActionVerified, func(ctx context.Context, e event.Event) { - fmt.Println("Action verification completed!") - }) - if err != nil { - panic(fmt.Errorf("failed to subscribe to events: %w", err)) - } - - err = client.SubscribeToEvents(ctx, event.TaskProgressSupernodesFound, func(ctx context.Context, e event.Event) { - // Extract data from the event - if count, ok := e.Data["count"].(int); ok { - fmt.Printf("Found %d supernodes for processing\n", count) - } - }) - if err != nil { - panic(fmt.Errorf("failed to subscribe to events: %w", err)) - } - - // Subscribe to transaction hash events - err = client.SubscribeToEvents(ctx, event.TxhasReceived, func(ctx context.Context, e event.Event) { - if txHash, ok := e.Data["txhash"].(string); ok { - fmt.Printf("Transaction hash received: %s\n", txHash) - - // You can now use this hash to query the blockchain - fmt.Printf("View transaction: https://explorer.lumera.org/tx/%s\n", txHash) - } - }) - if err != nil { - panic(fmt.Errorf("failed to subscribe to events: %w", err)) - } - - // Handle completion or failure - err = client.SubscribeToEvents(ctx, event.TaskCompleted, func(ctx context.Context, e event.Event) { - fmt.Printf("Task %s completed successfully at %s\n", e.TaskID, e.Timestamp.Format(time.RFC3339)) - }) - if err != nil { - panic(fmt.Errorf("failed to subscribe to events: %w", err)) - } - - err = client.SubscribeToEvents(ctx, event.TaskFailed, func(ctx context.Context, e event.Event) { - if errMsg, ok := e.Data["error"].(string); ok { - fmt.Printf("Task failed: %s\n", errMsg) - } - }) - if err != nil { - panic(fmt.Errorf("failed to subscribe to events: %w", err)) - } +// Blockchain connection configuration +lumeraConfig := config.LumeraConfig{ + GRPCAddr: "localhost:9090", // Lumera blockchain GRPC endpoint + ChainID: "testing", // Chain identifier } -``` - -### Retrieving Task Information -You can retrieve information about a task at any time: +// Combine configurations +sdkConfig := config.NewConfig(accountConfig, lumeraConfig) -```go -func getTaskInfo(ctx context.Context, client action.Client, taskID string) { - // Get task information - taskEntry, found := client.GetTask(ctx, taskID) - if !found { - fmt.Printf("Task %s not found\n", taskID) - return - } - - // Display task information - fmt.Printf("Task ID: %s\n", taskEntry.TaskID) - fmt.Printf("Task Type: %s\n", taskEntry.TaskType) - fmt.Printf("Status: %s\n", taskEntry.Status) - fmt.Printf("Created: %s\n", taskEntry.CreatedAt.Format(time.RFC3339)) - fmt.Printf("Last Updated: %s\n", taskEntry.LastUpdatedAt.Format(time.RFC3339)) - - // Check if the task has an error - if taskEntry.Error != nil { - fmt.Printf("Error: %s\n", taskEntry.Error) - } - - // Display task events history - fmt.Println("\nEvent History:") - for i, e := range taskEntry.Events { - fmt.Printf("%d. [%s] %s\n", i+1, e.Timestamp.Format(time.RFC3339), e.Type) - - // Display event data if any - if len(e.Data) > 0 { - fmt.Println(" Data:") - for k, v := range e.Data { - fmt.Printf(" - %s: %v\n", k, v) - } - } - } - - // Calculate current progress - if len(taskEntry.Events) > 0 { - latestEvent := taskEntry.Events[len(taskEntry.Events)-1].Type - current, total := event.GetTaskProgress(latestEvent) - fmt.Printf("\nProgress: %d/%d (%.1f%%)\n", current, total, float64(current)/float64(total)*100) - } +// Validate configuration (recommended) +if err := sdkConfig.Validate(); err != nil { + // Handle validation error } ``` -## Configuration - -The SDK uses a structured configuration system that allows you to customize various aspects of its behavior. +### Required Fields -### Configuration Options +**AccountConfig:** +- `LocalCosmosAddress`: Your Lumera cosmos address (e.g., "lumera1abc...") +- `KeyName`: Name of the key in your keyring +- `Keyring`: Initialized Cosmos SDK keyring containing your keys +- `PeerType`: Peer type from securekeyx (optional, defaults to SIMPLENODE when left empty, which is suitable for most use cases) -The main configuration components are: +**LumeraConfig:** +- `GRPCAddr`: GRPC endpoint of the Lumera blockchain +- `ChainID`: Chain identifier for the network you're connecting to -- **AccountConfig**: Local Cosmos address settings -- **LumeraConfig**: Chain-specific settings (GRPC address, chain ID, timeout) +### Keyring Setup -### Creating a Configuration +The SDK requires a Cosmos SDK keyring. Here are common initialization patterns: ```go -cfg, err := config.New( - config.WithLocalCosmosAddress("lumera1qv3..."), // Your account address - config.WithGRPCAddr("127.0.0.1:9090"), // Address of the Lumera gRPC endpoint - config.WithChainID("lumera-testnet"), // Chain ID of the network - config.WithTimeout(10 * time.Second), // Timeout for network operations -) -``` - -### Default Values - -- Default local Cosmos address: "lumera1qv3" -- Default chain ID: "lumera-testnet" -- Default gRPC address: "127.0.0.1:9090" -- Default timeout: 10 seconds - -## Key Management +import "github.com/cosmos/cosmos-sdk/crypto/keyring" -The SDK provides functionality for managing keys securely using the Cosmos SDK keyring. +// For testing (stores keys in memory) +kr, err := keyring.New("app-name", "test", "/tmp", nil) -### Working with the CLI for Key Management +// For production (stores keys in OS keyring) +kr, err := keyring.New("app-name", "os", "", nil) -The SDK includes CLI commands for key management: +// For file-based storage +kr, err := keyring.New("app-name", "file", "/path/to/keys", nil) +``` -#### Adding a New Key +### Example Configuration -```bash -supernode keys add mykey +```go +config := config.NewConfig( + config.AccountConfig{ + LocalCosmosAddress: "lumera1...", + KeyName: "my-key", + Keyring: keyring, + }, + config.LumeraConfig{ + GRPCAddr: "localhost:9090", + ChainID: "testing", + }, +) ``` -#### Recovering a Key from Mnemonic +## Client Initialization -```bash -supernode keys recover mykey -``` +The SDK requires a Cosmos SDK keyring containing your cryptographic keys for signing transactions and authenticating with supernodes. You must provide an initialized keyring instance along with the name of the key to use. -#### Listing Keys +```go +import ( + "context" + "github.com/LumeraProtocol/supernode/sdk/action" + "github.com/LumeraProtocol/supernode/sdk/config" + "github.com/cosmos/cosmos-sdk/crypto/keyring" +) -```bash -supernode keys list -``` +// keyring is a cosmos-sdk keyring.Keyring that contains your keys +// It should be initialized with your preferred backend (test, file, os, etc.) -### Programmatic Key Management -```go -// Initialize keyring -kr, err := keyring.New("lumera", keyring.BackendTest, "/path/to/keyring", nil) -if err != nil { - panic(fmt.Errorf("failed to initialize keyring: %w", err)) +// Set up configuration +accountConfig := config.AccountConfig{ + LocalCosmosAddress: "lumera1...", // Your cosmos address + KeyName: "your-key", // Name of the key in your keyring + Keyring: keyring, // Cosmos SDK keyring instance } -// Get a key by name -key, err := kr.Key("mykey") -if err != nil { - panic(fmt.Errorf("key not found: %w", err)) +lumeraConfig := config.LumeraConfig{ + GRPCAddr: "localhost:9090", // Lumera blockchain GRPC address + ChainID: "testing", // Chain ID } -// Get address from key -address, err := key.GetAddress() +sdkConfig := config.NewConfig(accountConfig, lumeraConfig) + +// Initialize the action client +client, err := action.NewClient(context.Background(), sdkConfig, logger) if err != nil { - panic(fmt.Errorf("failed to get address: %w", err)) + // Handle error } - -fmt.Printf("Address: %s\n", address.String()) ``` -## Core Components +## Action Client Methods -### Action Client +### StartCascade -The `action.Client` interface provides methods for performing actions on the Lumera blockchain: +Initiates a cascade operation to upload and process a file through the supernode network. ```go -type Client interface { - StartCascade(ctx context.Context, data []byte, actionID string) (string, error) - DeleteTask(ctx context.Context, taskID string) error - GetTask(ctx context.Context, taskID string) (*task.TaskEntry, bool) - SubscribeToEvents(ctx context.Context, eventType event.EventType, handler event.Handler) error - SubscribeToAllEvents(ctx context.Context, handler event.Handler) error +taskID, err := client.StartCascade( + ctx, + "/path/to/file.txt", // File path to upload + "action-id-123", // Action ID from blockchain transaction + "base64-signature", // Base64-encoded signature of file's blake3 hash +) +if err != nil { + // Handle error } +// taskID can be used to track the operation progress ``` -### Lumera Client +**Parameters:** +- `ctx context.Context`: Context for the operation +- `filePath string`: Path to the file to be processed +- `actionID string`: ID of the action registered on the blockchain +- `signature string`: Base64-encoded signature of the file's blake3 hash by the action creator -The Lumera adapter provides an interface to interact with the Lumera blockchain: +**Signature Creation Steps:** +1. Read the entire file content into a byte array +2. Compute the Blake3 hash of the file data using a 32-byte hasher +3. Sign the Blake3 hash bytes using your keyring and key name +4. Base64 encode the signed hash to create the final signature ```go -type Client interface { - GetAction(ctx context.Context, actionID string) (Action, error) - GetSupernodes(ctx context.Context, height int64) ([]Supernode, error) -} -``` - -### Task Manager - -The task manager handles the creation and management of tasks: +import ( + "os" + "io" + "bytes" + "encoding/base64" + "lukechampine.com/blake3" + "github.com/LumeraProtocol/supernode/pkg/keyring" +) -```go -type Manager interface { - CreateCascadeTask(ctx context.Context, data []byte, actionID string) (string, error) - GetTask(ctx context.Context, taskID string) (*TaskEntry, bool) - DeleteTask(ctx context.Context, taskID string) error - SubscribeToEvents(ctx context.Context, eventType event.EventType, handler event.Handler) - SubscribeToAllEvents(ctx context.Context, handler event.Handler) +// 1. Read the file content +fileData, err := os.ReadFile("/path/to/file.txt") +if err != nil { + // Handle error } -``` -## Working with Tasks - -Tasks are the primary way to perform operations that may take time to complete, such as cascade operations. - -### Creating a Task +// 2. Compute Blake3 hash of the file data +hasher := blake3.New(32, nil) +if _, err := io.Copy(hasher, bytes.NewReader(fileData)); err != nil { + // Handle error +} +hash := hasher.Sum(nil) -```go -// Create a cascade task -taskID, err := client.StartCascade(ctx, data, actionID) +// 3. Sign the hash using your keyring +signedHash, err := keyring.SignBytes(keyring, keyName, hash) if err != nil { - panic(fmt.Errorf("failed to create task: %w", err)) + // Handle error } -``` -### Checking Task Status +// 4. Base64 encode the signature - this is what you pass to StartCascade +signature := base64.StdEncoding.EncodeToString(signedHash) +``` -```go -taskEntry, found := client.GetTask(ctx, taskID) -if !found { - fmt.Println("Task not found") - return -} +**Returns:** +- `string`: Task ID for tracking the operation +- `error`: Error if the operation fails -fmt.Printf("Task status: %s\n", taskEntry.Status) -``` +### DownloadCascade -### Deleting a Task +Downloads a cascade file from the supernode network using an action ID. ```go -err := client.DeleteTask(ctx, taskID) +taskID, err := client.DownloadCascade( + ctx, + "action-id-123", // Action ID of the cascade to download + "/output/directory", // Directory where the file will be saved + "base64-signature", // Download signature +) if err != nil { - panic(fmt.Errorf("failed to delete task: %w", err)) + // Handle error } +// taskID can be used to track the download progress ``` -## Event System - -The SDK provides an event system that allows you to track the progress of operations in real-time. +**Parameters:** +- `ctx context.Context`: Context for the operation +- `actionID string`: ID of the action to download +- `outputDir string`: Directory where the downloaded file will be saved +- `signature string`: Base64-encoded signature for download authorization -### Event Types - -Event types are defined in the `event` package: +**Signature Creation for Download:** +The download signature is created by combining the action ID with the creator's address, signing it, and base64 encoding the result. ```go -const ( - TaskStarted EventType = "task.started" - TaskProgressActionVerified EventType = "task.progress.action_verified" - TaskProgressActionVerificationFailed EventType = "task.progress.action_verification_failed" - TaskProgressSupernodesFound EventType = "task.progress.supernode_found" - TaskProgressSupernodesUnavailable EventType = "task.progress.supernodes_unavailable" - // ... many other event types - TaskCompleted EventType = "task.completed" - TxhasReceived EventType = "txhash.received" - TaskFailed EventType = "task.failed" -) -``` - -### Subscribing to Events +// Create signature data: actionID.creatorAddress +signatureData := fmt.Sprintf("%s.%s", actionID, creatorAddress) -```go -// Subscribe to a specific event type -client.SubscribeToEvents(ctx, event.TaskCompleted, func(ctx context.Context, e event.Event) { - fmt.Printf("Task %s completed\n", e.TaskID) -}) +// Sign the signature data +signedSignature, err := keyring.SignBytes(keyring, keyName, []byte(signatureData)) +if err != nil { + // Handle error +} -// Subscribe to all events -client.SubscribeToAllEvents(ctx, func(ctx context.Context, e event.Event) { - fmt.Printf("Event: %s, Task: %s\n", e.Type, e.TaskID) -}) +// Base64 encode the signature +signature := base64.StdEncoding.EncodeToString(signedSignature) ``` -## Command Line Interface +**Returns:** +- `string`: Task ID for tracking the download operation +- `error`: Error if the operation fails -The SDK includes a CLI for various operations. +### GetTask -### Starting the Supernode +Retrieves information about a specific task by its ID. -```bash -supernode start +```go +taskEntry, found := client.GetTask(ctx, "task-id-123") +if !found { + // Task not found +} +// Use taskEntry to access task information ``` -This command starts the supernode service with the configuration defined in config.yaml. - -### Key Management Commands +**Parameters:** +- `ctx context.Context`: Context for the operation +- `taskID string`: ID of the task to retrieve -See the [Key Management](#key-management) section for details. +**Returns:** +- `*task.TaskEntry`: Task information including status, events, and metadata +- `bool`: Whether the task was found -## Advanced Features +### DeleteTask -### Secure Communication with Supernodes - -The SDK provides secure communication with supernodes using ALTS (Application Layer Transport Security): +Removes a task from the task cache by its ID. ```go -// Create client factory -factory := net.NewClientFactory(ctx, logger, kr, net.FactoryConfig{ - LocalCosmosAddress: cfg.Account.LocalCosmosAddress, -}) - -// Create client for a specific supernode -client, err := factory.CreateClient(ctx, supernode) +err := client.DeleteTask(ctx, "task-id-123") if err != nil { - panic(fmt.Errorf("failed to create client: %w", err)) + // Handle error (task not found or deletion failed) } -defer client.Close(ctx) - -// Check supernode health -resp, err := client.HealthCheck(ctx) -if err != nil { - panic(fmt.Errorf("health check failed: %w", err)) -} - -fmt.Printf("Supernode health: %s\n", resp.Status) ``` -### RaptorQ Encoding +**Parameters:** +- `ctx context.Context`: Context for the operation +- `taskID string`: ID of the task to delete -The SDK uses RaptorQ for efficient data encoding and storage: +**Returns:** +- `error`: Error if the task doesn't exist or deletion fails -```go -// This is handled internally by the cascade operation -taskID, err := client.StartCascade(ctx, data, actionID) -``` - -## Architecture - -The Lumera Supernode SDK follows a modular architecture to provide flexibility and maintainability. - -### Core Components - -1. **Action Client**: Entry point for application developers -2. **Task Manager**: Handles task creation and lifecycle -3. **Event Bus**: Provides real-time updates on task progress -4. **Network Layer**: Handles communication with supernodes -5. **Lumera Adapter**: Interfaces with the Lumera blockchain -6. **Configuration**: Manages SDK settings - -### Data Flow - -1. Application creates a cascade operation via the Action Client -2. Task Manager creates and schedules the task -3. The task fetches the action from the blockchain via the Lumera Adapter -4. The task finds suitable supernodes and registers the data -5. Events are emitted throughout the process for tracking progress -6. The task completes or fails, and a final event is emitted +### GetSupernodeStatus -## API Reference - -### action.Client +Retrieves the current status and resource information of a specific supernode. ```go -type Client interface { - // StartCascade initiates a cascade operation - StartCascade(ctx context.Context, data []byte, actionID string) (string, error) - - // DeleteTask removes a task by its ID - DeleteTask(ctx context.Context, taskID string) error - - // GetTask retrieves a task by its ID - GetTask(ctx context.Context, taskID string) (*task.TaskEntry, bool) - - // SubscribeToEvents registers a handler for specific event types - SubscribeToEvents(ctx context.Context, eventType event.EventType, handler event.Handler) error - - // SubscribeToAllEvents registers a handler for all events - SubscribeToAllEvents(ctx context.Context, handler event.Handler) error +status, err := client.GetSupernodeStatus(ctx, "lumera1abc...") +if err != nil { + // Handle error } +// Use status to access CPU, memory, and service information ``` -### task.Manager +**Parameters:** +- `ctx context.Context`: Context for the operation +- `supernodeAddress string`: Cosmos address of the supernode -```go -type Manager interface { - // CreateCascadeTask creates a cascade task - CreateCascadeTask(ctx context.Context, data []byte, actionID string) (string, error) - - // GetTask retrieves a task by its ID - GetTask(ctx context.Context, taskID string) (*TaskEntry, bool) - - // DeleteTask removes a task by its ID - DeleteTask(ctx context.Context, taskID string) error - - // SubscribeToEvents registers a handler for specific event types - SubscribeToEvents(ctx context.Context, eventType event.EventType, handler event.Handler) - - // SubscribeToAllEvents registers a handler for all events - SubscribeToAllEvents(ctx context.Context, handler event.Handler) -} -``` +**Returns:** +- `*supernodeservice.SupernodeStatusresponse`: Status information including CPU usage, memory stats, and active services +- `error`: Error if the supernode is unreachable or query fails -### event.Bus +### SubscribeToEvents -```go -type Bus struct { - // NewBus creates a new event bus - NewBus(ctx context.Context, logger log.Logger, maxWorkers int) *Bus - - // Subscribe registers a handler for a specific event type - Subscribe(ctx context.Context, eventType EventType, handler Handler) - - // SubscribeAll registers a handler for all event types - SubscribeAll(ctx context.Context, handler Handler) - - // Publish sends an event to all relevant subscribers - Publish(ctx context.Context, event Event) -} -``` - -### config.Config +Registers an event handler for specific event types to monitor task progress. ```go -type Config struct { - // Account configuration - Account AccountConfig - - // Lumera blockchain configuration - Lumera LumeraConfig +err := client.SubscribeToEvents(ctx, event.SDKTaskCompleted, func(ctx context.Context, e event.Event) { + fmt.Printf("Task %s completed with type %s\n", e.TaskID, e.TaskType) + // Handle the specific event +}) +if err != nil { + // Handle subscription error } - -// New builds a Config, applying the supplied functional options -func New(opts ...Option) (*Config, error) ``` -## Troubleshooting +**Parameters:** +- `ctx context.Context`: Context for the operation +- `eventType event.EventType`: Specific event type to listen for +- `handler event.Handler`: Function to call when the event occurs -### Common Issues +**Returns:** +- `error`: Error if subscription fails -#### Connection Errors +### SubscribeToAllEvents -If you encounter connection errors when trying to connect to the Lumera blockchain: - -1. Check that the gRPC address is correct and reachable -2. Verify that the chain ID matches the network you're trying to connect to -3. Ensure that the timeout is sufficiently long for your network conditions +Registers an event handler for all event types to monitor all task activity. ```go -// Increase timeout for slow networks -cfg, err := config.New( - config.WithGRPCAddr("127.0.0.1:9090"), - config.WithChainID("lumera-testnet"), - config.WithTimeout(30 * time.Second), // Increased timeout -) +err := client.SubscribeToAllEvents(ctx, func(ctx context.Context, e event.Event) { + fmt.Printf("Event %s for task %s: %v\n", e.Type, e.TaskID, e.Data) + // Handle any event type +}) +if err != nil { + // Handle subscription error +} ``` -#### Key Management Issues +**Parameters:** +- `ctx context.Context`: Context for the operation +- `handler event.Handler`: Function to call for any event -If you encounter issues with key management: +**Returns:** +- `error`: Error if subscription fails -1. Ensure that the keyring directory exists and is writable -2. Check that the key name is correct -3. For test backends, ensure that you're using a consistent environment +## Event System -```bash -# List keys to verify they exist -supernode keys list +The SDK provides an event system to monitor task progress through event subscriptions. You can subscribe to specific event types or all events to track the lifecycle of your tasks. + +### Available Events + +**SDK Task Events:** +- `SDKTaskStarted`: Task has been initiated +- `SDKSupernodesUnavailable`: No supernodes available for processing +- `SDKSupernodesFound`: Supernodes discovered for task processing +- `SDKRegistrationAttempt`: Attempting to register with a supernode +- `SDKRegistrationFailure`: Registration with supernode failed +- `SDKRegistrationSuccessful`: Successfully registered with supernode +- `SDKTaskTxHashReceived`: Transaction hash received from supernode +- `SDKTaskCompleted`: Task completed successfully +- `SDKTaskFailed`: Task failed with error +- `SDKDownloadAttempt`: Attempting to download from supernode +- `SDKDownloadFailure`: Download attempt failed +- `SDKOutputPathReceived`: File download path received +- `SDKDownloadSuccessful`: Download completed successfully + +**Supernode Events (forwarded from supernodes):** +- `SupernodeActionRetrieved`: Action retrieved from blockchain +- `SupernodeActionFeeVerified`: Action fee verified +- `SupernodeTopCheckPassed`: Top supernode verification passed +- `SupernodeMetadataDecoded`: Action metadata decoded successfully +- `SupernodeDataHashVerified`: Data hash verification completed +- `SupernodeInputEncoded`: Input data encoded +- `SupernodeSignatureVerified`: Signature verification passed +- `SupernodeRQIDGenerated`: RaptorQ ID generated +- `SupernodeRQIDVerified`: RaptorQ ID verified +- `SupernodeArtefactsStored`: Artifacts stored successfully +- `SupernodeActionFinalized`: Action processing finalized +- `SupernodeArtefactsDownloaded`: Artifacts downloaded +- `SupernodeUnknown`: Unknown supernode event + +### Event Data Keys + +Events may include additional data accessible through these keys: + +- `event.KeyError`: Error message (for failure events) +- `event.KeyCount`: Count of items (e.g., supernodes found) +- `event.KeySupernode`: Supernode endpoint +- `event.KeySupernodeAddress`: Supernode cosmos address +- `event.KeyIteration`: Attempt iteration number +- `event.KeyTxHash`: Transaction hash +- `event.KeyMessage`: Event message +- `event.KeyProgress`: Progress information +- `event.KeyEventType`: Event type string +- `event.KeyOutputPath`: File output path +- `event.KeyTaskID`: Task ID +- `event.KeyActionID`: Action ID + +### Usage Examples + +**Subscribe to Task Completion:** +```go +err := client.SubscribeToEvents(ctx, event.SDKTaskCompleted, func(ctx context.Context, e event.Event) { + fmt.Printf("Task %s completed\n", e.TaskID) +}) ``` +**Subscribe to All Events:** +```go +err := client.SubscribeToAllEvents(ctx, func(ctx context.Context, e event.Event) { + fmt.Printf("Event: %s for task %s\n", e.Type, e.TaskID) +})