Skip to content
Draft
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
426 changes: 215 additions & 211 deletions cmd/app.go

Large diffs are not rendered by default.

17 changes: 17 additions & 0 deletions lotusclient/lotusclient.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package lotusclient

import (
"github.com/ybbus/jsonrpc/v3"
)

// NewLotusClient creates a new JSON-RPC client for interacting with a Lotus node.
func NewLotusClient(lotusAPI string, lotusToken string) jsonrpc.RPCClient {
if lotusToken == "" {
return jsonrpc.NewClient(lotusAPI)
}
return jsonrpc.NewClientWithOpts(lotusAPI, &jsonrpc.RPCClientOpts{
CustomHeaders: map[string]string{
"Authorization": "Bearer " + lotusToken,
},
})
}
31 changes: 31 additions & 0 deletions migrate/migrations/202507210010_create_deal_proof_trackings.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package migrations

import (
"github.com/go-gormigrate/gormigrate/v2"
"gorm.io/gorm"
)

func CreateDealProofTrackings202507210010() *gormigrate.Migration {
return &gormigrate.Migration{
ID: "202507210010",
Migrate: func(tx *gorm.DB) error {
return tx.Exec(`
CREATE TABLE IF NOT EXISTS deal_proof_trackings (
deal_id INTEGER PRIMARY KEY,
provider TEXT NOT NULL,
sector_id INTEGER NOT NULL,
sector_start_epoch INTEGER NOT NULL,
current_deadline_index INTEGER NOT NULL,
period_start_epoch INTEGER NOT NULL,
estimated_next_proof_time DATETIME NOT NULL,
faults INTEGER NOT NULL,
recoveries INTEGER NOT NULL,
last_updated_at DATETIME NOT NULL
)
`).Error
},
Rollback: func(tx *gorm.DB) error {
return tx.Exec(`DROP TABLE IF EXISTS deal_proof_trackings`).Error
},
}
}
2 changes: 2 additions & 0 deletions migrate/migrations/migrations.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

package migrations

