package rpc import ( "fmt" "net/http" "strconv" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" "github.com/gorilla/mux" "github.com/spf13/cobra" "github.com/spf13/viper" tmliteProxy "github.com/tendermint/tendermint/lite/proxy" "github.com/cosmos/cosmos-sdk/client/utils" ) //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 a the block at given height", Args: cobra.MaximumNArgs(1), RunE: printBlock, } cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:26657", "Node to connect to") viper.BindPFlag(client.FlagNode, cmd.Flags().Lookup(client.FlagNode)) cmd.Flags().Bool(client.FlagTrustNode, false, "Trust connected full node (don't verify proofs for responses)") viper.BindPFlag(client.FlagTrustNode, cmd.Flags().Lookup(client.FlagTrustNode)) return cmd } func getBlock(cliCtx context.CLIContext, height *int64) ([]byte, error) { // get the node node, err := cliCtx.GetNode() if err != nil { return nil, err } // header -> BlockchainInfo // header, tx -> Block // results -> BlockResults res, err := node.Block(height) if err != nil { return nil, err } if !cliCtx.TrustNode { check, err := cliCtx.Verify(res.Block.Height) if err != nil { return nil, err } err = tmliteProxy.ValidateBlockMeta(res.BlockMeta, check) if err != nil { return nil, err } err = tmliteProxy.ValidateBlock(res.Block, check) if err != nil { return nil, err } } if cliCtx.Indent { return cdc.MarshalJSONIndent(res, "", " ") } return cdc.MarshalJSON(res) } // get the current blockchain height func GetChainHeight(cliCtx context.CLIContext) (int64, error) { node, err := cliCtx.GetNode() if err != nil { return -1, err } status, err := node.Status() if err != nil { return -1, err } height := status.SyncInfo.LatestBlockHeight return height, nil } // CMD func printBlock(cmd *cobra.Command, args []string) error { 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(context.NewCLIContext(), height) if err != nil { return err } fmt.Println(string(output)) return nil } // REST // REST handler to get a block func BlockRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) height, err := strconv.ParseInt(vars["height"], 10, 64) if err != nil { w.WriteHeader(http.StatusBadRequest) w.Write([]byte("ERROR: Couldn't parse block height. Assumed format is '/block/{height}'.")) return } chainHeight, err := GetChainHeight(cliCtx) if height > chainHeight { w.WriteHeader(http.StatusNotFound) w.Write([]byte("ERROR: Requested block height is bigger then the chain length.")) return } output, err := getBlock(cliCtx, &height) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(err.Error())) return } utils.PostProcessResponse(w, cdc, output, cliCtx.Indent) } } // REST handler to get the latest block func LatestBlockRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { height, err := GetChainHeight(cliCtx) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(err.Error())) return } output, err := getBlock(cliCtx, &height) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(err.Error())) return } utils.PostProcessResponse(w, cdc, output, cliCtx.Indent) } }