github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/libs/cosmos-sdk/client/rpc/validators.go (about)

     1  package rpc
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"net/http"
     7  	"strconv"
     8  	"strings"
     9  
    10  	"github.com/gorilla/mux"
    11  	"github.com/spf13/cobra"
    12  	"github.com/spf13/viper"
    13  
    14  	tmtypes "github.com/fibonacci-chain/fbc/libs/tendermint/types"
    15  
    16  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/client/context"
    17  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/client/flags"
    18  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/codec"
    19  	sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types"
    20  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types/rest"
    21  )
    22  
    23  // TODO these next two functions feel kinda hacky based on their placement
    24  
    25  // ValidatorCommand returns the validator set for a given height
    26  func ValidatorCommand(cdc *codec.Codec) *cobra.Command {
    27  	cmd := &cobra.Command{
    28  		Use:   "tendermint-validator-set [height]",
    29  		Short: "Get the full tendermint validator set at given height",
    30  		Args:  cobra.MaximumNArgs(1),
    31  		RunE: func(cmd *cobra.Command, args []string) error {
    32  			var height *int64
    33  
    34  			// optional height
    35  			if len(args) > 0 {
    36  				h, err := strconv.Atoi(args[0])
    37  				if err != nil {
    38  					return err
    39  				}
    40  				if h > 0 {
    41  					tmp := int64(h)
    42  					height = &tmp
    43  				}
    44  			}
    45  
    46  			cliCtx := context.NewCLIContext().WithCodec(cdc)
    47  
    48  			result, err := GetValidators(cliCtx, height, viper.GetInt(flags.FlagPage), viper.GetInt(flags.FlagLimit))
    49  			if err != nil {
    50  				return err
    51  			}
    52  
    53  			return cliCtx.PrintOutput(result)
    54  		},
    55  	}
    56  
    57  	cmd.Flags().StringP(flags.FlagNode, "n", "tcp://localhost:26657", "Node to connect to")
    58  	viper.BindPFlag(flags.FlagNode, cmd.Flags().Lookup(flags.FlagNode))
    59  	cmd.Flags().Bool(flags.FlagTrustNode, false, flags.TrustNodeUsage)
    60  	viper.BindPFlag(flags.FlagTrustNode, cmd.Flags().Lookup(flags.FlagTrustNode))
    61  	cmd.Flags().Bool(flags.FlagIndentResponse, false, "indent JSON response")
    62  	viper.BindPFlag(flags.FlagIndentResponse, cmd.Flags().Lookup(flags.FlagIndentResponse))
    63  	cmd.Flags().Int(flags.FlagPage, 0, "Query a specific page of paginated results")
    64  	viper.BindPFlag(flags.FlagPage, cmd.Flags().Lookup(flags.FlagPage))
    65  	cmd.Flags().Int(flags.FlagLimit, 100, "Query number of results returned per page")
    66  
    67  	return cmd
    68  }
    69  
    70  // Validator output in bech32 format
    71  type ValidatorOutput struct {
    72  	Address          sdk.ConsAddress `json:"address"`
    73  	PubKey           string          `json:"pub_key"`
    74  	ProposerPriority int64           `json:"proposer_priority"`
    75  	VotingPower      int64           `json:"voting_power"`
    76  }
    77  
    78  // Validators at a certain height output in bech32 format
    79  type ResultValidatorsOutput struct {
    80  	BlockHeight int64             `json:"block_height"`
    81  	Validators  []ValidatorOutput `json:"validators"`
    82  }
    83  
    84  func (rvo ResultValidatorsOutput) String() string {
    85  	var b strings.Builder
    86  
    87  	b.WriteString(fmt.Sprintf("block height: %d\n", rvo.BlockHeight))
    88  
    89  	for _, val := range rvo.Validators {
    90  		b.WriteString(
    91  			fmt.Sprintf(`
    92    Address:          %s
    93    Pubkey:           %s
    94    ProposerPriority: %d
    95    VotingPower:      %d
    96  		`,
    97  				val.Address, val.PubKey, val.ProposerPriority, val.VotingPower,
    98  			),
    99  		)
   100  	}
   101  
   102  	return b.String()
   103  }
   104  
   105  func bech32ValidatorOutput(validator *tmtypes.Validator) (ValidatorOutput, error) {
   106  	bechValPubkey, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeConsPub, validator.PubKey)
   107  	if err != nil {
   108  		return ValidatorOutput{}, err
   109  	}
   110  
   111  	return ValidatorOutput{
   112  		Address:          sdk.ConsAddress(validator.Address),
   113  		PubKey:           bechValPubkey,
   114  		ProposerPriority: validator.ProposerPriority,
   115  		VotingPower:      validator.VotingPower,
   116  	}, nil
   117  }
   118  
   119  // GetValidators from client
   120  func GetValidators(cliCtx context.CLIContext, height *int64, page, limit int) (ResultValidatorsOutput, error) {
   121  	// get the node
   122  	node, err := cliCtx.GetNode()
   123  	if err != nil {
   124  		return ResultValidatorsOutput{}, err
   125  	}
   126  
   127  	validatorsRes, err := node.Validators(height, page, limit)
   128  	if err != nil {
   129  		return ResultValidatorsOutput{}, err
   130  	}
   131  
   132  	if !cliCtx.TrustNode {
   133  		check, err := cliCtx.Verify(validatorsRes.BlockHeight)
   134  		if err != nil {
   135  			return ResultValidatorsOutput{}, err
   136  		}
   137  
   138  		if !bytes.Equal(check.ValidatorsHash, tmtypes.NewValidatorSet(validatorsRes.Validators).Hash(*height)) {
   139  			return ResultValidatorsOutput{}, fmt.Errorf("received invalid validatorset")
   140  		}
   141  	}
   142  
   143  	outputValidatorsRes := ResultValidatorsOutput{
   144  		BlockHeight: validatorsRes.BlockHeight,
   145  		Validators:  make([]ValidatorOutput, len(validatorsRes.Validators)),
   146  	}
   147  
   148  	for i := 0; i < len(validatorsRes.Validators); i++ {
   149  		outputValidatorsRes.Validators[i], err = bech32ValidatorOutput(validatorsRes.Validators[i])
   150  		if err != nil {
   151  			return ResultValidatorsOutput{}, err
   152  		}
   153  	}
   154  
   155  	return outputValidatorsRes, nil
   156  }
   157  
   158  // REST
   159  
   160  // Validator Set at a height REST handler
   161  func ValidatorSetRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
   162  	return func(w http.ResponseWriter, r *http.Request) {
   163  		_, page, limit, err := rest.ParseHTTPArgsWithLimit(r, 100)
   164  		if err != nil {
   165  			rest.WriteErrorResponse(w, http.StatusBadRequest, "failed to parse pagination parameters")
   166  			return
   167  		}
   168  
   169  		vars := mux.Vars(r)
   170  		height, err := strconv.ParseInt(vars["height"], 10, 64)
   171  		if err != nil {
   172  			rest.WriteErrorResponse(w, http.StatusBadRequest, "failed to parse block height")
   173  			return
   174  		}
   175  
   176  		chainHeight, err := GetChainHeight(cliCtx)
   177  		if err != nil {
   178  			rest.WriteErrorResponse(w, http.StatusInternalServerError, "failed to parse chain height")
   179  			return
   180  		}
   181  		if height > chainHeight {
   182  			rest.WriteErrorResponse(w, http.StatusNotFound, "requested block height is bigger then the chain length")
   183  			return
   184  		}
   185  
   186  		output, err := GetValidators(cliCtx, &height, page, limit)
   187  		if err != nil {
   188  			rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
   189  			return
   190  		}
   191  		rest.PostProcessResponse(w, cliCtx, output)
   192  	}
   193  }
   194  
   195  // Latest Validator Set REST handler
   196  func LatestValidatorSetRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
   197  	return func(w http.ResponseWriter, r *http.Request) {
   198  		_, page, limit, err := rest.ParseHTTPArgsWithLimit(r, 100)
   199  		if err != nil {
   200  			rest.WriteErrorResponse(w, http.StatusBadRequest, "failed to parse pagination parameters")
   201  			return
   202  		}
   203  
   204  		output, err := GetValidators(cliCtx, nil, page, limit)
   205  		if err != nil {
   206  			rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
   207  			return
   208  		}
   209  
   210  		rest.PostProcessResponse(w, cliCtx, output)
   211  	}
   212  }