import (
Expand All @@ -14,5 +15,6 @@ func GetMigrations() []*gormigrate.Migration {
_202507090900_add_missing_deal_template_fields(),
_202507090915_add_not_null_defaults(),
_202507091000_add_schedule_fields_to_deal_templates(),
CreateDealProofTrackings202507210010(),
}
}
5 changes: 5 additions & 0 deletions model/migrate.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import (
"crypto/rand"
"encoding/base64"

// For registering DealProofTracking in migrations
"github.com/data-preservation-programs/singularity/service/dealprooftracker"

"github.com/cockroachdb/errors"
"github.com/data-preservation-programs/singularity/migrate/migrations"
"github.com/go-gormigrate/gormigrate/v2"
Expand Down Expand Up @@ -31,6 +34,8 @@ var Tables = []any{
&Deal{},
&Schedule{},
&Wallet{},
// --- Added for dealprooftracker ---
&dealprooftracker.DealProofTracking{},
}

var logger = logging.Logger("model")
Expand Down
39 changes: 39 additions & 0 deletions service/dealprooftracker/api.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package dealprooftracker

import (
"net/http"
"strconv"
"github.com/gin-gonic/gin"

Check failure on line 6 in service/dealprooftracker/api.go

View workflow job for this annotation

GitHub Actions / integration-test

missing go.sum entry for module providing package github.com/gin-gonic/gin (imported by github.com/data-preservation-programs/singularity/service/dealprooftracker); to add:

Check failure on line 6 in service/dealprooftracker/api.go

View workflow job for this annotation

GitHub Actions / go-test / macos (go this)

missing go.sum entry for module providing package github.com/gin-gonic/gin (imported by github.com/data-preservation-programs/singularity/service/dealprooftracker); to add:

Check failure on line 6 in service/dealprooftracker/api.go

View workflow job for this annotation

GitHub Actions / go-test / ubuntu (go this)

missing go.sum entry for module providing package github.com/gin-gonic/gin (imported by github.com/data-preservation-programs/singularity/service/dealprooftracker); to add:

Check failure on line 6 in service/dealprooftracker/api.go

View workflow job for this annotation

GitHub Actions / go-test / macos (go next)

missing go.sum entry for module providing package github.com/gin-gonic/gin (imported by github.com/data-preservation-programs/singularity/service/dealprooftracker); to add:

Check failure on line 6 in service/dealprooftracker/api.go

View workflow job for this annotation

GitHub Actions / go-test / macos (go next)

missing go.sum entry for module providing package github.com/gin-gonic/gin (imported by github.com/data-preservation-programs/singularity/service/dealprooftracker); to add:

Check failure on line 6 in service/dealprooftracker/api.go

View workflow job for this annotation

GitHub Actions / go-test / macos (go next)

missing go.sum entry for module providing package github.com/gin-gonic/gin (imported by github.com/data-preservation-programs/singularity/service/dealprooftracker); to add:

Check failure on line 6 in service/dealprooftracker/api.go

View workflow job for this annotation

GitHub Actions / go-test / macos (go next)

missing go.sum entry for module providing package github.com/gin-gonic/gin (imported by github.com/data-preservation-programs/singularity/service/dealprooftracker); to add:

Check failure on line 6 in service/dealprooftracker/api.go

View workflow job for this annotation

GitHub Actions / go-test / macos (go next)

missing go.sum entry for module providing package github.com/gin-gonic/gin (imported by github.com/data-preservation-programs/singularity/service/dealprooftracker); to add:

Check failure on line 6 in service/dealprooftracker/api.go

View workflow job for this annotation

GitHub Actions / go-test / macos (go next)

missing go.sum entry for module providing package github.com/gin-gonic/gin (imported by github.com/data-preservation-programs/singularity/service/dealprooftracker); to add:

Check failure on line 6 in service/dealprooftracker/api.go

View workflow job for this annotation

GitHub Actions / go-test / macos (go next)

missing go.sum entry for module providing package github.com/gin-gonic/gin (imported by github.com/data-preservation-programs/singularity/service/dealprooftracker); to add:

Check failure on line 6 in service/dealprooftracker/api.go

View workflow job for this annotation

GitHub Actions / go-test / macos (go next)

missing go.sum entry for module providing package github.com/gin-gonic/gin (imported by github.com/data-preservation-programs/singularity/service/dealprooftracker); to add:

Check failure on line 6 in service/dealprooftracker/api.go

View workflow job for this annotation

GitHub Actions / go-test / macos (go next)

missing go.sum entry for module providing package github.com/gin-gonic/gin (imported by github.com/data-preservation-programs/singularity/service/dealprooftracker); to add:

Check failure on line 6 in service/dealprooftracker/api.go

View workflow job for this annotation

GitHub Actions / go-test / macos (go next)

missing go.sum entry for module providing package github.com/gin-gonic/gin (imported by github.com/data-preservation-programs/singularity/service/dealprooftracker); to add:

Check failure on line 6 in service/dealprooftracker/api.go

View workflow job for this annotation

GitHub Actions / go-check / All

missing go.sum entry for module providing package github.com/gin-gonic/gin (imported by github.com/data-preservation-programs/singularity/service/dealprooftracker); to add:

Check failure on line 6 in service/dealprooftracker/api.go

View workflow job for this annotation

GitHub Actions / go-check / All

missing go.sum entry for module providing package github.com/gin-gonic/gin (imported by github.com/data-preservation-programs/singularity/service/dealprooftracker); to add:

Check failure on line 6 in service/dealprooftracker/api.go

View workflow job for this annotation

GitHub Actions / go-check / All

missing go.sum entry for module providing package github.com/gin-gonic/gin (imported by github.com/data-preservation-programs/singularity/service/dealprooftracker); to add:

Check failure on line 6 in service/dealprooftracker/api.go

View workflow job for this annotation

GitHub Actions / go-test / ubuntu (go next)

missing go.sum entry for module providing package github.com/gin-gonic/gin (imported by github.com/data-preservation-programs/singularity/service/dealprooftracker); to add:

Check failure on line 6 in service/dealprooftracker/api.go

View workflow job for this annotation

GitHub Actions / go-test / ubuntu (go next)

missing go.sum entry for module providing package github.com/gin-gonic/gin (imported by github.com/data-preservation-programs/singularity/service/dealprooftracker); to add:

Check failure on line 6 in service/dealprooftracker/api.go

View workflow job for this annotation

GitHub Actions / go-test / ubuntu (go next)

missing go.sum entry for module providing package github.com/gin-gonic/gin (imported by github.com/data-preservation-programs/singularity/service/dealprooftracker); to add:

Check failure on line 6 in service/dealprooftracker/api.go

View workflow job for this annotation

GitHub Actions / go-test / ubuntu (go next)

missing go.sum entry for module providing package github.com/gin-gonic/gin (imported by github.com/data-preservation-programs/singularity/service/dealprooftracker); to add:

Check failure on line 6 in service/dealprooftracker/api.go

View workflow job for this annotation

GitHub Actions / go-test / ubuntu (go next)

missing go.sum entry for module providing package github.com/gin-gonic/gin (imported by github.com/data-preservation-programs/singularity/service/dealprooftracker); to add:

Check failure on line 6 in service/dealprooftracker/api.go

View workflow job for this annotation

GitHub Actions / go-test / ubuntu (go next)

missing go.sum entry for module providing package github.com/gin-gonic/gin (imported by github.com/data-preservation-programs/singularity/service/dealprooftracker); to add:

Check failure on line 6 in service/dealprooftracker/api.go

View workflow job for this annotation

GitHub Actions / go-test / ubuntu (go next)

missing go.sum entry for module providing package github.com/gin-gonic/gin (imported by github.com/data-preservation-programs/singularity/service/dealprooftracker); to add:

Check failure on line 6 in service/dealprooftracker/api.go

View workflow job for this annotation

GitHub Actions / go-test / ubuntu (go next)

missing go.sum entry for module providing package github.com/gin-gonic/gin (imported by github.com/data-preservation-programs/singularity/service/dealprooftracker); to add:

Check failure on line 6 in service/dealprooftracker/api.go

View workflow job for this annotation

GitHub Actions / go-test / ubuntu (go next)

missing go.sum entry for module providing package github.com/gin-gonic/gin (imported by github.com/data-preservation-programs/singularity/service/dealprooftracker); to add:

Check failure on line 6 in service/dealprooftracker/api.go

View workflow job for this annotation

GitHub Actions / go-test / ubuntu (go next)

missing go.sum entry for module providing package github.com/gin-gonic/gin (imported by github.com/data-preservation-programs/singularity/service/dealprooftracker); to add:

Check failure on line 6 in service/dealprooftracker/api.go

View workflow job for this annotation

GitHub Actions / staticcheck

missing go.sum entry for module providing package github.com/gin-gonic/gin (imported by github.com/data-preservation-programs/singularity/service/dealprooftracker); to add:

Check failure on line 6 in service/dealprooftracker/api.go

View workflow job for this annotation

GitHub Actions / go-test / windows (go this)

missing go.sum entry for module providing package github.com/gin-gonic/gin (imported by github.com/data-preservation-programs/singularity/service/dealprooftracker); to add:

Check failure on line 6 in service/dealprooftracker/api.go

View workflow job for this annotation

GitHub Actions / go-test / windows (go next)

missing go.sum entry for module providing package github.com/gin-gonic/gin (imported by github.com/data-preservation-programs/singularity/service/dealprooftracker); to add:

Check failure on line 6 in service/dealprooftracker/api.go

View workflow job for this annotation

GitHub Actions / go-test / windows (go next)

missing go.sum entry for module providing package github.com/gin-gonic/gin (imported by github.com/data-preservation-programs/singularity/service/dealprooftracker); to add:

Check failure on line 6 in service/dealprooftracker/api.go

View workflow job for this annotation

GitHub Actions / go-test / windows (go next)

missing go.sum entry for module providing package github.com/gin-gonic/gin (imported by github.com/data-preservation-programs/singularity/service/dealprooftracker); to add:

Check failure on line 6 in service/dealprooftracker/api.go

View workflow job for this annotation

GitHub Actions / go-test / windows (go next)

missing go.sum entry for module providing package github.com/gin-gonic/gin (imported by github.com/data-preservation-programs/singularity/service/dealprooftracker); to add:

Check failure on line 6 in service/dealprooftracker/api.go

View workflow job for this annotation

GitHub Actions / go-test / windows (go next)

missing go.sum entry for module providing package github.com/gin-gonic/gin (imported by github.com/data-preservation-programs/singularity/service/dealprooftracker); to add:

Check failure on line 6 in service/dealprooftracker/api.go

View workflow job for this annotation

GitHub Actions / go-test / windows (go next)

missing go.sum entry for module providing package github.com/gin-gonic/gin (imported by github.com/data-preservation-programs/singularity/service/dealprooftracker); to add:

Check failure on line 6 in service/dealprooftracker/api.go

View workflow job for this annotation

GitHub Actions / go-test / windows (go next)

missing go.sum entry for module providing package github.com/gin-gonic/gin (imported by github.com/data-preservation-programs/singularity/service/dealprooftracker); to add:

Check failure on line 6 in service/dealprooftracker/api.go

View workflow job for this annotation

GitHub Actions / go-test / windows (go next)

missing go.sum entry for module providing package github.com/gin-gonic/gin (imported by github.com/data-preservation-programs/singularity/service/dealprooftracker); to add:

Check failure on line 6 in service/dealprooftracker/api.go

View workflow job for this annotation

GitHub Actions / go-test / windows (go next)

missing go.sum entry for module providing package github.com/gin-gonic/gin (imported by github.com/data-preservation-programs/singularity/service/dealprooftracker); to add:

Check failure on line 6 in service/dealprooftracker/api.go

View workflow job for this annotation

GitHub Actions / go-test / windows (go next)

missing go.sum entry for module providing package github.com/gin-gonic/gin (imported by github.com/data-preservation-programs/singularity/service/dealprooftracker); to add:
)

// RegisterRoutes registers dealprooftracker API endpoints
func RegisterRoutes(r *gin.Engine, tracker *ProofTracker) {
r.GET("/api/v1/dealproofs/:deal_id", func(c *gin.Context) {
dealID, err := strconv.ParseUint(c.Param("deal_id"), 10, 64)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid deal_id"})
return
}
ctx := c.Request.Context()
info, err := tracker.GetDBProofInfo(ctx, dealID)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, info)
})
r.GET("/api/v1/dealproofs/:deal_id/live", func(c *gin.Context) {
dealID, err := strconv.ParseUint(c.Param("deal_id"), 10, 64)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid deal_id"})
return
}
ctx := c.Request.Context()
info, err := tracker.GetLiveProofInfo(ctx, dealID)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, info)
})
}
197 changes: 197 additions & 0 deletions service/dealprooftracker/cli.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
package dealprooftracker

