Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(cmd): use rpc client instead of http.Request #2521

Merged
merged 13 commits into from
Aug 7, 2023
189 changes: 189 additions & 0 deletions cmd/celestia/blob.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
package main

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

"github.com/spf13/cobra"

"github.com/celestiaorg/celestia-node/blob"
"github.com/celestiaorg/celestia-node/share"
)

type response struct {
Result interface{} `json:"result"`
}
Wondertan marked this conversation as resolved.
Show resolved Hide resolved

func init() {
blobCmd.AddCommand(getCmd, getAllCmd, submitCmd, getProofCmd)
}

var blobCmd = &cobra.Command{
Use: "blob [command]",
Short: "Allows to interact with the Blob Service via JSON-RPC",
Args: cobra.NoArgs,
}

var getCmd = &cobra.Command{
Use: "get [height, namespace, commitment]",
Args: cobra.ExactArgs(3),
Short: "Returns the blob for the given namespace by commitment at a particular height.",
RunE: func(cmd *cobra.Command, args []string) error {
client, err := rpcClient(cmd.Context())
if err != nil {
return err
}

height, err := strconv.ParseUint(args[0], 10, 64)
if err != nil {
return fmt.Errorf("error parsing a height:%v", err)
}

namespace, err := parseV0Namespace(args[1])
if err != nil {
return fmt.Errorf("error parsing a namespace:%v", err)
}

commitment, err := base64.StdEncoding.DecodeString(args[2])
if err != nil {
return fmt.Errorf("error parsing a commitment:%v", err)
}

blob, err := client.Blob.Get(cmd.Context(), height, namespace, commitment)

output, err := prepareOutput(blob, err)
if err != nil {
return err
}

fmt.Fprintln(os.Stdout, string(output))
return nil
},
}

var getAllCmd = &cobra.Command{
Use: "get-all [height, namespace]",
Args: cobra.ExactArgs(2),
Short: "Returns all blobs for the given namespace at a particular height.",
RunE: func(cmd *cobra.Command, args []string) error {
client, err := rpcClient(cmd.Context())
if err != nil {
return err
}

height, err := strconv.ParseUint(args[0], 10, 64)
if err != nil {
return fmt.Errorf("error parsing a height:%v", err)
}

namespace, err := parseV0Namespace(args[1])
if err != nil {
return fmt.Errorf("error parsing a namespace:%v", err)
}

var output []byte
blobs, err := client.Blob.GetAll(cmd.Context(), height, []share.Namespace{namespace})

output, err = prepareOutput(blobs, err)
if err != nil {
return err
}

fmt.Fprintln(os.Stdout, string(output))
return nil
},
}

var submitCmd = &cobra.Command{
Use: "submit [namespace, blobData]",
Args: cobra.ExactArgs(2),
Short: "Submit the blob at the given namespace. Note: only one blob is allowed to submit through the RPC.",
RunE: func(cmd *cobra.Command, args []string) error {
client, err := rpcClient(cmd.Context())
if err != nil {
return err
}

namespace, err := parseV0Namespace(args[0])
if err != nil {
return fmt.Errorf("error parsing a namespace:%v", err)
}

parsedBlob, err := blob.NewBlobV0(namespace, []byte(args[1]))
if err != nil {
return fmt.Errorf("error creating a blob:%v", err)
}

height, err := client.Blob.Submit(cmd.Context(), []*blob.Blob{parsedBlob})

response := struct {
Height uint64 `json:"uint64"`
Commitment blob.Commitment `json:"commitment"`
}{
Height: height,
Commitment: parsedBlob.Commitment,
}

output, err := prepareOutput(response, err)
if err != nil {
return err
}

fmt.Fprintln(os.Stdout, string(output))
return nil
Wondertan marked this conversation as resolved.
Show resolved Hide resolved
},
}

