feat: Integrate tendermint Block endpoints into the cli (#14659)

Co-authored-by: Aleksandr Bezobchuk <alexanderbez@users.noreply.github.com>
Co-authored-by: Julien Robert <julien@rbrt.fr>
This commit is contained in:
cipherZ
2023-02-14 19:23:45 +01:00
committed by GitHub
co-authored by Aleksandr Bezobchuk Julien Robert
parent cb23af6d97
commit 86eca4c72b
20 changed files with 1876 additions and 321 deletions
+7
View File
@@ -15,6 +15,7 @@ type CometRPC interface {
Validators(ctx context.Context, height *int64, page, perPage *int) (*coretypes.ResultValidators, error)
Status(context.Context) (*coretypes.ResultStatus, error)
Block(ctx context.Context, height *int64) (*coretypes.ResultBlock, error)
BlockByHash(ctx context.Context, hash []byte) (*coretypes.ResultBlock, error)
BlockchainInfo(ctx context.Context, minHeight, maxHeight int64) (*coretypes.ResultBlockchainInfo, error)
Commit(ctx context.Context, height *int64) (*coretypes.ResultCommit, error)
Tx(ctx context.Context, hash []byte, prove bool) (*coretypes.ResultTx, error)
@@ -25,4 +26,10 @@ type CometRPC interface {
page, perPage *int,
orderBy string,
) (*coretypes.ResultTxSearch, error)
BlockSearch(
ctx context.Context,
query string,
page, perPage *int,
orderBy string,
) (*coretypes.ResultBlockSearch, error)
}
+111 -64
View File
@@ -2,75 +2,18 @@ package rpc
import (
"context"
"encoding/hex"
"fmt"
"strconv"
"github.com/spf13/cobra"
"time"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/codec/legacy"
cmt "github.com/cometbft/cometbft/proto/tendermint/types"
coretypes "github.com/cometbft/cometbft/rpc/core/types"
sdk "github.com/cosmos/cosmos-sdk/types"
)
// BlockCommand returns the verified block data for a given heights
func BlockCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "block [height]",
Short: "Get verified data for the block at given height",
Args: cobra.MaximumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err
}
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
}
}
output, err := getBlock(clientCtx, height)
if err != nil {
return err
}
fmt.Println(string(output))
return nil
},
}
cmd.Flags().StringP(flags.FlagNode, "n", "tcp://localhost:26657", "Node to connect to")
return cmd
}
func getBlock(clientCtx client.Context, height *int64) ([]byte, error) {
// get the node
node, err := clientCtx.GetNode()
if err != nil {
return nil, err
}
// header -> BlockchainInfo
// header, tx -> Block
// results -> BlockResults
res, err := node.Block(context.Background(), height)
if err != nil {
return nil, err
}
return legacy.Cdc.MarshalJSON(res)
}
// get the current blockchain height
// GetChainHeight returns the current blockchain height.
func GetChainHeight(clientCtx client.Context) (int64, error) {
node, err := clientCtx.GetNode()
if err != nil {
@@ -85,3 +28,107 @@ func GetChainHeight(clientCtx client.Context) (int64, error) {
height := status.SyncInfo.LatestBlockHeight
return height, nil
}
// QueryBlocks performs a search for blocks based on BeginBlock and EndBlock
// events via the CometBFT RPC. A custom query may be passed as described below:
//
// To tell which events you want, you need to provide a query. query is a
// string, which has a form: "condition AND condition ..." (no OR at the
// moment). condition has a form: "key operation operand". key is a string with
// a restricted set of possible symbols ( \t\n\r\\()"'=>< are not allowed).
// operation can be "=", "<", "<=", ">", ">=", "CONTAINS" AND "EXISTS". operand
// can be a string (escaped with single quotes), number, date or time.
// Examples:
// tm.event = 'NewBlock' # new blocks
// tm.event = 'CompleteProposal' # node got a complete proposal
// tm.event = 'Tx' AND tx.hash = 'XYZ' # single transaction
// tm.event = 'Tx' AND tx.height = 5 # all txs of the fifth block
// tx.height = 5 # all txs of the fifth block
//
// For more information, see the /subscribe CometBFT RPC endpoint documentation
func QueryBlocks(clientCtx client.Context, page, limit int, query string, orderBy string) (*sdk.SearchBlocksResult, error) {
node, err := clientCtx.GetNode()
if err != nil {
return nil, err
}
resBlocks, err := node.BlockSearch(context.Background(), query, &page, &limit, orderBy)
if err != nil {
return nil, err
}
blocks, err := formatBlockResults(resBlocks.Blocks)
if err != nil {
return nil, err
}
result := sdk.NewSearchBlocksResult(int64(resBlocks.TotalCount), int64(len(blocks)), int64(page), int64(limit), blocks)
return result, nil
}
// get block by height
func GetBlockByHeight(clientCtx client.Context, height *int64) (*cmt.Block, error) {
// get the node
node, err := clientCtx.GetNode()
if err != nil {
return nil, err
}
// header -> BlockchainInfo
// header, tx -> Block
// results -> BlockResults
resBlock, err := node.Block(context.Background(), height)
if err != nil {
return nil, err
}
out := sdk.NewResponseResultBlock(resBlock, resBlock.Block.Time.Format(time.RFC3339))
if out == nil {
return nil, fmt.Errorf("unable to create response block from comet result block: %v", resBlock)
}
return out, nil
}
func GetBlockByHash(clientCtx client.Context, hashHexString string) (*cmt.Block, error) {
hash, err := hex.DecodeString(hashHexString)
if err != nil {
return nil, err
}
// get the node
node, err := clientCtx.GetNode()
if err != nil {
return nil, err
}
resBlock, err := node.BlockByHash(context.Background(), hash)
if err != nil {
return nil, err
} else if resBlock.Block == nil {
return nil, fmt.Errorf("block not found with hash: %s", hashHexString)
}
out := sdk.NewResponseResultBlock(resBlock, resBlock.Block.Time.Format(time.RFC3339))
if out == nil {
return nil, fmt.Errorf("unable to create response block from comet result block: %v", resBlock)
}
return out, nil
}
// formatBlockResults parses the indexed blocks into a slice of BlockResponse objects.
func formatBlockResults(resBlocks []*coretypes.ResultBlock) ([]*cmt.Block, error) {
out := make([]*cmt.Block, len(resBlocks))
for i := range resBlocks {
out[i] = sdk.NewResponseResultBlock(resBlocks[i], resBlocks[i].Block.Time.Format(time.RFC3339))
if out[i] == nil {
return nil, fmt.Errorf("unable to create response block from comet result block: %v", resBlocks[i])
}
}
return out, nil
}