Skip to content

Commit

Permalink
feat: Add chain connectivity card
Browse files Browse the repository at this point in the history
  • Loading branch information
strahe committed Aug 31, 2024
1 parent 6abbc21 commit 5ce6ffd
Show file tree
Hide file tree
Showing 16 changed files with 318 additions and 267 deletions.
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ curio-rpc-gen:
goimports -w graph/curiorpc
.PHONY: api-gen

graph-type-gen:
type-gen:
cd ui && yarn graphql-codegen -c ../codegen.yml

go-gen:
go generate ./...

gen: go-gen curio-rpc-gen graph-type-gen
gen: go-gen curio-rpc-gen type-gen
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ To build the dashboard, follow these steps:

## Requirements

- **Curio Node**: Accessible Curio Web API, such as `http://localhost:4701`
- **YugabyteDB**: The database used by the Curio cluster.
- **Prometheus**: To collect metrics from the Curio cluster.
- **Lotus Daemon Node**: Required for blockchain data.
Expand Down
2 changes: 1 addition & 1 deletion cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func main() {
},
Before: func(c *cli.Context) error {
defer func() {
logging.SetLogLevel("rpc", "ERROR") // nolint: errcheck
_ = logging.SetLogLevel("rpc", "ERROR") // nolint: errcheck
}()
if c.Bool("debug") {
return logging.SetLogLevel("*", "DEBUG")
Expand Down
9 changes: 8 additions & 1 deletion cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ var runCmd = &cli.Command{
if err != nil {
return fmt.Errorf("failed to load config: %w", err)
}

harmonyDB, err := db.NewHarmonyDB(cctx.Context, cfg.HarmonyDB)
if err != nil {
return fmt.Errorf("failed to connect to harmonydb: %w", err)
Expand All @@ -47,6 +48,12 @@ var runCmd = &cli.Command{
}
defer closer()

curioAPI, closer, err := getCurioWebRpcV0(cctx, cfg)
if err != nil {
return fmt.Errorf("failed to get curio web rpc: %w", err)
}
defer closer()

if err := setupNetwork(cctx.Context, chainAPI); err != nil {
return fmt.Errorf("failed to setup network: %w", err)
}
Expand All @@ -62,7 +69,7 @@ var runCmd = &cli.Command{
if err != nil {
return fmt.Errorf("failed to create Prometheus client: %s", err)
}
if err := graph.Router(e, cfg, resolvers.NewResolver(harmonyDB, chainAPI, client)); err != nil {
if err := graph.Router(e, cfg, resolvers.NewResolver(harmonyDB, chainAPI, curioAPI, client)); err != nil {
return fmt.Errorf("failed to setup GraphQL routes: %w", err)
}

Expand Down
52 changes: 50 additions & 2 deletions cmd/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,17 @@ package main
import (
"context"
"fmt"
"net/url"
"os"
"strings"

"github.com/strahe/curio-dashboard/config"

"github.com/filecoin-project/curio/api"
"github.com/filecoin-project/curio/deps"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-jsonrpc"
"github.com/filecoin-project/lotus/build"
"github.com/strahe/curio-dashboard/config"
"github.com/strahe/curio-dashboard/graph/curiorpc"
"github.com/urfave/cli/v2"
)

Expand All @@ -26,6 +27,53 @@ func getChainAPI(cctx *cli.Context, cfg config.ChainConfig) (api.Chain, jsonrpc.
return deps.GetFullNodeAPIV1Curio(cctx, apiInfo)
}

func getCurioWebRpcV0(ctx *cli.Context, cfg *config.Config) (curiorpc.WebRPC, jsonrpc.ClientCloser, error) {
var webRPCs []curiorpc.WebRPC
var closers []jsonrpc.ClientCloser

for _, endpoint := range cfg.Curio.APIs {

u, err := url.Parse(endpoint)
if err != nil {
log.Errorf("Failed to parse curio api endpoint: %s, Reason: %s", endpoint, err.Error())
continue
}
var rpcAddr string
switch u.Scheme {
case "http", "ws":
rpcAddr = fmt.Sprintf("ws://%s/api/webrpc/v0", u.Host)
case "https", "wss":
rpcAddr = fmt.Sprintf("wss://%s/api/webrpc/v0", u.Host)
default:
log.Errorf("Invalid scheme for curio api endpoint: %s", endpoint)
continue
}

wRpc, closer, err := curiorpc.NewWebRPCV0(ctx.Context, rpcAddr, nil)
if err != nil {
log.Errorf("Not able to establish connection to curio with addr: %s, Reason: %s", rpcAddr, err.Error())
continue
}
webRPCs = append(webRPCs, wRpc)
closers = append(closers, closer)
}

if len(webRPCs) < 1 {
return nil, nil, fmt.Errorf("no curio nodes available")
}

finalCloser := func() {
for _, c := range closers {
c()
}
}

var webRPC curiorpc.WebRPCStruct
curiorpc.WebRPCProxy(webRPCs, &webRPC)

return &webRPC, finalCloser, nil
}

func setupNetwork(ctx context.Context, node api.Chain) error {
// get network name
ntn, err := node.StateNetworkName(ctx)
Expand Down
10 changes: 10 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ type ChainConfig struct {
APIs []string `toml:"apis" comment:"List of chain API to connect to"`
}

type CurioConfig struct {
APIs []string `toml:"apis" comment:"List of Curio API to connect to"`
}

type MetricsConfig struct {
Enabled bool `toml:"enabled" comment:"Enable metrics api for analytics, must provide the Prometheus URL for collecting Curio metrics."`
Prometheus string `toml:"prometheus" comment:"URL to connect to the Prometheus server"`
Expand Down Expand Up @@ -42,6 +46,7 @@ type Config struct {
Http HttpConfig `toml:"api" comment:"Http configuration"`
HarmonyDB HarmonyDBConfig `toml:"harmonydb" comment:"HarmonyDB database configuration"`
Chain ChainConfig `toml:"chain" comment:"Chain API configuration"`
Curio CurioConfig `toml:"curio" comment:"Curio configuration"`
Auth AuthConfig `toml:"auth" comment:"Authentication configuration"`
Features FeaturesConfig `toml:"features" comment:"Features configuration"`
}
Expand Down Expand Up @@ -79,4 +84,9 @@ var DefaultConfig = Config{
Prometheus: "http://localhost:9090",
},
},
Curio: CurioConfig{
APIs: []string{
"http://127.0.0.1:4701",
},
},
}
63 changes: 63 additions & 0 deletions graph/curiorpc/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,78 @@ package curiorpc
import (
"context"
"net/http"
"reflect"
"time"

"github.com/filecoin-project/go-jsonrpc"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/lib/retry"
)

type contextKey string

func NewWebRPCV0(ctx context.Context, addr string, requestHeader http.Header) (WebRPC, jsonrpc.ClientCloser, error) {
var res WebRPCStruct
closer, err := jsonrpc.NewMergeClient(ctx, addr, "CurioWeb",
api.GetInternalStructs(&res), requestHeader, jsonrpc.WithErrors(api.RPCErrors))

return &res, closer, err
}

func WebRPCProxy[T WebRPC](ins []T, outStr *WebRPCStruct) {
outs := api.GetInternalStructs(outStr)

var rIns []reflect.Value
for _, in := range ins {
rIns = append(rIns, reflect.ValueOf(in))
}

for _, out := range outs {
rProxyInternal := reflect.ValueOf(out).Elem()

for f := 0; f < rProxyInternal.NumField(); f++ {
field := rProxyInternal.Type().Field(f)

var fns []reflect.Value
for _, rin := range rIns {
fns = append(fns, rin.MethodByName(field.Name))
}

rProxyInternal.Field(f).Set(reflect.MakeFunc(field.Type, func(args []reflect.Value) (results []reflect.Value) {
errorsToRetry := []error{&jsonrpc.RPCConnectionError{}, &jsonrpc.ErrClient{}}
initialBackoff, err := time.ParseDuration("1s")
if err != nil {
return nil
}

ctx := args[0].Interface().(context.Context)

curr := -1

// for calls that need to be performed on the same node
// primarily for miner when calling create block and submit block subsequently
key := contextKey("retry-node")
if ctx.Value(key) != nil {
if (*ctx.Value(key).(**int)) == nil {
*ctx.Value(key).(**int) = &curr
} else {
curr = **ctx.Value(key).(**int) - 1
}
}

total := len(rIns)
result, _ := retry.Retry(ctx, 5, initialBackoff, errorsToRetry, func() ([]reflect.Value, error) {
curr = (curr + 1) % total

result := fns[curr].Call(args)
if result[len(result)-1].IsNil() {
return result, nil
}
e := result[len(result)-1].Interface().(error)
return result, e
})
return result
}))
}
}
}
Loading

0 comments on commit 5ce6ffd

Please sign in to comment.