var getProofCmd = &cobra.Command{
Use: "get-proof [height, namespace, commitment]",
Args: cobra.ExactArgs(3),
Short: "Retrieves the blob in the given namespaces at the given height by commitment and returns its Proof.",
RunE: func(cmd *cobra.Command, args []string) error {
client, err := rpcClient(cmd.Context())
if err != nil {
return err
}

height, err := strconv.ParseUint(args[0], 10, 64)
if err != nil {
return fmt.Errorf("error parsing a height:%v", err)
}

namespace, err := parseV0Namespace(args[1])
if err != nil {
return fmt.Errorf("error parsing a namespace:%v", err)
}

commitment, err := base64.StdEncoding.DecodeString(args[2])
if err != nil {
return fmt.Errorf("error parsing a commitment:%v", err)
}

proof, err := client.Blob.GetProof(cmd.Context(), height, namespace, commitment)

output, err := prepareOutput(proof, err)
if err != nil {
return err
}

fmt.Fprintln(os.Stdout, string(output))
return nil
},
}

func prepareOutput(data interface{}, err error) ([]byte, error) {
if err != nil {
data = err
}

bytes, err := json.MarshalIndent(response{
Result: data,
}, "", " ")
vgonkivs marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return nil, err
}
return bytes, nil
}
151 changes: 40 additions & 111 deletions cmd/celestia/rpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@

import (
"bytes"
"context"
"encoding/base64"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"io"
"log"
Expand All @@ -17,13 +19,14 @@
"github.com/spf13/cobra"

"github.com/celestiaorg/celestia-node/api/rpc/client"
"github.com/celestiaorg/celestia-node/blob"
"github.com/celestiaorg/celestia-node/share"
"github.com/celestiaorg/celestia-node/state"
)

const (
authEnvKey = "CELESTIA_NODE_AUTH_TOKEN"

rpcClientKey = "rpcClient"
)

var requestURL string
Expand Down Expand Up @@ -61,13 +64,33 @@
false,
"Print JSON-RPC request along with the response",
)
rpcCmd.AddCommand(blobCmd)
rootCmd.AddCommand(rpcCmd)
}