import (
"encoding/json"
"fmt"
"os"
"strconv"
"time"

"github.com/urfave/cli/v2"
"github.com/data-preservation-programs/singularity/database"
)

const defaultPollingInterval = 5 * time.Minute

// CLICommands returns the CLI commands for dealprooftracker
func CLICommands() *cli.Command {
return &cli.Command{
Name: "dealproof",
Usage: "Deal Proof Tracker commands",
Subcommands: []*cli.Command{
{
Name: "run",
Usage: "Start the deal proof tracker service loop",
Action: runTracker,
Flags: commonFlags(),
},
{
Name: "live",
Usage: "Fetch proof info in real time from Lotus",
ArgsUsage: "<dealID>",
Action: liveProof,
Flags: commonFlags(),
},
{
Name: "db",
Usage: "Fetch proof info from the local DB",
ArgsUsage: "<dealID>",
Action: dbProof,
Flags: commonFlags(),
},
{
Name: "health",
Usage: "Check DB and Lotus connectivity",
Action: healthCheck,
Flags: commonFlags(),
},
},
}
}

func commonFlags() []cli.Flag {
return []cli.Flag{
&cli.StringFlag{
Name: "database-connection-string",
EnvVars: []string{"DATABASE_CONNECTION_STRING"},
Usage: "Database connection string",
},
&cli.StringFlag{
Name: "lotus-api",
EnvVars: []string{"LOTUS_API"},
Usage: "Lotus API endpoint",
},
&cli.StringFlag{
Name: "lotus-token",
EnvVars: []string{"LOTUS_TOKEN"},
Usage: "Lotus API token",
},
&cli.StringFlag{
Name: "output",
Usage: "Output format: plain|json",
Value: "plain",
},
&cli.DurationFlag{
Name: "interval",
Usage: "Polling interval for the tracker service",
Value: defaultPollingInterval,
},
}
}

