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 }