var rpcCmd = &cobra.Command{
Use: "rpc [namespace] [method] [params...]",
Short: "Send JSON-RPC request",
Args: cobra.MinimumNArgs(2),
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
client, err := newRPCClient(cmd.Context())
if err != nil {
return err
}

ctx := context.WithValue(cmd.Context(), rpcClientKey, client)

Check warning on line 81 in cmd/celestia/rpc.go

View workflow job for this annotation

GitHub Actions / go-ci / Lint

context-keys-type: should not use basic type untyped string as key in context.WithValue (revive)
cmd.SetContext(ctx)
return nil
},
PersistentPostRunE: func(cmd *cobra.Command, args []string) error {
client, err := rpcClient(cmd.Context())
if err != nil {
return err
}

client.Close()
return nil
},
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
modules := client.Modules
if len(args) == 0 {
Expand Down Expand Up @@ -115,116 +138,6 @@
panic(fmt.Sprintf("Error parsing namespace: %v", err))
}
parsedParams[1] = namespace
case "Submit":
vgonkivs marked this conversation as resolved.
Show resolved Hide resolved
// 1. Namespace
var err error
namespace, err := parseV0Namespace(params[0])
if err != nil {
panic(fmt.Sprintf("Error parsing namespace: %v", err))
}
// 2. Blob data
var blobData []byte
switch {
case strings.HasPrefix(params[1], "0x"):
decoded, err := hex.DecodeString(params[1][2:])
if err != nil {
panic("Error decoding blob: hex string could not be decoded.")
}
blobData = decoded
case strings.HasPrefix(params[1], "\""):
// user input an utf string that needs to be encoded to base64
src := []byte(params[1])
blobData = make([]byte, base64.StdEncoding.EncodedLen(len(src)))
base64.StdEncoding.Encode(blobData, []byte(params[1]))
default:
// otherwise, we assume the user has already encoded their input to base64
blobData, err = base64.StdEncoding.DecodeString(params[1])
if err != nil {
panic("Error decoding blob data: base64 string could not be decoded.")
}
}
parsedBlob, err := blob.NewBlobV0(namespace, blobData)
if err != nil {
panic(fmt.Sprintf("Error creating blob: %v", err))
}
parsedParams[0] = []*blob.Blob{parsedBlob}
// param count doesn't match input length, so cut off nil values
return parsedParams[:1]
case "SubmitPayForBlob":
// 1. Fee (state.Int is a string)
parsedParams[0] = params[0]
// 2. GasLimit (uint64)
num, err := strconv.ParseUint(params[1], 10, 64)
if err != nil {
panic("Error parsing gas limit: uint64 could not be parsed.")
}
parsedParams[1] = num
// 3. Namespace
namespace, err := parseV0Namespace(params[2])
if err != nil {
panic(fmt.Sprintf("Error parsing namespace: %v", err))
}
// 4. Blob data
var blobData []byte
switch {
case strings.HasPrefix(params[3], "0x"):
decoded, err := hex.DecodeString(params[3][2:])
if err != nil {
panic("Error decoding blob: hex string could not be decoded.")
}
blobData = decoded
case strings.HasPrefix(params[3], "\""):
// user input an utf string that needs to be encoded to base64
src := []byte(params[1])
blobData = make([]byte, base64.StdEncoding.EncodedLen(len(src)))
base64.StdEncoding.Encode(blobData, []byte(params[3]))
default:
// otherwise, we assume the user has already encoded their input to base64
blobData, err = base64.StdEncoding.DecodeString(params[3])
if err != nil {
panic("Error decoding blob: base64 string could not be decoded.")
}
}
parsedBlob, err := blob.NewBlobV0(namespace, blobData)
if err != nil {
panic(fmt.Sprintf("Error creating blob: %v", err))
}
parsedParams[2] = []*blob.Blob{parsedBlob}
return parsedParams[:3]
case "Get":
// 1. Height
num, err := strconv.ParseUint(params[0], 10, 64)
if err != nil {
panic("Error parsing height: uint64 could not be parsed.")
}
parsedParams[0] = num
// 2. NamespaceID
namespace, err := parseV0Namespace(params[1])
if err != nil {
panic(fmt.Sprintf("Error parsing namespace: %v", err))
}
parsedParams[1] = namespace
// 3: Commitment
commitment, err := base64.StdEncoding.DecodeString(params[2])
if err != nil {
panic("Error decoding commitment: base64 string could not be decoded.")
}
parsedParams[2] = commitment
return parsedParams
case "GetAll": // NOTE: Over the cli, you can only pass one namespace
// 1. Height
num, err := strconv.ParseUint(params[0], 10, 64)
if err != nil {
panic("Error parsing height: uint64 could not be parsed.")
}
parsedParams[0] = num
// 2. Namespace
namespace, err := parseV0Namespace(params[1])
if err != nil {
panic(fmt.Sprintf("Error parsing namespace: %v", err))
}
parsedParams[1] = []share.Namespace{namespace}
return parsedParams
case "QueryDelegation", "QueryUnbonding", "BalanceForAddress":
var err error
parsedParams[0], err = parseAddressFromString(params[0])
Expand Down Expand Up @@ -445,8 +358,24 @@
}
return decoded, nil
}

func parseJSON(param string) (json.RawMessage, error) {
var raw json.RawMessage
err := json.Unmarshal([]byte(param), &raw)
return raw, err
}

func newRPCClient(ctx context.Context) (*client.Client, error) {
if authTokenFlag == "" {
authTokenFlag = os.Getenv(authEnvKey)
}
return client.NewClient(ctx, requestURL, authTokenFlag)
}

func rpcClient(ctx context.Context) (*client.Client, error) {
client, ok := ctx.Value(rpcClientKey).(*client.Client)
if !ok {
return nil, errors.New("rpc client was not set")
}
return client, nil
}
Loading