Skip to content
This repository has been archived by the owner on Jun 12, 2024. It is now read-only.

Commit

Permalink
Add Offline CLI commands and CLI docs. (#383)
Browse files Browse the repository at this point in the history
* add more blockchain cli

* add offline and tx txbytes mode

* fix typo

* lint

* add cli README

* link to go sdk
  • Loading branch information
Stumble authored Sep 21, 2019
1 parent f127c29 commit 91ce28f
Show file tree
Hide file tree
Showing 9 changed files with 663 additions and 6 deletions.
246 changes: 246 additions & 0 deletions client/blockchain/query.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
package blockchain

import (
"encoding/hex"
"fmt"
"strconv"
"strings"
"time"

"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/client/rpc"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
txutils "github.com/cosmos/cosmos-sdk/x/auth/client/utils"
"github.com/spf13/cobra"
tmliteProxy "github.com/tendermint/tendermint/lite/proxy"
ctypes "github.com/tendermint/tendermint/rpc/core/types"

// "github.com/lino-network/lino/utils"

linotypes "github.com/lino-network/lino/types"
)

func GetQueryCmd(cdc *codec.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "blockchain",
Short: "Blockchain-related Queries",
DisableFlagParsing: true,
SuggestionsMinimumDistance: 2,
RunE: client.ValidateCmd,
}
cmd.AddCommand(
// txutils.QueryTxCmd(cdc),
rpc.ValidatorCommand(cdc),
rpc.BlockCommand(),
)
cmd.AddCommand(client.GetCommands(
getCmdTx(cdc),
getCmdHeight(cdc),
getCmdMessage(cdc),
)...)
return cmd
}

// GetCmdHeight -
func getCmdHeight(cdc *codec.Codec) *cobra.Command {
return &cobra.Command{
Use: "height",
Short: "height current block height",
Args: cobra.ExactArgs(0),
RunE: func(cmd *cobra.Command, args []string) error {
cliCtx := context.NewCLIContext().WithCodec(cdc)
height, err := rpc.GetChainHeight(cliCtx)
if err != nil {
return err
}
fmt.Println(height)
return nil
},
}
}

type MsgPrintLayout struct {
Hash string `json:"hash"`
Msgs []sdk.Msg `json:"msgs"`
}

// GetCmdBlock -
func getCmdMessage(cdc *codec.Codec) *cobra.Command {
return &cobra.Command{
Use: "msg",
Short: "msg <block-height>, print messages and results of the block height",
Args: cobra.MaximumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
cliCtx := context.NewCLIContext().WithCodec(cdc)
var height *int64
// optional height
if len(args) > 0 {
h, err := strconv.Atoi(args[0])
if err != nil {
return err
}
if h > 0 {
tmp := int64(h)
height = &tmp
}
}

// get the node
node, err := cliCtx.GetNode()
if err != nil {
return err
}

// header -> BlockchainInfo
// header, tx -> Block
// results -> BlockResults
res, err := node.Block(height)
if err != nil {
return err
}

if !cliCtx.TrustNode {
check, err := cliCtx.Verify(res.Block.Height)
if err != nil {
return err
}

err = tmliteProxy.ValidateBlockMeta(res.BlockMeta, check)
if err != nil {
return err
}

err = tmliteProxy.ValidateBlock(res.Block, check)
if err != nil {
return err
}
}

decoder := linotypes.TxDecoder(cdc)
var result []MsgPrintLayout
for _, txbytes := range res.Block.Data.Txs {
hexstr := hex.EncodeToString(txbytes.Hash())
hexstr = strings.ToUpper(hexstr)
tx, err := decoder(txbytes)
if err != nil {
return err
}
result = append(result, MsgPrintLayout{
Hash: hexstr,
Msgs: tx.GetMsgs(),
})
}
out, err := cdc.MarshalJSONIndent(result, "", " ")
if err != nil {
return err
}
fmt.Println(string(out))

return nil
},
}
}

// GetCmdTx -
func getCmdTx(cdc *codec.Codec) *cobra.Command {
return &cobra.Command{
Use: "tx",
Short: "tx <hash>",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
cliCtx := context.NewCLIContext().WithCodec(cdc)
hashHexStr := args[0]
hash, err := hex.DecodeString(hashHexStr)
if err != nil {
return err
}

node, err := cliCtx.GetNode()
if err != nil {
return err
}

resTx, err := node.Tx(hash, !cliCtx.TrustNode)
if err != nil {
return err
}

if !cliCtx.TrustNode {
if err = txutils.ValidateTxResult(cliCtx, resTx); err != nil {
return err
}
}

_ = printTx(cdc, resTx.Tx)

resBlocks, err := getBlocksForTxResults(cliCtx, []*ctypes.ResultTx{resTx})
if err != nil {
return err
}

out, err := formatTxResult(cliCtx.Codec, resTx, resBlocks[resTx.Height])
if err != nil {
return err
}

fmt.Println(out)
return nil
},
}
}

func getBlocksForTxResults(cliCtx context.CLIContext, resTxs []*ctypes.ResultTx) (map[int64]*ctypes.ResultBlock, error) {
node, err := cliCtx.GetNode()
if err != nil {
return nil, err
}

resBlocks := make(map[int64]*ctypes.ResultBlock)

for _, resTx := range resTxs {
if _, ok := resBlocks[resTx.Height]; !ok {
resBlock, err := node.Block(&resTx.Height)
if err != nil {
return nil, err
}

resBlocks[resTx.Height] = resBlock
}
}

return resBlocks, nil
}