// Helper to open DB and initialize tracker
func resolveDeps(c *cli.Context) (*ProofTracker, error) {
connStr := c.String("database-connection-string")
if connStr == "" {
return nil, fmt.Errorf("database connection string is required")
}

db, _, err := database.OpenWithLogger(connStr)
if err != nil {
return nil, fmt.Errorf("failed to open DB: %w", err)
}

lotusAPI := c.String("lotus-api")
if lotusAPI == "" {
return nil, fmt.Errorf("lotus-api is required")
}

lotusToken := c.String("lotus-token")
interval := c.Duration("interval")

// Create and return the tracker
return NewProofTracker(db, lotusAPI, lotusToken, interval), nil
}

func runTracker(c *cli.Context) error {
tracker, err := resolveDeps(c)
if err != nil {
return err
}
tracker.Start(c.Context)
select {} // Block forever
}

func liveProof(c *cli.Context) error {
tracker, err := resolveDeps(c)
if err != nil {
return err
}

if c.NArg() < 1 {
return fmt.Errorf("dealID required")
}

dealID, err := strconv.ParseUint(c.Args().Get(0), 10, 64)
if err != nil {
return fmt.Errorf("invalid dealID: %w", err)
}

info, err := tracker.GetLiveProofInfo(c.Context, dealID)
if err != nil {
return fmt.Errorf("failed to get live proof info: %w", err)
}

return printOutput(c, info)
}

