From 81af12ee8fdcddda256924881745c362a4e238ca Mon Sep 17 00:00:00 2001 From: Viacheslav Gonkivskyi Date: Fri, 15 Sep 2023 15:38:08 +0300 Subject: [PATCH] improvement(cmd): extract rpc cmds --- cmd/celestia/cmd_test.go | 20 + cmd/celestia/das.go | 27 -- cmd/celestia/{ => internal/admin}/admin.go | 74 ++- cmd/celestia/internal/init.go | 33 ++ cmd/celestia/{ => internal/rpc}/blob.go | 66 ++- cmd/celestia/internal/rpc/das.go | 36 ++ cmd/celestia/{ => internal/rpc}/header.go | 68 ++- cmd/celestia/{ => internal/rpc}/p2p.go | 186 +++----- cmd/celestia/{ => internal/rpc}/share.go | 75 ++- cmd/celestia/{ => internal/rpc}/state.go | 153 +++---- cmd/celestia/internal/util.go | 64 +++ .../{rpc_test.go => internal/util_test.go} | 4 +- cmd/celestia/rpc.go | 431 +----------------- 13 files changed, 396 insertions(+), 841 deletions(-) delete mode 100644 cmd/celestia/das.go rename cmd/celestia/{ => internal/admin}/admin.go (51%) create mode 100644 cmd/celestia/internal/init.go rename cmd/celestia/{ => internal/rpc}/blob.go (76%) create mode 100644 cmd/celestia/internal/rpc/das.go rename cmd/celestia/{ => internal/rpc}/header.go (58%) rename cmd/celestia/{ => internal/rpc}/p2p.go (73%) rename cmd/celestia/{ => internal/rpc}/share.go (70%) rename cmd/celestia/{ => internal/rpc}/state.go (76%) create mode 100644 cmd/celestia/internal/util.go rename cmd/celestia/{rpc_test.go => internal/util_test.go} (97%) diff --git a/cmd/celestia/cmd_test.go b/cmd/celestia/cmd_test.go index 503548b708..9c26489e14 100644 --- a/cmd/celestia/cmd_test.go +++ b/cmd/celestia/cmd_test.go @@ -128,3 +128,23 @@ func TestBridge(t *testing.T) { }) */ } + +func parseSignatureForHelpstring(methodSig reflect.StructField) string { + simplifiedSignature := "(" + in, out := methodSig.Type.NumIn(), methodSig.Type.NumOut() + for i := 1; i < in; i++ { + simplifiedSignature += methodSig.Type.In(i).String() + if i != in-1 { + simplifiedSignature += ", " + } + } + simplifiedSignature += ") -> (" + for i := 0; i < out-1; i++ { + simplifiedSignature += methodSig.Type.Out(i).String() + if i != out-2 { + simplifiedSignature += ", " + } + } + simplifiedSignature += ")" + return simplifiedSignature +} diff --git a/cmd/celestia/das.go b/cmd/celestia/das.go deleted file mode 100644 index 58c4a8362b..0000000000 --- a/cmd/celestia/das.go +++ /dev/null @@ -1,27 +0,0 @@ -package main - -import "github.com/spf13/cobra" - -func init() { - dasCmd.AddCommand(samplingStatsCmd) -} - -var dasCmd = &cobra.Command{ - Use: "das [command]", - Short: "Allows to interact with the Daser via JSON-RPC", - Args: cobra.NoArgs, -} - -var samplingStatsCmd = &cobra.Command{ - Use: "sampling-stats", - Short: "Returns the current statistics over the DA sampling process", - Args: cobra.NoArgs, - RunE: func(cmd *cobra.Command, args []string) error { - client, err := rpcClient(cmd.Context()) - if err != nil { - return err - } - stats, err := client.DAS.SamplingStats(cmd.Context()) - return printOutput(stats, err, nil) - }, -} diff --git a/cmd/celestia/admin.go b/cmd/celestia/internal/admin/admin.go similarity index 51% rename from cmd/celestia/admin.go rename to cmd/celestia/internal/admin/admin.go index a3665e0aa1..dfb12e6f66 100644 --- a/cmd/celestia/admin.go +++ b/cmd/celestia/internal/admin/admin.go @@ -1,33 +1,31 @@ -package main +package admin import ( - "context" "errors" "strings" "github.com/filecoin-project/go-jsonrpc/auth" "github.com/spf13/cobra" + + "github.com/celestiaorg/celestia-node/cmd/celestia/internal" ) func init() { - nodeCmd.AddCommand(nodeInfoCmd, logCmd, verifyCmd, authCmd) - rootCmd.AddCommand(nodeCmd) -} + NodeCmd.AddCommand(nodeInfoCmd, logCmd, verifyCmd, authCmd) -var nodeCmd = &cobra.Command{ - Use: "node [command]", - Short: "Allows administrating running node.", - Args: cobra.NoArgs, - PersistentPreRunE: func(cmd *cobra.Command, args []string) error { - rpcClient, err := newRPCClient(cmd.Context()) - if err != nil { - return err - } + NodeCmd.PersistentFlags().StringVar( + &internal.RequestURL, + "url", + "http://localhost:26658", + "Request URL", + ) +} - ctx := context.WithValue(cmd.Context(), rpcClientKey{}, rpcClient) - cmd.SetContext(ctx) - return nil - }, +var NodeCmd = &cobra.Command{ + Use: "node [command]", + Short: "Allows administrating running node.", + Args: cobra.NoArgs, + PersistentPreRunE: internal.InitClient, } var nodeInfoCmd = &cobra.Command{ @@ -35,27 +33,19 @@ var nodeInfoCmd = &cobra.Command{ Args: cobra.NoArgs, Short: "Returns administrative information about the node.", RunE: func(c *cobra.Command, args []string) error { - client, err := rpcClient(c.Context()) - if err != nil { - return err - } - info, err := client.Node.Info(c.Context()) - return printOutput(info, err, nil) + info, err := internal.RPCClient.Node.Info(c.Context()) + return internal.PrintOutput(info, err, nil) }, } var logCmd = &cobra.Command{ - Use: "log-level", - Args: cobra.MinimumNArgs(1), - Short: "Allows to set log level for module to in format :" + + Use: "log-level", + Args: cobra.MinimumNArgs(1), + Short: "Sets log level for module.", + Long: "Allows to set log level for module to in format :" + "`DEBUG, INFO, WARN, ERROR, DPANIC, PANIC, FATAL and their lower-case forms`.\n" + "To set all modules to a particular level `*:` should be passed", RunE: func(c *cobra.Command, args []string) error { - client, err := rpcClient(c.Context()) - if err != nil { - return err - } - for _, ll := range args { params := strings.Split(ll, ":") if len(params) != 2 { @@ -63,7 +53,7 @@ var logCmd = &cobra.Command{ "e.g. pubsub:debug") } - if err = client.Node.LogLevelSet(c.Context(), params[0], params[1]); err != nil { + if err := internal.RPCClient.Node.LogLevelSet(c.Context(), params[0], params[1]); err != nil { return err } } @@ -77,13 +67,8 @@ var verifyCmd = &cobra.Command{ Short: "Returns the permissions assigned to the given token.", RunE: func(c *cobra.Command, args []string) error { - client, err := rpcClient(c.Context()) - if err != nil { - return err - } - - perms, err := client.Node.AuthVerify(c.Context(), args[0]) - return printOutput(perms, err, nil) + perms, err := internal.RPCClient.Node.AuthVerify(c.Context(), args[0]) + return internal.PrintOutput(perms, err, nil) }, } @@ -92,17 +77,12 @@ var authCmd = &cobra.Command{ Args: cobra.MinimumNArgs(1), Short: "Signs and returns a new token with the given permissions.", RunE: func(c *cobra.Command, args []string) error { - client, err := rpcClient(c.Context()) - if err != nil { - return err - } - perms := make([]auth.Permission, len(args)) for i, p := range args { perms[i] = (auth.Permission)(p) } - result, err := client.Node.AuthNew(c.Context(), perms) - return printOutput(result, err, nil) + result, err := internal.RPCClient.Node.AuthNew(c.Context(), perms) + return internal.PrintOutput(result, err, nil) }, } diff --git a/cmd/celestia/internal/init.go b/cmd/celestia/internal/init.go new file mode 100644 index 0000000000..787c3d61de --- /dev/null +++ b/cmd/celestia/internal/init.go @@ -0,0 +1,33 @@ +package internal + +import ( + "os" + + "github.com/spf13/cobra" + + "github.com/celestiaorg/celestia-node/api/rpc/client" +) + +const authEnvKey = "CELESTIA_NODE_AUTH_TOKEN" + +var ( + RPCClient *client.Client + RequestURL string +) + +// InitClient creates the rpc client under the given at the *RequestURL* address +func InitClient(cmd *cobra.Command, _ []string) error { + var err error + RPCClient, err = client.NewClient(cmd.Context(), RequestURL, os.Getenv(authEnvKey)) + if err != nil { + return err + } + return nil +} + +// CloseClient closes the connection with the rpc client. +func CloseClient(_ *cobra.Command, _ []string) { + if RPCClient != nil { + RPCClient.Close() + } +} diff --git a/cmd/celestia/blob.go b/cmd/celestia/internal/rpc/blob.go similarity index 76% rename from cmd/celestia/blob.go rename to cmd/celestia/internal/rpc/blob.go index 9550f2c13a..236cc75b67 100644 --- a/cmd/celestia/blob.go +++ b/cmd/celestia/internal/rpc/blob.go @@ -1,4 +1,4 @@ -package main +package rpc import ( "encoding/base64" @@ -9,6 +9,7 @@ import ( "github.com/spf13/cobra" "github.com/celestiaorg/celestia-node/blob" + "github.com/celestiaorg/celestia-node/cmd/celestia/internal" "github.com/celestiaorg/celestia-node/share" ) @@ -20,7 +21,13 @@ var ( ) func init() { - blobCmd.AddCommand(getCmd, getAllCmd, submitCmd, getProofCmd) + BlobCmd.PersistentFlags().StringVar( + &internal.RequestURL, + "url", + "http://localhost:26658", + "Request URL", + ) + BlobCmd.AddCommand(getCmd, getAllCmd, submitCmd, getProofCmd) getCmd.PersistentFlags().BoolVar( &base64Flag, @@ -28,6 +35,7 @@ func init() { false, "printed blob's data a base64 string", ) + getAllCmd.PersistentFlags().BoolVar( &base64Flag, "base64", @@ -50,10 +58,12 @@ func init() { ) } -var blobCmd = &cobra.Command{ - Use: "blob [command]", - Short: "Allows to interact with the Blob Service via JSON-RPC", - Args: cobra.NoArgs, +var BlobCmd = &cobra.Command{ + Use: "blob [command]", + Short: "Allows to interact with the Blob Service via JSON-RPC", + Args: cobra.NoArgs, + PersistentPreRunE: internal.InitClient, + PersistentPostRun: internal.CloseClient, } var getCmd = &cobra.Command{ @@ -61,17 +71,12 @@ var getCmd = &cobra.Command{ 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]) + namespace, err := internal.ParseV0Namespace(args[1]) if err != nil { return fmt.Errorf("error parsing a namespace:%v", err) } @@ -81,13 +86,13 @@ var getCmd = &cobra.Command{ return fmt.Errorf("error parsing a commitment:%v", err) } - blob, err := client.Blob.Get(cmd.Context(), height, namespace, commitment) + blob, err := internal.RPCClient.Blob.Get(cmd.Context(), height, namespace, commitment) formatter := formatData if base64Flag || err != nil { formatter = nil } - return printOutput(blob, err, formatter) + return internal.PrintOutput(blob, err, formatter) }, } @@ -96,28 +101,23 @@ var getAllCmd = &cobra.Command{ 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]) + namespace, err := internal.ParseV0Namespace(args[1]) if err != nil { return fmt.Errorf("error parsing a namespace:%v", err) } - blobs, err := client.Blob.GetAll(cmd.Context(), height, []share.Namespace{namespace}) + blobs, err := internal.RPCClient.Blob.GetAll(cmd.Context(), height, []share.Namespace{namespace}) formatter := formatData if base64Flag || err != nil { formatter = nil } - return printOutput(blobs, err, formatter) + return internal.PrintOutput(blobs, err, formatter) }, } @@ -126,12 +126,7 @@ var submitCmd = &cobra.Command{ 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]) + namespace, err := internal.ParseV0Namespace(args[0]) if err != nil { return fmt.Errorf("error parsing a namespace:%v", err) } @@ -141,7 +136,7 @@ var submitCmd = &cobra.Command{ return fmt.Errorf("error creating a blob:%v", err) } - height, err := client.Blob.Submit( + height, err := internal.RPCClient.Blob.Submit( cmd.Context(), []*blob.Blob{parsedBlob}, &blob.SubmitOptions{Fee: fee, GasLimit: gasLimit}, @@ -154,7 +149,7 @@ var submitCmd = &cobra.Command{ Height: height, Commitment: parsedBlob.Commitment, } - return printOutput(response, err, nil) + return internal.PrintOutput(response, err, nil) }, } @@ -163,17 +158,12 @@ var getProofCmd = &cobra.Command{ 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]) + namespace, err := internal.ParseV0Namespace(args[1]) if err != nil { return fmt.Errorf("error parsing a namespace:%v", err) } @@ -183,8 +173,8 @@ var getProofCmd = &cobra.Command{ return fmt.Errorf("error parsing a commitment:%v", err) } - proof, err := client.Blob.GetProof(cmd.Context(), height, namespace, commitment) - return printOutput(proof, err, nil) + proof, err := internal.RPCClient.Blob.GetProof(cmd.Context(), height, namespace, commitment) + return internal.PrintOutput(proof, err, nil) }, } diff --git a/cmd/celestia/internal/rpc/das.go b/cmd/celestia/internal/rpc/das.go new file mode 100644 index 0000000000..f0f67a6bce --- /dev/null +++ b/cmd/celestia/internal/rpc/das.go @@ -0,0 +1,36 @@ +package rpc + +import ( + "github.com/spf13/cobra" + + "github.com/celestiaorg/celestia-node/cmd/celestia/internal" +) + +func init() { + DASCmd.AddCommand(samplingStatsCmd) + + DASCmd.PersistentFlags().StringVar( + &internal.RequestURL, + "url", + "http://localhost:26658", + "Request URL", + ) +} + +var DASCmd = &cobra.Command{ + Use: "das [command]", + Short: "Allows to interact with the Daser via JSON-RPC", + Args: cobra.NoArgs, + PersistentPreRunE: internal.InitClient, + PersistentPostRun: internal.CloseClient, +} + +var samplingStatsCmd = &cobra.Command{ + Use: "sampling-stats", + Short: "Returns the current statistics over the DA sampling process", + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, args []string) error { + stats, err := internal.RPCClient.DAS.SamplingStats(cmd.Context()) + return internal.PrintOutput(stats, err, nil) + }, +} diff --git a/cmd/celestia/header.go b/cmd/celestia/internal/rpc/header.go similarity index 58% rename from cmd/celestia/header.go rename to cmd/celestia/internal/rpc/header.go index 1d8dc6d01b..3253fabecd 100644 --- a/cmd/celestia/header.go +++ b/cmd/celestia/internal/rpc/header.go @@ -1,4 +1,4 @@ -package main +package rpc import ( "encoding/hex" @@ -6,22 +6,33 @@ import ( "strconv" "github.com/spf13/cobra" + + "github.com/celestiaorg/celestia-node/cmd/celestia/internal" ) func init() { - headerCmd.AddCommand( + HeaderCmd.AddCommand( localHeadCmd, networkHeadCmd, getByHashCmd, getByHeightCmd, syncStateCmd, ) + + HeaderCmd.PersistentFlags().StringVar( + &internal.RequestURL, + "url", + "http://localhost:26658", + "Request URL", + ) } -var headerCmd = &cobra.Command{ - Use: "header [command]", - Short: "Allows interaction with the Header Module via JSON-RPC", - Args: cobra.NoArgs, +var HeaderCmd = &cobra.Command{ + Use: "header [command]", + Short: "Allows interaction with the Header Module via JSON-RPC", + Args: cobra.NoArgs, + PersistentPreRunE: internal.InitClient, + PersistentPostRun: internal.CloseClient, } var localHeadCmd = &cobra.Command{ @@ -29,13 +40,8 @@ var localHeadCmd = &cobra.Command{ Short: "Returns the ExtendedHeader from the chain head.", Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { - client, err := rpcClient(cmd.Context()) - if err != nil { - return err - } - - header, err := client.Header.LocalHead(cmd.Context()) - return printOutput(header, err, nil) + header, err := internal.RPCClient.Header.LocalHead(cmd.Context()) + return internal.PrintOutput(header, err, nil) }, } @@ -44,13 +50,8 @@ var networkHeadCmd = &cobra.Command{ Short: "Provides the Syncer's view of the current network head.", Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { - client, err := rpcClient(cmd.Context()) - if err != nil { - return err - } - - header, err := client.Header.NetworkHead(cmd.Context()) - return printOutput(header, err, nil) + header, err := internal.RPCClient.Header.NetworkHead(cmd.Context()) + return internal.PrintOutput(header, err, nil) }, } @@ -59,17 +60,12 @@ var getByHashCmd = &cobra.Command{ Short: "Returns the header of the given hash from the node's header store.", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - client, err := rpcClient(cmd.Context()) - if err != nil { - return err - } - hash, err := hex.DecodeString(args[0]) if err != nil { return fmt.Errorf("error decoding a hash: expected a hex encoded string:%v", err) } - header, err := client.Header.GetByHash(cmd.Context(), hash) - return printOutput(header, err, nil) + header, err := internal.RPCClient.Header.GetByHash(cmd.Context(), hash) + return internal.PrintOutput(header, err, nil) }, } @@ -78,18 +74,13 @@ var getByHeightCmd = &cobra.Command{ Short: "Returns the ExtendedHeader at the given height if it is currently available.", Args: cobra.ExactArgs(1), 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) } - header, err := client.Header.GetByHeight(cmd.Context(), height) - return printOutput(header, err, nil) + header, err := internal.RPCClient.Header.GetByHeight(cmd.Context(), height) + return internal.PrintOutput(header, err, nil) }, } @@ -98,12 +89,7 @@ var syncStateCmd = &cobra.Command{ Short: "Returns the current state of the header Syncer.", Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { - client, err := rpcClient(cmd.Context()) - if err != nil { - return err - } - - header, err := client.Header.SyncState(cmd.Context()) - return printOutput(header, err, nil) + header, err := internal.RPCClient.Header.SyncState(cmd.Context()) + return internal.PrintOutput(header, err, nil) }, } diff --git a/cmd/celestia/p2p.go b/cmd/celestia/internal/rpc/p2p.go similarity index 73% rename from cmd/celestia/p2p.go rename to cmd/celestia/internal/rpc/p2p.go index d0b6549f6c..41cf34f1a4 100644 --- a/cmd/celestia/p2p.go +++ b/cmd/celestia/internal/rpc/p2p.go @@ -1,4 +1,4 @@ -package main +package rpc import ( "github.com/libp2p/go-libp2p/core/metrics" @@ -7,6 +7,8 @@ import ( "github.com/libp2p/go-libp2p/core/protocol" ma2 "github.com/multiformats/go-multiaddr" "github.com/spf13/cobra" + + "github.com/celestiaorg/celestia-node/cmd/celestia/internal" ) type peerInfo struct { @@ -15,7 +17,7 @@ type peerInfo struct { } func init() { - p2pCmd.AddCommand(infoCmd, + P2PCmd.AddCommand(infoCmd, peersCmd, peerInfoCmd, connectCmd, @@ -33,12 +35,21 @@ func init() { bandwidthForProtocolCmd, pubsubPeersCmd, ) + + P2PCmd.PersistentFlags().StringVar( + &internal.RequestURL, + "url", + "http://localhost:26658", + "Request URL", + ) } -var p2pCmd = &cobra.Command{ - Use: "p2p [command]", - Short: "Allows interaction with the P2P Module via JSON-RPC", - Args: cobra.NoArgs, +var P2PCmd = &cobra.Command{ + Use: "p2p [command]", + Short: "Allows interaction with the P2P Module via JSON-RPC", + Args: cobra.NoArgs, + PersistentPreRunE: internal.InitClient, + PersistentPostRun: internal.CloseClient, } var infoCmd = &cobra.Command{ @@ -46,12 +57,7 @@ var infoCmd = &cobra.Command{ Short: "Gets the node's peer info (peer id and multiaddresses)", Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { - client, err := rpcClient(cmd.Context()) - if err != nil { - return err - } - - info, err := client.P2P.Info(cmd.Context()) + info, err := internal.RPCClient.P2P.Info(cmd.Context()) formatter := func(data interface{}) interface{} { peerAdd := data.(peer.AddrInfo) @@ -65,7 +71,7 @@ var infoCmd = &cobra.Command{ PeerAddr: ma, } } - return printOutput(info, err, formatter) + return internal.PrintOutput(info, err, formatter) }, } @@ -74,12 +80,7 @@ var peersCmd = &cobra.Command{ Short: "Lists the peers we are connected to", Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { - client, err := rpcClient(cmd.Context()) - if err != nil { - return err - } - - result, err := client.P2P.Peers(cmd.Context()) + result, err := internal.RPCClient.P2P.Peers(cmd.Context()) peers := make([]string, len(result)) for i, peer := range result { peers[i] = peer.String() @@ -93,7 +94,7 @@ var peersCmd = &cobra.Command{ Peers: conPeers, } } - return printOutput(peers, err, formatter) + return internal.PrintOutput(peers, err, formatter) }, } @@ -102,16 +103,11 @@ var peerInfoCmd = &cobra.Command{ Short: "Gets PeerInfo for a given peer", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - client, err := rpcClient(cmd.Context()) - if err != nil { - return err - } - pid, err := peer.Decode(args[0]) if err != nil { return err } - info, err := client.P2P.PeerInfo(cmd.Context(), pid) + info, err := internal.RPCClient.P2P.PeerInfo(cmd.Context(), pid) formatter := func(data interface{}) interface{} { peerAdd := data.(peer.AddrInfo) ma := make([]string, len(info.Addrs)) @@ -124,7 +120,7 @@ var peerInfoCmd = &cobra.Command{ PeerAddr: ma, } } - return printOutput(info, err, formatter) + return internal.PrintOutput(info, err, formatter) }, } @@ -133,11 +129,6 @@ var connectCmd = &cobra.Command{ Short: "Establishes a connection with the given peer", Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { - client, err := rpcClient(cmd.Context()) - if err != nil { - return err - } - pid, err := peer.Decode(args[0]) if err != nil { return err @@ -153,9 +144,9 @@ var connectCmd = &cobra.Command{ Addrs: []ma2.Multiaddr{ma}, } - err = client.P2P.Connect(cmd.Context(), peerInfo) + err = internal.RPCClient.P2P.Connect(cmd.Context(), peerInfo) if err != nil { - return printOutput(nil, err, nil) + return internal.PrintOutput(nil, err, nil) } return connectednessCmd.RunE(cmd, args) }, @@ -166,19 +157,14 @@ var closePeerCmd = &cobra.Command{ Short: "Closes the connection with the given peer", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - client, err := rpcClient(cmd.Context()) - if err != nil { - return err - } - pid, err := peer.Decode(args[0]) if err != nil { return err } - err = client.P2P.ClosePeer(cmd.Context(), pid) + err = internal.RPCClient.P2P.ClosePeer(cmd.Context(), pid) if err != nil { - return printOutput(nil, err, nil) + return internal.PrintOutput(nil, err, nil) } return connectednessCmd.RunE(cmd, args) }, @@ -189,17 +175,12 @@ var connectednessCmd = &cobra.Command{ Short: "Checks the connection state between current and given peers", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - client, err := rpcClient(cmd.Context()) - if err != nil { - return err - } - pid, err := peer.Decode(args[0]) if err != nil { return err } - con, err := client.P2P.Connectedness(cmd.Context(), pid) + con, err := internal.RPCClient.P2P.Connectedness(cmd.Context(), pid) formatter := func(data interface{}) interface{} { conn := data.(network.Connectedness) @@ -209,7 +190,7 @@ var connectednessCmd = &cobra.Command{ ConnectionState: conn.String(), } } - return printOutput(con, err, formatter) + return internal.PrintOutput(con, err, formatter) }, } @@ -218,12 +199,7 @@ var natStatusCmd = &cobra.Command{ Short: "Gets the currrent NAT status", Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { - client, err := rpcClient(cmd.Context()) - if err != nil { - return err - } - - r, err := client.P2P.NATStatus(cmd.Context()) + r, err := internal.RPCClient.P2P.NATStatus(cmd.Context()) formatter := func(data interface{}) interface{} { rr := data.(network.Reachability) @@ -233,7 +209,7 @@ var natStatusCmd = &cobra.Command{ Reachability: rr.String(), } } - return printOutput(r, err, formatter) + return internal.PrintOutput(r, err, formatter) }, } @@ -242,17 +218,12 @@ var blockPeerCmd = &cobra.Command{ Short: "Blocks the given peer", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - client, err := rpcClient(cmd.Context()) - if err != nil { - return err - } - pid, err := peer.Decode(args[0]) if err != nil { return err } - err = client.P2P.BlockPeer(cmd.Context(), pid) + err = internal.RPCClient.P2P.BlockPeer(cmd.Context(), pid) formatter := func(data interface{}) interface{} { err, ok := data.(error) @@ -270,7 +241,7 @@ var blockPeerCmd = &cobra.Command{ Reason: err, } } - return printOutput(err, nil, formatter) + return internal.PrintOutput(err, nil, formatter) }, } @@ -279,17 +250,12 @@ var unblockPeerCmd = &cobra.Command{ Short: "Unblocks the given peer", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - client, err := rpcClient(cmd.Context()) - if err != nil { - return err - } - pid, err := peer.Decode(args[0]) if err != nil { return err } - err = client.P2P.UnblockPeer(cmd.Context(), pid) + err = internal.RPCClient.P2P.UnblockPeer(cmd.Context(), pid) formatter := func(data interface{}) interface{} { err, ok := data.(error) @@ -308,7 +274,7 @@ var unblockPeerCmd = &cobra.Command{ Reason: err, } } - return printOutput(err, nil, formatter) + return internal.PrintOutput(err, nil, formatter) }, } @@ -317,12 +283,7 @@ var blockedPeersCmd = &cobra.Command{ Short: "Lists the node's blocked peers", Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { - client, err := rpcClient(cmd.Context()) - if err != nil { - return err - } - - list, err := client.P2P.ListBlockedPeers(cmd.Context()) + list, err := internal.RPCClient.P2P.ListBlockedPeers(cmd.Context()) pids := make([]string, len(list)) for i, peer := range list { @@ -337,7 +298,7 @@ var blockedPeersCmd = &cobra.Command{ Peers: peers, } } - return printOutput(pids, err, formatter) + return internal.PrintOutput(pids, err, formatter) }, } @@ -346,17 +307,12 @@ var protectCmd = &cobra.Command{ Short: "Protects the given peer from being pruned by the given tag", Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { - client, err := rpcClient(cmd.Context()) - if err != nil { - return err - } - pid, err := peer.Decode(args[0]) if err != nil { return err } - err = client.P2P.Protect(cmd.Context(), pid, args[1]) + err = internal.RPCClient.P2P.Protect(cmd.Context(), pid, args[1]) formatter := func(data interface{}) interface{} { err, ok := data.(error) @@ -374,27 +330,23 @@ var protectCmd = &cobra.Command{ Reason: err, } } - return printOutput(err, nil, formatter) + return internal.PrintOutput(err, nil, formatter) }, } var unprotectCmd = &cobra.Command{ - Use: "unprotect [peer.ID, tag]", - Short: "Removes a protection that may have been placed on a peer, under the specified tag." + + Use: "unprotect [peer.ID, tag]", + Short: "Removes protection from the given peer.", + Long: "Removes a protection that may have been placed on a peer, under the specified tag." + "The return value indicates whether the peer continues to be protected after this call, by way of a different tag", Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { - client, err := rpcClient(cmd.Context()) - if err != nil { - return err - } - pid, err := peer.Decode(args[0]) if err != nil { return err } - _, err = client.P2P.Unprotect(cmd.Context(), pid, args[1]) + _, err = internal.RPCClient.P2P.Unprotect(cmd.Context(), pid, args[1]) formatter := func(data interface{}) interface{} { err, ok := data.(error) @@ -412,7 +364,7 @@ var unprotectCmd = &cobra.Command{ Reason: err, } } - return printOutput(err, nil, formatter) + return internal.PrintOutput(err, nil, formatter) }, } @@ -421,18 +373,13 @@ var protectedCmd = &cobra.Command{ Short: "Ensures that a given peer is protected under a specific tag", Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { - client, err := rpcClient(cmd.Context()) - if err != nil { - return err - } - pid, err := peer.Decode(args[0]) if err != nil { return err } - result, err := client.P2P.IsProtected(cmd.Context(), pid, args[1]) - return printOutput(result, err, nil) + result, err := internal.RPCClient.P2P.IsProtected(cmd.Context(), pid, args[1]) + return internal.PrintOutput(result, err, nil) }, } @@ -444,17 +391,13 @@ type bandwidthStats struct { } var bandwidthStatsCmd = &cobra.Command{ - Use: "bandwidth-stats", - Short: "Get stats struct with bandwidth metrics for all data sent/" + + Use: "bandwidth-stats", + Short: "Provides metrics for current peer.", + Long: "Get stats struct with bandwidth metrics for all data sent/" + "received by the local peer, regardless of protocol or remote peer IDs", Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { - client, err := rpcClient(cmd.Context()) - if err != nil { - return err - } - - result, err := client.P2P.BandwidthStats(cmd.Context()) + result, err := internal.RPCClient.P2P.BandwidthStats(cmd.Context()) formatter := func(data interface{}) interface{} { stats := data.(metrics.Stats) @@ -465,7 +408,7 @@ var bandwidthStatsCmd = &cobra.Command{ RateOut: stats.RateOut, } } - return printOutput(result, err, formatter) + return internal.PrintOutput(result, err, formatter) }, } @@ -474,17 +417,12 @@ var peerBandwidthCmd = &cobra.Command{ Short: "Gets stats struct with bandwidth metrics associated with the given peer.ID", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - client, err := rpcClient(cmd.Context()) - if err != nil { - return err - } - pid, err := peer.Decode(args[0]) if err != nil { return err } - result, err := client.P2P.BandwidthForPeer(cmd.Context(), pid) + result, err := internal.RPCClient.P2P.BandwidthForPeer(cmd.Context(), pid) formatter := func(data interface{}) interface{} { stats := data.(metrics.Stats) @@ -495,7 +433,7 @@ var peerBandwidthCmd = &cobra.Command{ RateOut: stats.RateOut, } } - return printOutput(result, err, formatter) + return internal.PrintOutput(result, err, formatter) }, } @@ -504,12 +442,7 @@ var bandwidthForProtocolCmd = &cobra.Command{ Short: "Gets stats struct with bandwidth metrics associated with the given protocol.ID", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - client, err := rpcClient(cmd.Context()) - if err != nil { - return err - } - - result, err := client.P2P.BandwidthForProtocol(cmd.Context(), protocol.ID(args[0])) + result, err := internal.RPCClient.P2P.BandwidthForProtocol(cmd.Context(), protocol.ID(args[0])) formatter := func(data interface{}) interface{} { stats := data.(metrics.Stats) @@ -520,7 +453,7 @@ var bandwidthForProtocolCmd = &cobra.Command{ RateOut: stats.RateOut, } } - return printOutput(result, err, formatter) + return internal.PrintOutput(result, err, formatter) }, } @@ -529,12 +462,7 @@ var pubsubPeersCmd = &cobra.Command{ Short: "Lists the peers we are connected to in the given topic", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - client, err := rpcClient(cmd.Context()) - if err != nil { - return err - } - - result, err := client.P2P.PubSubPeers(cmd.Context(), args[0]) + result, err := internal.RPCClient.P2P.PubSubPeers(cmd.Context(), args[0]) peers := make([]string, len(result)) for i, peer := range result { @@ -549,6 +477,6 @@ var pubsubPeersCmd = &cobra.Command{ Peers: conPeers, } } - return printOutput(peers, err, formatter) + return internal.PrintOutput(peers, err, formatter) }, } diff --git a/cmd/celestia/share.go b/cmd/celestia/internal/rpc/share.go similarity index 70% rename from cmd/celestia/share.go rename to cmd/celestia/internal/rpc/share.go index 2f84871d80..fa6d748c78 100644 --- a/cmd/celestia/share.go +++ b/cmd/celestia/internal/rpc/share.go @@ -1,4 +1,4 @@ -package main +package rpc import ( "encoding/hex" @@ -9,11 +9,19 @@ import ( "github.com/celestiaorg/celestia-app/pkg/da" + "github.com/celestiaorg/celestia-node/cmd/celestia/internal" "github.com/celestiaorg/celestia-node/share" ) func init() { - shareCmd.AddCommand( + ShareCmd.PersistentFlags().StringVar( + &internal.RequestURL, + "url", + "http://localhost:26658", + "Request URL", + ) + + ShareCmd.AddCommand( sharesAvailableCmd, probabilityOfAvailabilityCmd, getSharesByNamespaceCmd, @@ -22,10 +30,12 @@ func init() { ) } -var shareCmd = &cobra.Command{ - Use: "share [command]", - Short: "Allows interaction with the Share Module via JSON-RPC", - Args: cobra.NoArgs, +var ShareCmd = &cobra.Command{ + Use: "share [command]", + Short: "Allows interaction with the Share Module via JSON-RPC", + Args: cobra.NoArgs, + PersistentPreRunE: internal.InitClient, + PersistentPostRun: internal.CloseClient, } var sharesAvailableCmd = &cobra.Command{ @@ -33,11 +43,6 @@ var sharesAvailableCmd = &cobra.Command{ Short: "Subjectively validates if Shares committed to the given Root are available on the Network.", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - client, err := rpcClient(cmd.Context()) - if err != nil { - return err - } - raw, err := parseJSON(args[0]) if err != nil { return err @@ -49,7 +54,7 @@ var sharesAvailableCmd = &cobra.Command{ return err } - err = client.Share.SharesAvailable(cmd.Context(), &root) + err = internal.RPCClient.Share.SharesAvailable(cmd.Context(), &root) formatter := func(data interface{}) interface{} { err, ok := data.(error) available := false @@ -66,7 +71,7 @@ var sharesAvailableCmd = &cobra.Command{ Reason: err, } } - return printOutput(err, nil, formatter) + return internal.PrintOutput(err, nil, formatter) }, } @@ -75,13 +80,8 @@ var probabilityOfAvailabilityCmd = &cobra.Command{ Short: "Calculates the probability of the data square being available based on the number of samples collected.", Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { - client, err := rpcClient(cmd.Context()) - if err != nil { - return err - } - - prob := client.Share.ProbabilityOfAvailability(cmd.Context()) - return printOutput(prob, nil, nil) + prob := internal.RPCClient.Share.ProbabilityOfAvailability(cmd.Context()) + return internal.PrintOutput(prob, nil, nil) }, } @@ -90,11 +90,6 @@ var getSharesByNamespaceCmd = &cobra.Command{ Short: "Gets all shares from an EDS within the given namespace.", Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { - client, err := rpcClient(cmd.Context()) - if err != nil { - return err - } - raw, err := parseJSON(args[0]) if err != nil { return err @@ -106,13 +101,13 @@ var getSharesByNamespaceCmd = &cobra.Command{ return err } - ns, err := parseV0Namespace(args[1]) + ns, err := internal.ParseV0Namespace(args[1]) if err != nil { return err } - shares, err := client.Share.GetSharesByNamespace(cmd.Context(), &root, ns) - return printOutput(shares, err, nil) + shares, err := internal.RPCClient.Share.GetSharesByNamespace(cmd.Context(), &root, ns) + return internal.PrintOutput(shares, err, nil) }, } @@ -121,11 +116,6 @@ var getShare = &cobra.Command{ Short: "Gets a Share by coordinates in EDS.", Args: cobra.ExactArgs(3), RunE: func(cmd *cobra.Command, args []string) error { - client, err := rpcClient(cmd.Context()) - if err != nil { - return err - } - raw, err := parseJSON(args[0]) if err != nil { return err @@ -147,7 +137,7 @@ var getShare = &cobra.Command{ return err } - s, err := client.Share.GetShare(cmd.Context(), &root, int(row), int(col)) + s, err := internal.RPCClient.Share.GetShare(cmd.Context(), &root, int(row), int(col)) formatter := func(data interface{}) interface{} { sh, ok := data.(share.Share) @@ -165,7 +155,7 @@ var getShare = &cobra.Command{ Data: share.GetData(sh), } } - return printOutput(s, err, formatter) + return internal.PrintOutput(s, err, formatter) }, } @@ -174,11 +164,6 @@ var getEDS = &cobra.Command{ Short: "Gets the full EDS identified by the given root", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - client, err := rpcClient(cmd.Context()) - if err != nil { - return err - } - raw, err := parseJSON(args[0]) if err != nil { return err @@ -190,7 +175,13 @@ var getEDS = &cobra.Command{ return err } - shares, err := client.Share.GetEDS(cmd.Context(), &root) - return printOutput(shares, err, nil) + shares, err := internal.RPCClient.Share.GetEDS(cmd.Context(), &root) + return internal.PrintOutput(shares, err, nil) }, } + +func parseJSON(param string) (json.RawMessage, error) { + var raw json.RawMessage + err := json.Unmarshal([]byte(param), &raw) + return raw, err +} diff --git a/cmd/celestia/state.go b/cmd/celestia/internal/rpc/state.go similarity index 76% rename from cmd/celestia/state.go rename to cmd/celestia/internal/rpc/state.go index 2f3d1e138a..24ed96a24c 100644 --- a/cmd/celestia/state.go +++ b/cmd/celestia/internal/rpc/state.go @@ -1,4 +1,4 @@ -package main +package rpc import ( "fmt" @@ -9,11 +9,12 @@ import ( "github.com/spf13/cobra" "github.com/celestiaorg/celestia-node/blob" + "github.com/celestiaorg/celestia-node/cmd/celestia/internal" "github.com/celestiaorg/celestia-node/state" ) func init() { - stateCmd.AddCommand( + StateCmd.AddCommand( accountAddressCmd, balanceCmd, balanceForAddressCmd, @@ -28,12 +29,21 @@ func init() { queryUnbondingCmd, queryRedelegationCmd, ) + + StateCmd.PersistentFlags().StringVar( + &internal.RequestURL, + "url", + "http://localhost:26658", + "Request URL", + ) } -var stateCmd = &cobra.Command{ - Use: "state [command]", - Short: "Allows interaction with the State Module via JSON-RPC", - Args: cobra.NoArgs, +var StateCmd = &cobra.Command{ + Use: "state [command]", + Short: "Allows interaction with the State Module via JSON-RPC", + Args: cobra.NoArgs, + PersistentPreRunE: internal.InitClient, + PersistentPostRun: internal.CloseClient, } var accountAddressCmd = &cobra.Command{ @@ -41,13 +51,8 @@ var accountAddressCmd = &cobra.Command{ Short: "Retrieves the address of the node's account/signer.", Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { - client, err := rpcClient(cmd.Context()) - if err != nil { - return err - } - - address, err := client.State.AccountAddress(cmd.Context()) - return printOutput(address, err, nil) + address, err := internal.RPCClient.State.AccountAddress(cmd.Context()) + return internal.PrintOutput(address, err, nil) }, } @@ -57,13 +62,8 @@ var balanceCmd = &cobra.Command{ "the corresponding block's AppHash.", Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { - client, err := rpcClient(cmd.Context()) - if err != nil { - return err - } - - balance, err := client.State.Balance(cmd.Context()) - return printOutput(balance, err, nil) + balance, err := internal.RPCClient.State.Balance(cmd.Context()) + return internal.PrintOutput(balance, err, nil) }, } @@ -73,18 +73,13 @@ var balanceForAddressCmd = &cobra.Command{ "the corresponding block's AppHash.", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - client, err := rpcClient(cmd.Context()) - if err != nil { - return err - } - addr, err := parseAddressFromString(args[0]) if err != nil { return fmt.Errorf("error parsing an address:%v", err) } - balance, err := client.State.BalanceForAddress(cmd.Context(), addr) - return printOutput(balance, err, nil) + balance, err := internal.RPCClient.State.BalanceForAddress(cmd.Context(), addr) + return internal.PrintOutput(balance, err, nil) }, } @@ -93,11 +88,6 @@ var transferCmd = &cobra.Command{ Short: "Sends the given amount of coins from default wallet of the node to the given account address.", Args: cobra.ExactArgs(4), RunE: func(cmd *cobra.Command, args []string) error { - client, err := rpcClient(cmd.Context()) - if err != nil { - return err - } - addr, err := parseAddressFromString(args[0]) if err != nil { return fmt.Errorf("error parsing an address:%v", err) @@ -116,13 +106,13 @@ var transferCmd = &cobra.Command{ return fmt.Errorf("error parsing a gas limit:%v", err) } - txResponse, err := client.State.Transfer( + txResponse, err := internal.RPCClient.State.Transfer( cmd.Context(), addr.Address.(state.AccAddress), math.NewInt(amount), math.NewInt(fee), gasLimit, ) - return printOutput(txResponse, err, nil) + return internal.PrintOutput(txResponse, err, nil) }, } @@ -131,20 +121,15 @@ var submitTxCmd = &cobra.Command{ Short: "Submits the given transaction/message to the Celestia network and blocks until the tx is included in a block.", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - client, err := rpcClient(cmd.Context()) - if err != nil { - return err - } - - rawTx, err := decodeToBytes(args[0]) + rawTx, err := internal.DecodeToBytes(args[0]) if err != nil { return fmt.Errorf("failed to decode tx: %v", err) } - txResponse, err := client.State.SubmitTx( + txResponse, err := internal.RPCClient.State.SubmitTx( cmd.Context(), rawTx, ) - return printOutput(txResponse, err, nil) + return internal.PrintOutput(txResponse, err, nil) }, } @@ -153,11 +138,7 @@ var submitPFBCmd = &cobra.Command{ Short: "Allows to submit PFBs", Args: cobra.ExactArgs(4), RunE: func(cmd *cobra.Command, args []string) error { - client, err := rpcClient(cmd.Context()) - if err != nil { - return err - } - namespace, err := parseV0Namespace(args[0]) + namespace, err := internal.ParseV0Namespace(args[0]) if err != nil { return fmt.Errorf("error parsing a namespace:%v", err) } @@ -177,8 +158,13 @@ var submitPFBCmd = &cobra.Command{ return fmt.Errorf("error creating a blob:%v", err) } - txResp, err := client.State.SubmitPayForBlob(cmd.Context(), types.NewInt(fee), gasLimit, []*blob.Blob{parsedBlob}) - return printOutput(txResp, err, nil) + txResp, err := internal.RPCClient.State.SubmitPayForBlob( + cmd.Context(), + types.NewInt(fee), + gasLimit, + []*blob.Blob{parsedBlob}, + ) + return internal.PrintOutput(txResp, err, nil) }, } @@ -187,10 +173,6 @@ var cancelUnbondingDelegationCmd = &cobra.Command{ Short: "Cancels a user's pending undelegation from a validator.", Args: cobra.ExactArgs(5), RunE: func(cmd *cobra.Command, args []string) error { - client, err := rpcClient(cmd.Context()) - if err != nil { - return err - } addr, err := parseAddressFromString(args[0]) if err != nil { return fmt.Errorf("error parsing an address:%v", err) @@ -216,7 +198,7 @@ var cancelUnbondingDelegationCmd = &cobra.Command{ return fmt.Errorf("error parsing a gas limit:%v", err) } - txResponse, err := client.State.CancelUnbondingDelegation( + txResponse, err := internal.RPCClient.State.CancelUnbondingDelegation( cmd.Context(), addr.Address.(state.ValAddress), math.NewInt(amount), @@ -224,7 +206,7 @@ var cancelUnbondingDelegationCmd = &cobra.Command{ math.NewInt(fee), gasLimit, ) - return printOutput(txResponse, err, nil) + return internal.PrintOutput(txResponse, err, nil) }, } @@ -233,10 +215,6 @@ var beginRedelegateCmd = &cobra.Command{ Short: "Sends a user's delegated tokens to a new validator for redelegation", Args: cobra.ExactArgs(5), RunE: func(cmd *cobra.Command, args []string) error { - client, err := rpcClient(cmd.Context()) - if err != nil { - return err - } srcAddr, err := parseAddressFromString(args[0]) if err != nil { return fmt.Errorf("error parsing an address:%v", err) @@ -261,7 +239,7 @@ var beginRedelegateCmd = &cobra.Command{ return fmt.Errorf("error parsing a gas limit:%v", err) } - txResponse, err := client.State.BeginRedelegate( + txResponse, err := internal.RPCClient.State.BeginRedelegate( cmd.Context(), srcAddr.Address.(state.ValAddress), dstAddr.Address.(state.ValAddress), @@ -269,7 +247,7 @@ var beginRedelegateCmd = &cobra.Command{ math.NewInt(fee), gasLimit, ) - return printOutput(txResponse, err, nil) + return internal.PrintOutput(txResponse, err, nil) }, } @@ -278,10 +256,6 @@ var undelegateCmd = &cobra.Command{ Short: "Undelegates a user's delegated tokens, unbonding them from the current validator.", Args: cobra.ExactArgs(4), RunE: func(cmd *cobra.Command, args []string) error { - client, err := rpcClient(cmd.Context()) - if err != nil { - return err - } addr, err := parseAddressFromString(args[0]) if err != nil { return fmt.Errorf("error parsing an address:%v", err) @@ -300,14 +274,14 @@ var undelegateCmd = &cobra.Command{ return fmt.Errorf("error parsing a gas limit:%v", err) } - txResponse, err := client.State.Undelegate( + txResponse, err := internal.RPCClient.State.Undelegate( cmd.Context(), addr.Address.(state.ValAddress), math.NewInt(amount), math.NewInt(fee), gasLimit, ) - return printOutput(txResponse, err, nil) + return internal.PrintOutput(txResponse, err, nil) }, } @@ -316,11 +290,6 @@ var delegateCmd = &cobra.Command{ Short: "Sends a user's liquid tokens to a validator for delegation.", Args: cobra.ExactArgs(4), RunE: func(cmd *cobra.Command, args []string) error { - client, err := rpcClient(cmd.Context()) - if err != nil { - return err - } - addr, err := parseAddressFromString(args[0]) if err != nil { return fmt.Errorf("error parsing an address:%v", err) @@ -341,14 +310,14 @@ var delegateCmd = &cobra.Command{ return fmt.Errorf("error parsing a gas limit:%v", err) } - txResponse, err := client.State.Delegate( + txResponse, err := internal.RPCClient.State.Delegate( cmd.Context(), addr.Address.(state.ValAddress), math.NewInt(amount), math.NewInt(fee), gasLimit, ) - return printOutput(txResponse, err, nil) + return internal.PrintOutput(txResponse, err, nil) }, } @@ -357,20 +326,15 @@ var queryDelegationCmd = &cobra.Command{ Short: "Retrieves the delegation information between a delegator and a validator.", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - client, err := rpcClient(cmd.Context()) - if err != nil { - return err - } - addr, err := parseAddressFromString(args[0]) if err != nil { return fmt.Errorf("error parsing an address:%v", err) } - balance, err := client.State.QueryDelegation(cmd.Context(), addr.Address.(state.ValAddress)) + balance, err := internal.RPCClient.State.QueryDelegation(cmd.Context(), addr.Address.(state.ValAddress)) fmt.Println(balance) fmt.Println(err) - return printOutput(balance, err, nil) + return internal.PrintOutput(balance, err, nil) }, } @@ -379,18 +343,13 @@ var queryUnbondingCmd = &cobra.Command{ Short: "Retrieves the unbonding status between a delegator and a validator.", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - client, err := rpcClient(cmd.Context()) - if err != nil { - return err - } - addr, err := parseAddressFromString(args[0]) if err != nil { return fmt.Errorf("error parsing an address:%v", err) } - response, err := client.State.QueryUnbonding(cmd.Context(), addr.Address.(state.ValAddress)) - return printOutput(response, err, nil) + response, err := internal.RPCClient.State.QueryUnbonding(cmd.Context(), addr.Address.(state.ValAddress)) + return internal.PrintOutput(response, err, nil) }, } @@ -399,11 +358,6 @@ var queryRedelegationCmd = &cobra.Command{ Short: "Retrieves the status of the redelegations between a delegator and a validator.", Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { - client, err := rpcClient(cmd.Context()) - if err != nil { - return err - } - srcAddr, err := parseAddressFromString(args[0]) if err != nil { return fmt.Errorf("error parsing a src address:%v", err) @@ -414,11 +368,20 @@ var queryRedelegationCmd = &cobra.Command{ return fmt.Errorf("error parsing a dst address:%v", err) } - response, err := client.State.QueryRedelegations( + response, err := internal.RPCClient.State.QueryRedelegations( cmd.Context(), srcAddr.Address.(state.ValAddress), dstAddr.Address.(state.ValAddress), ) - return printOutput(response, err, nil) + return internal.PrintOutput(response, err, nil) }, } + +func parseAddressFromString(addrStr string) (state.Address, error) { + var address state.Address + err := address.UnmarshalJSON([]byte(addrStr)) + if err != nil { + return address, err + } + return address, nil +} diff --git a/cmd/celestia/internal/util.go b/cmd/celestia/internal/util.go new file mode 100644 index 0000000000..99c1a4415a --- /dev/null +++ b/cmd/celestia/internal/util.go @@ -0,0 +1,64 @@ +package internal + +import ( + "encoding/base64" + "encoding/hex" + "encoding/json" + "fmt" + "os" + "strings" + + "github.com/celestiaorg/celestia-node/share" +) + +func PrintOutput(data interface{}, err error, formatData func(interface{}) interface{}) error { + switch { + case err != nil: + data = err + case formatData != nil: + data = formatData(data) + } + + resp := struct { + Result interface{} `json:"result"` + }{ + Result: data, + } + + bytes, err := json.MarshalIndent(resp, "", " ") + if err != nil { + return err + } + fmt.Fprintln(os.Stdout, string(bytes)) + return nil +} + +// ParseV0Namespace parses a namespace from a base64 or hex string. The param +// is expected to be the user-specified portion of a v0 namespace ID (i.e. the +// last 10 bytes). +func ParseV0Namespace(param string) (share.Namespace, error) { + userBytes, err := DecodeToBytes(param) + if err != nil { + return nil, err + } + + // if the namespace ID is <= 10 bytes, left pad it with 0s + return share.NewBlobNamespaceV0(userBytes) +} + +// DecodeToBytes decodes a Base64 or hex input string into a byte slice. +func DecodeToBytes(param string) ([]byte, error) { + if strings.HasPrefix(param, "0x") { + decoded, err := hex.DecodeString(param[2:]) + if err != nil { + return nil, fmt.Errorf("error decoding namespace ID: %w", err) + } + return decoded, nil + } + // otherwise, it's just a base64 string + decoded, err := base64.StdEncoding.DecodeString(param) + if err != nil { + return nil, fmt.Errorf("error decoding namespace ID: %w", err) + } + return decoded, nil +} diff --git a/cmd/celestia/rpc_test.go b/cmd/celestia/internal/util_test.go similarity index 97% rename from cmd/celestia/rpc_test.go rename to cmd/celestia/internal/util_test.go index 53087646a7..c67efe506d 100644 --- a/cmd/celestia/rpc_test.go +++ b/cmd/celestia/internal/util_test.go @@ -1,4 +1,4 @@ -package main +package internal import ( "testing" @@ -69,7 +69,7 @@ func Test_parseNamespaceID(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - got, err := parseV0Namespace(tc.param) + got, err := ParseV0Namespace(tc.param) if tc.wantErr { assert.Error(t, err) return diff --git a/cmd/celestia/rpc.go b/cmd/celestia/rpc.go index e7e3c0d5be..1b1c269975 100644 --- a/cmd/celestia/rpc.go +++ b/cmd/celestia/rpc.go @@ -1,428 +1,19 @@ package main import ( - "bytes" - "context" - "encoding/base64" - "encoding/hex" - "encoding/json" - "errors" - "fmt" - "io" - "log" - "net/http" - "os" - "reflect" - "strconv" - "strings" - - "github.com/spf13/cobra" - - "github.com/celestiaorg/celestia-node/api/rpc/client" - "github.com/celestiaorg/celestia-node/share" - "github.com/celestiaorg/celestia-node/state" + "github.com/celestiaorg/celestia-node/cmd/celestia/internal/admin" + "github.com/celestiaorg/celestia-node/cmd/celestia/internal/rpc" ) -const authEnvKey = "CELESTIA_NODE_AUTH_TOKEN" - -var requestURL string -var authTokenFlag string -var printRequest bool - -type jsonRPCRequest struct { - ID int64 `json:"id"` - JSONRPC string `json:"jsonrpc"` - Method string `json:"method"` - Params []interface{} `json:"params"` -} - -type outputWithRequest struct { - Request jsonRPCRequest - Response json.RawMessage -} - func init() { - rpcCmd.PersistentFlags().StringVar( - &requestURL, - "url", - "http://localhost:26658", - "Request URL", - ) - rpcCmd.PersistentFlags().StringVar( - &authTokenFlag, - "auth", - "", - "Authorization token (if not provided, the "+authEnvKey+" environment variable will be used)", + rootCmd.AddCommand(admin.NodeCmd) + + rootCmd.AddCommand( + rpc.BlobCmd, + rpc.DASCmd, + rpc.HeaderCmd, + rpc.P2PCmd, + rpc.ShareCmd, + rpc.StateCmd, ) - rpcCmd.PersistentFlags().BoolVar( - &printRequest, - "print-request", - false, - "Print JSON-RPC request along with the response", - ) - rpcCmd.AddCommand(blobCmd) - rpcCmd.AddCommand(p2pCmd) - rpcCmd.AddCommand(dasCmd) - rpcCmd.AddCommand(headerCmd) - rpcCmd.AddCommand(shareCmd) - rpcCmd.AddCommand(stateCmd) - 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 { - rpcClient, err := newRPCClient(cmd.Context()) - if err != nil { - return err - } - - ctx := context.WithValue(cmd.Context(), rpcClientKey{}, rpcClient) - 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 { - // get keys from modules (map[string]interface{}) - var keys []string - for k := range modules { - keys = append(keys, k) - } - return keys, cobra.ShellCompDirectiveNoFileComp - } else if len(args) == 1 { - // get methods from module - module := modules[args[0]] - methods := reflect.VisibleFields(reflect.TypeOf(module).Elem()) - var methodNames []string - for _, m := range methods { - methodNames = append(methodNames, m.Name+"\t"+parseSignatureForHelpstring(m)) - } - return methodNames, cobra.ShellCompDirectiveNoFileComp - } - return nil, cobra.ShellCompDirectiveNoFileComp - }, - Run: func(cmd *cobra.Command, args []string) { - namespace := args[0] - method := args[1] - params := parseParams(method, args[2:]) - - sendJSONRPCRequest(namespace, method, params) - }, -} - -func parseParams(method string, params []string) []interface{} { - parsedParams := make([]interface{}, len(params)) - validateParamsFn := func(has, want int) error { - if has != want { - return fmt.Errorf("rpc: invalid amount of params. has=%d, want=%d", has, want) - } - return nil - } - switch method { - case "GetSharesByNamespace": - if err := validateParamsFn(len(params), 2); err != nil { - panic(err) - } - // 1. Share Root - root, err := parseJSON(params[0]) - if err != nil { - panic(fmt.Errorf("couldn't parse share root as json: %v", err)) - } - parsedParams[0] = root - // 2. Namespace - namespace, err := parseV0Namespace(params[1]) - if err != nil { - panic(fmt.Sprintf("Error parsing namespace: %v", err)) - } - parsedParams[1] = namespace - return parsedParams - case "QueryDelegation", "QueryUnbonding", "BalanceForAddress": - var err error - if err = validateParamsFn(len(params), 2); err != nil { - panic(err) - } - parsedParams[0], err = parseAddressFromString(params[0]) - if err != nil { - panic(fmt.Errorf("error parsing address: %w", err)) - } - return parsedParams - case "QueryRedelegations": - var err error - parsedParams[0], err = parseAddressFromString(params[0]) - if err != nil { - panic(fmt.Errorf("error parsing address: %w", err)) - } - parsedParams[1], err = parseAddressFromString(params[1]) - if err != nil { - panic(fmt.Errorf("error parsing address: %w", err)) - } - return parsedParams - case "Transfer", "Delegate", "Undelegate": - // 1. Address - var err error - if err = validateParamsFn(len(params), 4); err != nil { - panic(err) - } - parsedParams[0], err = parseAddressFromString(params[0]) - if err != nil { - panic(fmt.Errorf("error parsing address: %w", err)) - } - // 2. Amount + Fee - parsedParams[1] = params[1] - parsedParams[2] = params[2] - // 3. GasLimit (uint64) - num, err := strconv.ParseUint(params[3], 10, 64) - if err != nil { - panic("Error parsing gas limit: uint64 could not be parsed.") - } - parsedParams[3] = num - return parsedParams - case "CancelUnbondingDelegation": - // 1. Validator Address - var err error - if err = validateParamsFn(len(params), 5); err != nil { - panic(err) - } - parsedParams[0], err = parseAddressFromString(params[0]) - if err != nil { - panic(fmt.Errorf("error parsing address: %w", err)) - } - // 2. Amount + Height + Fee - parsedParams[1] = params[1] - parsedParams[2] = params[2] - parsedParams[3] = params[3] - // 4. GasLimit (uint64) - num, err := strconv.ParseUint(params[4], 10, 64) - if err != nil { - panic("Error parsing gas limit: uint64 could not be parsed.") - } - parsedParams[4] = num - return parsedParams - case "BeginRedelegate": - // 1. Source Validator Address - var err error - if err = validateParamsFn(len(params), 5); err != nil { - panic(err) - } - parsedParams[0], err = parseAddressFromString(params[0]) - if err != nil { - panic(fmt.Errorf("error parsing address: %w", err)) - } - // 2. Destination Validator Address - parsedParams[1], err = parseAddressFromString(params[1]) - if err != nil { - panic(fmt.Errorf("error parsing address: %w", err)) - } - // 2. Amount + Fee - parsedParams[2] = params[2] - parsedParams[3] = params[3] - // 4. GasLimit (uint64) - num, err := strconv.ParseUint(params[4], 10, 64) - if err != nil { - panic("Error parsing gas limit: uint64 could not be parsed.") - } - parsedParams[4] = num - return parsedParams - default: - } - - for i, param := range params { - if param[0] == '{' || param[0] == '[' { - rawJSON, err := parseJSON(param) - if err != nil { - parsedParams[i] = param - } else { - parsedParams[i] = rawJSON - } - } else { - // try to parse arguments as numbers before adding them as strings - num, err := strconv.ParseInt(param, 10, 64) - if err == nil { - parsedParams[i] = num - continue - } - parsedParams[i] = param - } - } - return parsedParams -} - -func sendJSONRPCRequest(namespace, method string, params []interface{}) { - url := requestURL - request := jsonRPCRequest{ - ID: 1, - JSONRPC: "2.0", - Method: fmt.Sprintf("%s.%s", namespace, method), - Params: params, - } - - requestBody, err := json.Marshal(request) - if err != nil { - log.Fatalf("Error marshaling JSON-RPC request: %v", err) - } - - req, err := http.NewRequest("POST", url, bytes.NewBuffer(requestBody)) - if err != nil { - log.Fatalf("Error creating JSON-RPC request: %v", err) - } - - req.Header.Set("Content-Type", "application/json") - - authToken := authTokenFlag - if authToken == "" { - authToken = os.Getenv(authEnvKey) - } - if authToken != "" { - req.Header.Set("Authorization", "Bearer "+authToken) - } - - client := &http.Client{} - resp, err := client.Do(req) - if err != nil { - log.Fatalf("Error sending JSON-RPC request: %v", err) - } - defer resp.Body.Close() - - responseBody, err := io.ReadAll(resp.Body) - if err != nil { - log.Fatalf("Error reading response body: %v", err) //nolint:gocritic - } - - rawResponseJSON, err := parseJSON(string(responseBody)) - if err != nil { - log.Fatalf("Error parsing JSON-RPC response: %v", err) - } - if printRequest { - output, err := json.MarshalIndent(outputWithRequest{ - Request: request, - Response: rawResponseJSON, - }, "", " ") - if err != nil { - panic(fmt.Sprintf("Error marshaling JSON-RPC response: %v", err)) - } - fmt.Println(string(output)) - return - } - - output, err := json.MarshalIndent(rawResponseJSON, "", " ") - if err != nil { - panic(fmt.Sprintf("Error marshaling JSON-RPC response: %v", err)) - } - fmt.Println(string(output)) -} - -func parseAddressFromString(addrStr string) (state.Address, error) { - var address state.Address - err := address.UnmarshalJSON([]byte(addrStr)) - if err != nil { - return address, err - } - return address, nil -} - -func parseSignatureForHelpstring(methodSig reflect.StructField) string { - simplifiedSignature := "(" - in, out := methodSig.Type.NumIn(), methodSig.Type.NumOut() - for i := 1; i < in; i++ { - simplifiedSignature += methodSig.Type.In(i).String() - if i != in-1 { - simplifiedSignature += ", " - } - } - simplifiedSignature += ") -> (" - for i := 0; i < out-1; i++ { - simplifiedSignature += methodSig.Type.Out(i).String() - if i != out-2 { - simplifiedSignature += ", " - } - } - simplifiedSignature += ")" - return simplifiedSignature -} - -// parseV0Namespace parses a namespace from a base64 or hex string. The param -// is expected to be the user-specified portion of a v0 namespace ID (i.e. the -// last 10 bytes). -func parseV0Namespace(param string) (share.Namespace, error) { - userBytes, err := decodeToBytes(param) - if err != nil { - return nil, err - } - - // if the namespace ID is <= 10 bytes, left pad it with 0s - return share.NewBlobNamespaceV0(userBytes) -} - -// decodeToBytes decodes a Base64 or hex input string into a byte slice. -func decodeToBytes(param string) ([]byte, error) { - if strings.HasPrefix(param, "0x") { - decoded, err := hex.DecodeString(param[2:]) - if err != nil { - return nil, fmt.Errorf("error decoding namespace ID: %w", err) - } - return decoded, nil - } - // otherwise, it's just a base64 string - decoded, err := base64.StdEncoding.DecodeString(param) - if err != nil { - return nil, fmt.Errorf("error decoding namespace ID: %w", err) - } - 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) -} - -type rpcClientKey struct{} - -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 -} - -func printOutput(data interface{}, err error, formatData func(interface{}) interface{}) error { - switch { - case err != nil: - data = err - case formatData != nil: - data = formatData(data) - } - - resp := struct { - Result interface{} `json:"result"` - }{ - Result: data, - } - - bytes, err := json.MarshalIndent(resp, "", " ") - if err != nil { - return err - } - fmt.Fprintln(os.Stdout, string(bytes)) - return nil }