Skip to content

Commit

Permalink
Fix overflow in /metrics/validators and add denom exponent (solarlabs…
Browse files Browse the repository at this point in the history
…team#18)

- Fix overflow when delegator tokens too large for /metrics/validators endpoint
- Add denom exponent to allow for easier custom denom definition
- Add documentation for denom exponent and coefficient to avoid confusion
  • Loading branch information
v-shash authored Aug 10, 2022
1 parent 47e3975 commit 5f4936f
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 17 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,8 @@ You can pass the artuments to the executable file to configure it. Here is the p

- `--bech-prefix` - the global prefix for addresses. Defaults to `persistence`
- `--denom` - the currency, for example, `uatom` for Cosmos. Defaults to `uxprt`
- `--denom-coefficient` - the number of decimals, `1000000` for cosmos. Defaults to `1`. Can't provide along with `--denom-exponent`
- `--denom-exponent` - the denom exponent, `6` for cosmos. Defaults to `0`. Can't provide along with `--denom-coefficient`
- `--listen-address` - the address with port the node would listen to. For example, you can use it to redefine port or to make the exporter accessible from the outside by listening on `127.0.0.1`. Defaults to `:9300` (so it's accessible from the outside on port 9300)
- `--node` - the gRPC node URL. Defaults to `localhost:9090`
- `--tendermint-rpc` - Tendermint RPC URL to query node stats (specifically `chain-id`). Defaults to `http://localhost:26657`
Expand Down
46 changes: 39 additions & 7 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ var (
ChainID string
ConstLabels map[string]string
DenomCoefficient float64
DenomExponent uint64
)

var log = zerolog.New(zerolog.ConsoleWriter{Out: os.Stdout}).With().Timestamp().Logger()
Expand Down Expand Up @@ -135,6 +136,8 @@ func Execute(cmd *cobra.Command, args []string) {
Str("--bech-consensus-node-prefix", ConsensusNodePrefix).
Str("--bech-consensus-node-pubkey-prefix", ConsensusNodePubkeyPrefix).
Str("--denom", Denom).
Str("--denom-cofficient", fmt.Sprintf("%f", DenomCoefficient)).
Str("--denom-exponent", fmt.Sprintf("%d", DenomExponent)).
Str("--listen-address", ListenAddress).
Str("--node", NodeAddress).
Str("--log-level", LogLevel).
Expand Down Expand Up @@ -203,13 +206,9 @@ func setChainID() {
}

func setDenom(grpcConn *grpc.ClientConn) {
// if --denom and --denom-coefficient are both provided, use them
// if --denom and (--denom-coefficient or --denom-exponent) are provided, use them
// instead of fetching them via gRPC. Can be useful for networks like osmosis.
if Denom != "" && DenomCoefficient != 0 {
log.Info().
Str("denom", Denom).
Float64("coefficient", DenomCoefficient).
Msg("Using provided denom and coefficient.")
if isUserProvidedAndHandled := checkAndHandleDenomInfoProvidedByUser(); isUserProvidedAndHandled {
return
}

Expand Down Expand Up @@ -249,10 +248,43 @@ func setDenom(grpcConn *grpc.ClientConn) {
log.Fatal().Msg("Could not find the denom info")
}

func checkAndHandleDenomInfoProvidedByUser() bool {

if Denom != "" {
if DenomCoefficient != 1 && DenomExponent != 0 {
log.Fatal().Msg("denom-coefficient and denom-exponent are both provided. Must provide only one")
}

if DenomCoefficient != 1 {
log.Info().
Str("denom", Denom).
Float64("coefficient", DenomCoefficient).
Msg("Using provided denom and coefficient.")
return true
}

if DenomExponent != 0 {
DenomCoefficient = math.Pow10(int(DenomExponent))
log.Info().
Str("denom", Denom).
Uint64("exponent", DenomExponent).
Float64("calculated coefficient", DenomCoefficient).
Msg("Using provided denom and denom exponent and calculating coefficient.")
return true
}

return false
}

return false

}

func main() {
rootCmd.PersistentFlags().StringVar(&ConfigPath, "config", "", "Config file path")
rootCmd.PersistentFlags().StringVar(&Denom, "denom", "", "Cosmos coin denom")
rootCmd.PersistentFlags().Float64Var(&DenomCoefficient, "denom-coefficient", 0, "Denom coefficient")
rootCmd.PersistentFlags().Float64Var(&DenomCoefficient, "denom-coefficient", 1, "Denom coefficient")
rootCmd.PersistentFlags().Uint64Var(&DenomExponent, "denom-exponent", 0, "Denom exponent")
rootCmd.PersistentFlags().StringVar(&ListenAddress, "listen-address", ":9300", "The address this exporter would listen on")
rootCmd.PersistentFlags().StringVar(&NodeAddress, "node", "localhost:9090", "RPC node address")
rootCmd.PersistentFlags().StringVar(&LogLevel, "log-level", "info", "Logging level")
Expand Down
36 changes: 26 additions & 10 deletions validators.go
Original file line number Diff line number Diff line change
Expand Up @@ -249,11 +249,19 @@ func ValidatorsHandler(w http.ResponseWriter, r *http.Request, grpcConn *grpc.Cl
"moniker": validator.Description.Moniker,
}).Set(jailed)

validatorsTokensGauge.With(prometheus.Labels{
"address": validator.OperatorAddress,
"moniker": validator.Description.Moniker,
"denom": Denom,
}).Set(float64(validator.Tokens.Int64()) / DenomCoefficient)
// because cosmos's dec doesn't have .toFloat64() method or whatever and returns everything as int
if value, err := strconv.ParseFloat(validator.Tokens.String(), 64); err != nil {
sublogger.Error().
Str("address", validator.OperatorAddress).
Err(err).
Msg("Could not parse delegator tokens")
} else {
validatorsTokensGauge.With(prometheus.Labels{
"address": validator.OperatorAddress,
"moniker": validator.Description.Moniker,
"denom": Denom,
}).Set(value / DenomCoefficient) // a better way to do this is using math/big Div then checking IsInt64
}

// because cosmos's dec doesn't have .toFloat64() method or whatever and returns everything as int
if value, err := strconv.ParseFloat(validator.DelegatorShares.String(), 64); err != nil {
Expand All @@ -269,11 +277,19 @@ func ValidatorsHandler(w http.ResponseWriter, r *http.Request, grpcConn *grpc.Cl
}).Set(value / DenomCoefficient)
}

validatorsMinSelfDelegationGauge.With(prometheus.Labels{
"address": validator.OperatorAddress,
"moniker": validator.Description.Moniker,
"denom": Denom,
}).Set(float64(validator.MinSelfDelegation.Int64()) / DenomCoefficient)
// because cosmos's dec doesn't have .toFloat64() method or whatever and returns everything as int
if value, err := strconv.ParseFloat(validator.MinSelfDelegation.String(), 64); err != nil {
sublogger.Error().
Str("address", validator.OperatorAddress).
Err(err).
Msg("Could not parse validator min self delegation")
} else {
validatorsMinSelfDelegationGauge.With(prometheus.Labels{
"address": validator.OperatorAddress,
"moniker": validator.Description.Moniker,
"denom": Denom,
}).Set(value / DenomCoefficient)
}

err = validator.UnpackInterfaces(interfaceRegistry) // Unpack interfaces, to populate the Anys' cached values
if err != nil {
Expand Down

0 comments on commit 5f4936f

Please sign in to comment.