func dbProof(c *cli.Context) error {
tracker, err := resolveDeps(c)
if err != nil {
return err
}

if c.NArg() < 1 {
return fmt.Errorf("dealID required")
}

dealID, err := strconv.ParseUint(c.Args().Get(0), 10, 64)
if err != nil {
return fmt.Errorf("invalid dealID: %w", err)
}

info, err := tracker.GetDBProofInfo(c.Context, dealID)
if err != nil {
return fmt.Errorf("failed to get DB proof info: %w", err)
}

return printOutput(c, info)
}

func healthCheck(c *cli.Context) error {
tracker, err := resolveDeps(c)
if err != nil {
return err
}

// Check DB connection
if err := tracker.db.Exec("SELECT 1").Error; err != nil {
return fmt.Errorf("DB health check failed: %w", err)
}

// Check Lotus connection by attempting to list deals (simplified health check)
info, checkErr := tracker.GetLiveProofInfo(c.Context, 1) // Use deal ID 1 as a test
if checkErr != nil {
return fmt.Errorf("Lotus health check failed: %w", checkErr)
}
_ = info // Ignore the result, we just care about connectivity

fmt.Println("Health check: OK")
return nil
}

func printOutput(c *cli.Context, v interface{}) error {
switch c.String("output") {
case "json":
return printJSON(v)
default:
fmt.Printf("%+v\n", v)
return nil
}
}

func printJSON(v interface{}) error {
enc := json.NewEncoder(os.Stdout)
enc.SetIndent("", " ")
return enc.Encode(v)
}
Loading
Loading