func formatTxResult(cdc *codec.Codec, resTx *ctypes.ResultTx, resBlock *ctypes.ResultBlock) (sdk.TxResponse, error) {
tx, err := parseTx(linotypes.TxDecoder(cdc), resTx.Tx)
if err != nil {
return sdk.TxResponse{}, err
}

return sdk.NewResponseResultTx(resTx, tx, resBlock.Block.Time.Format(time.RFC3339)), nil
}

func parseTx(decoder sdk.TxDecoder, txBytes []byte) (sdk.Tx, error) {
tx, err := decoder(txBytes)
if err != nil {
return nil, err
}

return tx, nil
}

func printTx(cdc *codec.Codec, txBytes []byte) error {
decoder := linotypes.TxDecoder(cdc)
msg, err := decoder(txBytes)
if err != nil {
return err
}
out, err2 := cdc.MarshalJSONIndent(msg, "", " ")
if err2 != nil {
return err
}
fmt.Println(string(out))
return nil
}
38 changes: 38 additions & 0 deletions client/broadcastcmd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package client

import (
"encoding/hex"
"fmt"

// "strings"

"github.com/spf13/cobra"
// "github.com/spf13/viper"
"github.com/cosmos/cosmos-sdk/codec"

linotypes "github.com/lino-network/lino/types"
)

// GetCmdBoradcast -
func GetCmdBroadcast(cdc *codec.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "broadcast",
Short: "broadcast <tx-hex>",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := NewCoreBroadcastContextFromViper().WithTxEncoder(linotypes.TxEncoder(cdc))
hexstr := args[0]
hexbytes, err := hex.DecodeString(hexstr)
if err != nil {
return err
}
res, err := ctx.BroadcastTx(hexbytes)
if err != nil {
return err
}
fmt.Println(res.String())
return nil
},
}
return cmd
}
20 changes: 20 additions & 0 deletions client/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ func NewCoreContextFromViper() core.CoreContext {
Height: viper.GetInt64(FlagHeight),
TrustNode: viper.GetBool(FlagTrustNode),
FromAddressName: viper.GetString(FlagName),
Offline: viper.GetBool(FlagOffline),
NodeURI: nodeURI,
Sequence: uint64(viper.GetInt64(FlagSequence)), // XXX(yumin): dangerous, but ok.
Client: rpc,
Expand All @@ -69,4 +70,23 @@ func NewCoreContextFromViper() core.CoreContext {
return ctx
}

func NewCoreBroadcastContextFromViper() core.CoreContext {
nodeURI := viper.GetString(FlagNode)
var rpc rpcclient.Client
if nodeURI != "" {
rpc = rpcclient.NewHTTP(nodeURI, "/websocket")
}

ctx := core.CoreContext{
ChainID: viper.GetString(FlagChainID),
Height: viper.GetInt64(FlagHeight),
TrustNode: viper.GetBool(FlagTrustNode),
FromAddressName: viper.GetString(FlagName),
Offline: viper.GetBool(FlagOffline),
NodeURI: nodeURI,
Client: rpc,
}
return ctx
}

type CommandTxCallback func(cmd *cobra.Command, args []string) error
1 change: 1 addition & 0 deletions client/core/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ type CoreContext struct {
FromAddressName string
Sequence uint64
Memo string
Offline bool
Client rpcclient.Client
PrivKeys []crypto.PrivKey
Fees sdk.Coins
Expand Down
16 changes: 15 additions & 1 deletion client/core/core.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package core

import (
"encoding/hex"
"fmt"
"strings"

sdk "github.com/cosmos/cosmos-sdk/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
Expand Down Expand Up @@ -64,7 +66,19 @@ func (ctx CoreContext) query(key cmn.HexBytes, storeName, endPath string) (res [

func (ctx CoreContext) DoTxPrintResponse(msg sdk.Msg) error {
if err := msg.ValidateBasic(); err != nil {
panic(err)
return err
}

if ctx.Offline {
tx, err := ctx.BuildAndSign([]sdk.Msg{msg})
if err != nil {
return err
}
txhex := hex.EncodeToString(tx)
txhex = strings.ToUpper(txhex)
fmt.Println(string(tx))
fmt.Println(txhex)
return nil
}

// build and sign the transaction, then broadcast to Tendermint
Expand Down
2 changes: 2 additions & 0 deletions client/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

// nolint
const (
FlagOffline = "offline"
FlagChainID = "chain-id"
FlagNode = "node"
FlagHeight = "height"
Expand All @@ -31,6 +32,7 @@ func PostCommands(cmds ...*cobra.Command) []*cobra.Command {
c.Flags().String(FlagPrivKey2, "", "Second(side) Private key to sign the transaction")
c.Flags().String(FlagNode, "tcp://localhost:26657", "<host>:<port> to tendermint rpc interface for this chain")
c.Flags().String(FlagFees, "", "Fees to pay along with transaction; eg: 1linocoin")
c.Flags().Bool(FlagOffline, false, "Print Tx to stdout only")
}
return cmds
}
Loading

0 comments on commit 91ce28f

Please sign in to comment.