From 8c282c5563b35ffb5d8cbc68d3b921e8b1937f0a Mon Sep 17 00:00:00 2001 From: Jay Jie Date: Sat, 16 Sep 2023 20:47:19 -0700 Subject: [PATCH] chore: consolidate rest routes into one file; add sdk versioning --- config/main.go | 22 +++++- exporter/main.go | 17 +++-- exporter/metric.go | 16 ++--- main.go | 5 +- rest/akash.go | 9 +-- rest/balances.go | 7 +- rest/block.go | 11 +-- rest/consensus.go | 10 +-- rest/delegations.go | 6 +- rest/gov.go | 10 +-- rest/gravity.go | 29 ++++---- rest/ibc.go | 13 ++-- rest/inflation.go | 12 ++-- rest/node.go | 6 +- rest/rest.go | 46 +++++++------ rest/reward_commission.go | 6 +- rest/routes.go | 140 ++++++++++++++++++++++++++++++++++++++ rest/slashing.go | 11 +-- rest/stakingPool.go | 13 ++-- rest/tx.go | 6 +- rest/upgrade.go | 6 +- rest/validators.go | 8 ++- rest/validatorsets.go | 125 ++++++++++++++++++++++++---------- utils/converter.go | 27 ++++++++ 24 files changed, 414 insertions(+), 147 deletions(-) create mode 100644 rest/routes.go diff --git a/config/main.go b/config/main.go index bdd9e15..2bc88a4 100644 --- a/config/main.go +++ b/config/main.go @@ -3,6 +3,7 @@ package config import ( "fmt" "log" + "strings" sdktypes "github.com/cosmos/cosmos-sdk/types" "github.com/jim380/Cendermint/utils" @@ -12,6 +13,7 @@ import ( type Config struct { Chain string + SDKVersion string OperatorAddr string RestAddr string RpcAddr string @@ -58,7 +60,11 @@ func (config Config) CheckInputs(chainList map[string][]string) { } } - // TODO add more robust checks + // TO-DO add more robust checks + if config.SDKVersion == "" { + log.Fatal("SDK version was not provided") + } + if config.OperatorAddr == "" { log.Fatal("Operator address was not provided") } @@ -159,3 +165,17 @@ func GetChainList() map[string][]string { return chainList } + +func (config Config) GetSDKVersion() string { + return config.SDKVersion +} + +func (config Config) IsLegacySDKVersion() bool { + var legacy bool = false + + if strings.Contains(config.SDKVersion, "0.45") { + legacy = true + } + + return legacy +} diff --git a/exporter/main.go b/exporter/main.go index b0a3056..4277b0b 100644 --- a/exporter/main.go +++ b/exporter/main.go @@ -14,9 +14,9 @@ import ( "github.com/prometheus/client_golang/prometheus/promhttp" ) -func Start(chain string, port string, logger *zap.Logger) { +func Start(config config.Config, port string, logger *zap.Logger) { http.Handle("/metrics", promhttp.Handler()) - go Run(chain, logger) + go Run(config, logger) err := http.ListenAndServe(":"+port, nil) if err != nil { @@ -26,38 +26,37 @@ func Start(chain string, port string, logger *zap.Logger) { } -func Run(chain string, log *zap.Logger) { +func Run(cfg config.Config, log *zap.Logger) { cl := config.GetChainList() - denomList := config.GetDenomList(chain, cl) + denomList := config.GetDenomList(cfg.Chain, cl) registerGauges(denomList) counterVecs := registerLabels() pollInterval, _ := strconv.Atoi(os.Getenv("POLL_INTERVAL")) ticker := time.NewTicker(1 * time.Second).C - // ticker2 := time.NewTicker(40 * time.Second).C go func() { for { var block rest.Blocks - block.GetInfo() + block.GetInfo(cfg) currentBlockHeight, _ := strconv.ParseInt(block.Block.Header.Height, 10, 64) if previousBlockHeight != currentBlockHeight { fmt.Println("--------------------------- Start ---------------------------") - block.GetLastBlockTimestamp(currentBlockHeight) + block.GetLastBlockTimestamp(cfg, currentBlockHeight) zap.L().Info("\t", zap.Bool("Success", true), zap.String("Last block timestamp", block.Block.Header.LastTimestamp)) zap.L().Info("\t", zap.Bool("Success", true), zap.String("Current block timestamp", block.Block.Header.Timestamp)) zap.L().Info("\t", zap.Bool("Success", true), zap.String("Current block height", fmt.Sprint(currentBlockHeight))) select { case <-ticker: // fetch info from REST - restData := rest.GetData(chain, currentBlockHeight, block, denomList[0]) + restData := rest.GetData(cfg, currentBlockHeight, block, denomList[0]) SetMetric(currentBlockHeight, restData, log) // case <-ticker2: // takes ~5-6 blocks to return results per request // tends to halt the node too. Caution !!! - // restData := rest.GetDelegationsData(chain, currentBlockHeight, block, denomList[0]) + // restData := rest.GetDelegationsData(cfg, chain, currentBlockHeight, block, denomList[0]) // SetMetric(currentBlockHeight, restData, log) } diff --git a/exporter/metric.go b/exporter/metric.go index 10b5ceb..7a028f0 100644 --- a/exporter/metric.go +++ b/exporter/metric.go @@ -12,7 +12,7 @@ import ( // gauges/labels <-> value func SetMetric(currentBlock int64, restData *rest.RESTData, log *zap.Logger) { operAddr := rest.OperAddr - consPubKey := restData.Validators.ConsPubKey + consPubKey := restData.Validator.ConsPubKey consAddr := restData.Validatorsets[consPubKey.Key][0] // chain @@ -51,17 +51,17 @@ func SetMetric(currentBlock int64, restData *rest.RESTData, log *zap.Logger) { // validator info metricData.Validator.VotingPower = utils.StringToFloat64(restData.Validatorsets[consPubKey.Key][1]) - metricData.Validator.JailStatus = utils.BoolToFloat64(restData.Validators.Jailed) - metricData.Validator.MinSelfDelegation = utils.StringToFloat64(restData.Validators.MinSelfDelegation) + metricData.Validator.JailStatus = utils.BoolToFloat64(restData.Validator.Jailed) + metricData.Validator.MinSelfDelegation = utils.StringToFloat64(restData.Validator.MinSelfDelegation) // validator delegation - metricData.Validator.Delegation.Shares = utils.StringToFloat64(restData.Validators.DelegatorShares) + metricData.Validator.Delegation.Shares = utils.StringToFloat64(restData.Validator.DelegatorShares) metricData.Validator.Delegation.Ratio = metricData.Validator.Delegation.Shares / metricData.Network.Staking.BondedTokens // metricData.Validator.Delegation.DelegatorCount = restData.Delegations.DelegationCount // metricData.Validator.Delegation.Self = restData.Delegations.SelfDelegation // validator commission - metricData.Validator.Commission.Rate = utils.StringToFloat64(restData.Validators.Commission.Commission.Rate) - metricData.Validator.Commission.MaxRate = utils.StringToFloat64(restData.Validators.Commission.Commission.Max_rate) - metricData.Validator.Commission.MaxChangeRate = utils.StringToFloat64(restData.Validators.Commission.Commission.Max_change_rate) + metricData.Validator.Commission.Rate = utils.StringToFloat64(restData.Validator.Commission.Commission.Rate) + metricData.Validator.Commission.MaxRate = utils.StringToFloat64(restData.Validator.Commission.Commission.Max_rate) + metricData.Validator.Commission.MaxChangeRate = utils.StringToFloat64(restData.Validator.Commission.Commission.Max_change_rate) // validator signing metricData.Validator.Commit.PrecommitStatus = restData.Commit.ValidatorPrecommitStatus metricData.Validator.Proposer.Status = restData.Commit.ValidatorProposingStatus @@ -129,7 +129,7 @@ func SetMetric(currentBlock int64, restData *rest.RESTData, log *zap.Logger) { // labels node metricData.Network.ChainID = restData.Commit.ChainId - metricData.Validator.Moniker = restData.Validators.Description.Moniker + metricData.Validator.Moniker = restData.Validator.Description.Moniker metricData.Network.NodeInfo.Moniker = restData.NodeInfo.Default.Moniker metricData.Network.NodeInfo.NodeID = restData.NodeInfo.Default.NodeID metricData.Network.NodeInfo.TMVersion = restData.NodeInfo.Default.TMVersion diff --git a/main.go b/main.go index 14b9449..6575288 100644 --- a/main.go +++ b/main.go @@ -5,7 +5,7 @@ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -42,6 +42,7 @@ func main() { cfg := config.Config{ Chain: os.Getenv("CHAIN"), + SDKVersion: os.Getenv("SDK_VERSION"), OperatorAddr: os.Getenv("OPERATOR_ADDR"), RestAddr: os.Getenv("REST_ADDR"), RpcAddr: os.Getenv("RPC_ADDR"), @@ -70,5 +71,5 @@ func main() { rest.RPCAddr = rpcAddr rest.OperAddr = operAddr - exporter.Start(chain, listeningPort, logger) + exporter.Start(cfg, listeningPort, logger) } diff --git a/rest/akash.go b/rest/akash.go index c9d6e1a..2c18070 100644 --- a/rest/akash.go +++ b/rest/akash.go @@ -108,18 +108,15 @@ type GroupSpec struct { } func (rd *RESTData) getAkashDeployments() { - // var deployments akashDeployments var deployments, activeDeployments akashDeployments - // ?filters.state=active - res, err := HttpQuery(RESTAddr + "/akash/deployment/v1beta2/deployments/list") + route := getDeploymentsRoute() + res, err := HttpQuery(RESTAddr + route) if err != nil { zap.L().Fatal("", zap.Bool("Success", false), zap.String("err", err.Error())) } json.Unmarshal(res, &deployments) - // rd.AkashInfo.Deployments = deployments - // get total deployments count totalDeploymentsCount, err := strconv.Atoi(deployments.Pagination.Total) if err != nil { @@ -128,7 +125,7 @@ func (rd *RESTData) getAkashDeployments() { rd.AkashInfo.TotalDeployments = totalDeploymentsCount // get active deployments count - resActive, err := HttpQuery(RESTAddr + "/akash/deployment/v1beta2/deployments/list?filters.state=active") + resActive, err := HttpQuery(RESTAddr + route + "?filters.state=active") if err != nil { zap.L().Fatal("", zap.Bool("Success", false), zap.String("err", err.Error())) } diff --git a/rest/balances.go b/rest/balances.go index 06a123f..0b6bef0 100644 --- a/rest/balances.go +++ b/rest/balances.go @@ -4,6 +4,7 @@ import ( "encoding/json" "strings" + "github.com/jim380/Cendermint/config" "go.uber.org/zap" ) @@ -16,10 +17,12 @@ type Coin struct { Amount string } -func (rd *RESTData) getBalances() { +func (rd *RESTData) getBalances(cfg config.Config) { var b balances - res, err := HttpQuery(RESTAddr + "/cosmos/bank/v1beta1/balances/" + AccAddr) + route := getBalancesByAddressRoute(cfg) + + res, err := HttpQuery(RESTAddr + route + AccAddr) if err != nil { zap.L().Fatal("", zap.Bool("Success", false), zap.String("err", err.Error())) } diff --git a/rest/block.go b/rest/block.go index 1180ac4..30f7eda 100644 --- a/rest/block.go +++ b/rest/block.go @@ -5,6 +5,7 @@ import ( "strconv" "strings" + "github.com/jim380/Cendermint/config" "go.uber.org/zap" ) @@ -39,8 +40,9 @@ type lastCommit struct { } } -func (b *Blocks) GetInfo() Blocks { - res, err := HttpQuery(RESTAddr + "/blocks/latest") +func (b *Blocks) GetInfo(cfg config.Config) Blocks { + route := getBlockInfoRoute(cfg) + res, err := HttpQuery(RESTAddr + route) if err != nil { zap.L().Fatal("Connection to REST failed", zap.Bool("Success", false), zap.String("err", err.Error())) } @@ -54,9 +56,10 @@ func (b *Blocks) GetInfo() Blocks { return *b } -func (b *Blocks) GetLastBlockTimestamp(currentHeight int64) Blocks { +func (b *Blocks) GetLastBlockTimestamp(cfg config.Config, currentHeight int64) Blocks { var lastBlock LastBlock - res, err := HttpQuery(RESTAddr + "/blocks/" + strconv.Itoa(int(currentHeight-1))) + route := getBlockByHeightRoute(cfg) + res, err := HttpQuery(RESTAddr + route + strconv.Itoa(int(currentHeight-1))) if err != nil { zap.L().Fatal("Connection to REST failed", zap.Bool("Success", false), zap.String("err", err.Error())) } diff --git a/rest/consensus.go b/rest/consensus.go index 0155349..2cea690 100644 --- a/rest/consensus.go +++ b/rest/consensus.go @@ -6,6 +6,7 @@ import ( "strconv" "strings" + "github.com/jim380/Cendermint/config" "github.com/jim380/Cendermint/utils" "go.uber.org/zap" ) @@ -56,7 +57,7 @@ type rpcValidators struct { } `json:"validators"` } -func (rpc *RPCData) getConsensusDump() { +func (rpc *RPCData) getConsensusDump(cfg config.Config) { var cs ConsensusState var vSetsResult map[string][]string = make(map[string][]string) @@ -66,7 +67,7 @@ func (rpc *RPCData) getConsensusDump() { } json.Unmarshal(res, &cs) - conspubMonikerMap := rpc.getConspubMonikerMap() + conspubMonikerMap := rpc.getConspubMonikerMap(cfg) // cs.Result.Validatorset.Validators is already sorted based on voting power for index, validator := range cs.Result.Validatorset.Validators { var prevote, precommit string @@ -101,11 +102,12 @@ func (rpc *RPCData) getConsensusDump() { zap.L().Info("", zap.Bool("Success", true), zap.String("# of validators from RPC", fmt.Sprint(len(rpc.Validatorsets)))) } -func (rpc *RPCData) getConspubMonikerMap() map[string]string { +func (rpc *RPCData) getConspubMonikerMap(cfg config.Config) map[string]string { var v rpcValidators var vResult map[string]string = make(map[string]string) - res, err := HttpQuery(RESTAddr + "/cosmos/staking/v1beta1/validators?status=BOND_STATUS_BONDED&pagination.limit=300") + route := getValidatorsRoute(cfg) + res, err := HttpQuery(RESTAddr + route + "?status=BOND_STATUS_BONDED&pagination.limit=300") if err != nil { zap.L().Fatal("", zap.Bool("Success", false), zap.String("err", err.Error())) } diff --git a/rest/delegations.go b/rest/delegations.go index 5ac58ec..2d4ce01 100644 --- a/rest/delegations.go +++ b/rest/delegations.go @@ -5,6 +5,7 @@ import ( "fmt" "strings" + "github.com/jim380/Cendermint/config" "go.uber.org/zap" ) @@ -28,11 +29,12 @@ type delegationRes []struct { } } -func (rd *RESTData) getDelegations() { +func (rd *RESTData) getDelegations(cfg config.Config) { var delInfo delegationsInfo var delRes map[string][]string = make(map[string][]string) - res, err := HttpQuery(RESTAddr + "/cosmos/staking/v1beta1/validators/" + OperAddr + "/delegations" + "?pagination.limit=1000") + route := getValidatorByAddressRoute(cfg) + res, err := HttpQuery(RESTAddr + route + OperAddr + "/delegations" + "?pagination.limit=1000") if err != nil { zap.L().Fatal("", zap.Bool("Success", false), zap.String("err", err.Error())) } diff --git a/rest/gov.go b/rest/gov.go index e46c1be..9441829 100644 --- a/rest/gov.go +++ b/rest/gov.go @@ -7,6 +7,7 @@ import ( "go.uber.org/zap" + "github.com/jim380/Cendermint/config" utils "github.com/jim380/Cendermint/utils" ) @@ -35,7 +36,7 @@ type voteInfo struct { } `json:"vote"` } -func (rd *RESTData) getGovInfo() { +func (rd *RESTData) getGovInfo(cfg config.Config) { var ( g gov gi govInfo @@ -45,7 +46,8 @@ func (rd *RESTData) getGovInfo() { inVotingDidNotVote int ) - res, err := HttpQuery(RESTAddr + "/cosmos/gov/v1beta1/proposals?pagination.limit=1000") + route := getProposalsRoute(cfg) + res, err := HttpQuery(RESTAddr + route + "?pagination.limit=1000") if err != nil { zap.L().Fatal("", zap.Bool("Success", false), zap.String("err", err.Error())) } @@ -67,17 +69,15 @@ func (rd *RESTData) getGovInfo() { for _, value := range proposalsInVoting { var voteInfo voteInfo - res, err := HttpQuery(RESTAddr + "/cosmos/gov/v1beta1/proposals/" + value + "/votes/" + utils.GetAccAddrFromOperAddr(OperAddr)) + res, err := HttpQuery(RESTAddr + route + value + "/votes/" + utils.GetAccAddrFromOperAddr(OperAddr)) if err != nil { zap.L().Fatal("", zap.Bool("Success", false), zap.String("err", err.Error())) } json.Unmarshal(res, &voteInfo) if voteInfo.Votes.Option != "" { inVotingVoted++ - //fmt.Println(value + ":Voter voted") } else { inVotingDidNotVote++ - //fmt.Println(value + ":Voter didn't vote") } } totalProposalsCount, _ := strconv.ParseFloat(totalProposals[len(totalProposals)-1], 64) diff --git a/rest/gravity.go b/rest/gravity.go index 6af1dcd..abee70f 100644 --- a/rest/gravity.go +++ b/rest/gravity.go @@ -40,14 +40,6 @@ type ValsetReward struct { Amount string `json:"amount"` } -// type erc20Price struct { -// contractAddr `json:"0xe54fbaecc50731afe54924c40dfd1274f718fe02"` -// } - -// type contractAddr struct { -// ERC20Price float64 `json:"usd"` -// } - type ethPrice struct { ETHUSD `json:"ethereum"` } @@ -130,8 +122,6 @@ type member struct { func (rd *RESTData) getUmeePrice() { var p umeePrice - // contractAddr := os.Getenv("CONTRACT_ADDR") - // res, err := HttpQuery("https://peggo-fakex-qhcqt.ondigitalocean.app/api/v3/simple/token_price/ethereum?contract_addresses=" + contractAddr + "&vs_currencies=usd") res, err := HttpQuery("https://api.coingecko.com/api/v3/simple/price?ids=umee&vs_currencies=usd") if err != nil { @@ -145,7 +135,8 @@ func (rd *RESTData) getUmeePrice() { func (rd *RESTData) getBatchFees() { var b batchFees - res, err := HttpQuery(RESTAddr + "/gravity/v1beta/batchfees") + route := getBatchFeesRoute() + res, err := HttpQuery(RESTAddr + route) if err != nil { zap.L().Fatal("", zap.Bool("Success", false), zap.String("err", err.Error())) } @@ -163,7 +154,8 @@ func (rd *RESTData) getBatchFees() { func (rd *RESTData) getBatchesFees() { var b batches - res, err := HttpQuery(RESTAddr + "/gravity/v1beta1/batch/outgoingtx") + route := getBatchesFeesRoute() + res, err := HttpQuery(RESTAddr + route) if err != nil { zap.L().Fatal("", zap.Bool("Success", false), zap.String("err", err.Error())) } @@ -184,7 +176,8 @@ func (rd *RESTData) getBridgeFees() { var p ethPrice var bf float64 - res, err := HttpQuery("https://api.coingecko.com/api/v3/simple/price?ids=ethereum&vs_currencies=usd") + route := getBridgeFeesRoute() + res, err := HttpQuery(route) if err != nil { zap.L().Fatal("", zap.Bool("Success", false), zap.String("err", err.Error())) } @@ -200,8 +193,8 @@ func (rd *RESTData) getBridgeParams() { var params gravityParams rd.GravityInfo.GravityActive = 0.0 - - res, err := HttpQuery(RESTAddr + "/gravity/v1beta/params") + route := getBridgeParamsRoute() + res, err := HttpQuery(RESTAddr + route) if err != nil { zap.L().Fatal("", zap.Bool("Success", false), zap.String("err", err.Error())) } @@ -220,7 +213,8 @@ func (rd *RESTData) getOracleEventNonce() { var evt oracleEventNonce orchAddr := os.Getenv("UMEE_ORCH_ADDR") - res, err := HttpQuery(RESTAddr + "/gravity/v1beta/oracle/eventnonce/" + orchAddr) + route := getOracleEventNonceByAddressRoute() + res, err := HttpQuery(RESTAddr + route + orchAddr) if err != nil { zap.L().Fatal("", zap.Bool("Success", false), zap.String("err", err.Error())) } @@ -234,7 +228,8 @@ func (rd *RESTData) getValSet() { var vsResult map[string]string = make(map[string]string) - res, err := HttpQuery(RESTAddr + "/gravity/v1beta/valset/current") + route := getCurrentValidatorSetRoute() + res, err := HttpQuery(RESTAddr + route) if err != nil { zap.L().Fatal("", zap.Bool("Success", false), zap.String("err", err.Error())) } diff --git a/rest/ibc.go b/rest/ibc.go index c7b12a7..2bb5565 100644 --- a/rest/ibc.go +++ b/rest/ibc.go @@ -5,6 +5,7 @@ import ( "fmt" "strings" + "github.com/jim380/Cendermint/config" "go.uber.org/zap" ) @@ -80,11 +81,13 @@ type counterpartyConnection struct { } } -func (rd *RESTData) getIBCChannels() { +func (rd *RESTData) getIBCChannels(cfg config.Config) { var ibcInfo ibcChannelInfo var ibcChannels map[string][]string = make(map[string][]string) + ibcInfo.OpenChannels = 0 - res, err := HttpQuery(RESTAddr + "/ibc/core/channel/v1/channels?pagination.limit=1000000") + route := getIBCChannelsRoute(cfg) + res, err := HttpQuery(RESTAddr + route + "?pagination.limit=1000000") if err != nil { zap.L().Fatal("", zap.Bool("Success", false), zap.String("err", err.Error())) } @@ -116,11 +119,13 @@ func (rd *RESTData) getIBCChannels() { rd.IBC.IBCInfo.ibcChannelInfo = ibcInfo } -func (rd *RESTData) getIBCConnections() { +func (rd *RESTData) getIBCConnections(cfg config.Config) { var ibcInfo ibcConnectionInfo var ibcConnections map[string][]string = make(map[string][]string) + ibcInfo.OpenConnections = 0 - res, err := HttpQuery(RESTAddr + "/ibc/core/connection/v1/connections?pagination.limit=100000") + route := getIBCConnectionsRoute(cfg) + res, err := HttpQuery(RESTAddr + route + "?pagination.limit=100000") if err != nil { zap.L().Fatal("", zap.Bool("Success", false), zap.String("err", err.Error())) } diff --git a/rest/inflation.go b/rest/inflation.go index a877c87..195e3bd 100644 --- a/rest/inflation.go +++ b/rest/inflation.go @@ -6,6 +6,7 @@ import ( "go.uber.org/zap" + "github.com/jim380/Cendermint/config" utils "github.com/jim380/Cendermint/utils" ) @@ -21,14 +22,15 @@ type inflation_iris struct { } } -func (rd *RESTData) getInflation(chain string, denom string) { +func (rd *RESTData) getInflation(cfg config.Config, denom string) { var result string - switch chain { + route := getInflationRoute(cfg) + res, err := HttpQuery(RESTAddr + route) + + switch cfg.Chain { case "iris": var i inflation_iris - - res, err := HttpQuery(RESTAddr + "/irishub/mint/params") if err != nil { zap.L().Fatal("", zap.Bool("Success", false), zap.String("err", "Failed to connect to REST-Server")) } @@ -42,7 +44,7 @@ func (rd *RESTData) getInflation(chain string, denom string) { default: var i inflation - res, err := HttpQuery(RESTAddr + "/minting/inflation") // route does not existing in osmosis + res, err := HttpQuery(RESTAddr + route) // route does not existing in osmosis if err != nil { zap.L().Fatal("", zap.Bool("Success", false), zap.String("err", err.Error())) } diff --git a/rest/node.go b/rest/node.go index cda26fd..c73f4b0 100644 --- a/rest/node.go +++ b/rest/node.go @@ -4,6 +4,7 @@ import ( "encoding/json" "strings" + "github.com/jim380/Cendermint/config" "go.uber.org/zap" ) @@ -27,10 +28,11 @@ type appVersion struct { SDKVersion string `json:"cosmos_sdk_version"` } -func (rd *RESTData) getNodeInfo() { +func (rd *RESTData) getNodeInfo(cfg config.Config) { var nodeInfo nodeInfo - res, err := HttpQuery(RESTAddr + "/cosmos/base/tendermint/v1beta1/node_info") + route := getNodeInfoRoute(cfg) + res, err := HttpQuery(RESTAddr + route) if err != nil { zap.L().Fatal("", zap.Bool("Success", false), zap.String("err", err.Error())) } diff --git a/rest/rest.go b/rest/rest.go index e20635a..bfd4d1b 100644 --- a/rest/rest.go +++ b/rest/rest.go @@ -8,6 +8,7 @@ import ( "sync" "time" + "github.com/jim380/Cendermint/config" utils "github.com/jim380/Cendermint/utils" "go.uber.org/zap" ) @@ -28,7 +29,7 @@ type RESTData struct { StakingPool stakingPool Slashing slashingInfo Validatorsets map[string][]string - Validators validator + Validator validator Delegations delegationsInfo Balances []Coin Rewards []Coin @@ -56,7 +57,7 @@ func (rpc RPCData) new() *RPCData { return &RPCData{Validatorsets: make(map[string][]string)} } -func GetData(chain string, blockHeight int64, blockData Blocks, denom string) *RESTData { +func GetData(cfg config.Config, blockHeight int64, blockData Blocks, denom string) *RESTData { // rpc var rpcData RPCData rpc := rpcData.new() @@ -69,35 +70,38 @@ func GetData(chain string, blockHeight int64, blockData Blocks, denom string) *R wg := sync.WaitGroup{} wg.Add(1) go func() { - rpc.getConsensusDump() - rd.getStakingPool(denom) - rd.getSlashingParams() - rd.getInflation(chain, denom) - rd.getGovInfo() - rd.getValidatorsets(blockHeight) - rd.getValidator() - valMap, found := rd.Validatorsets[rd.Validators.ConsPubKey.Key] + rpc.getConsensusDump(cfg) + rd.getStakingPool(cfg, denom) + rd.getSlashingParams(cfg) + rd.getInflation(cfg, denom) + rd.getGovInfo(cfg) + // TO-DO fix finding validator in active set for sdk version >= v0.46 + rd.getValidatorsets(cfg, blockHeight) + rd.getValidator(cfg) + + valMap, found := rd.Validatorsets[rd.Validator.ConsPubKey.Key] if !found { zap.L().Fatal("", zap.Bool("Success", false), zap.String("err", "Validator not found in the active set")) } - rd.getBalances() - rd.getRewardsCommission() - rd.getSigningInfo(valMap[0]) + + rd.getBalances(cfg) + rd.getRewardsCommission(cfg) + rd.getSigningInfo(cfg, valMap[0]) consHexAddr := utils.Bech32AddrToHexAddr(valMap[0]) rd.getCommit(blockData, consHexAddr) - zap.L().Info("", zap.Bool("Success", true), zap.String("Moniker", rd.Validators.Description.Moniker)) + zap.L().Info("", zap.Bool("Success", true), zap.String("Moniker", rd.Validator.Description.Moniker)) zap.L().Info("", zap.Bool("Success", true), zap.String("VP", valMap[1])) zap.L().Info("", zap.Bool("Success", true), zap.String("Precommit", fmt.Sprintf("%f", rd.Commit.ValidatorPrecommitStatus))) zap.L().Info("\t", zap.Bool("Success", true), zap.String("Balances", fmt.Sprint(rd.Balances))) zap.L().Info("\t", zap.Bool("Success", true), zap.String("Rewards", fmt.Sprint(rd.Rewards))) zap.L().Info("\t", zap.Bool("Success", true), zap.String("Commission", fmt.Sprint(rd.Commission))) - rd.getIBCChannels() - rd.getIBCConnections() - rd.getNodeInfo() - rd.getTxInfo(blockHeight) + rd.getIBCChannels(cfg) + rd.getIBCConnections(cfg) + rd.getNodeInfo(cfg) + rd.getTxInfo(cfg, blockHeight) rd.computerTPS(blockData) - rd.getUpgradeInfo() + rd.getUpgradeInfo(cfg) rd.getAkashDeployments() // gravity rd.getBridgeParams() @@ -124,12 +128,12 @@ func (rd *RESTData) computerTPS(blockData Blocks) { rd.TxInfo.TPS = tps } -func GetDelegationsData(chain string, blockHeight int64, blockData Blocks, denom string) *RESTData { +func GetDelegationsData(cfg config.Config, chain string, blockHeight int64, blockData Blocks, denom string) *RESTData { var restData RESTData AccAddr = utils.GetAccAddrFromOperAddr(OperAddr) rd := restData.new(blockHeight) - rd.getDelegations() + rd.getDelegations(cfg) return rd } diff --git a/rest/reward_commission.go b/rest/reward_commission.go index 1aa54e8..241c946 100644 --- a/rest/reward_commission.go +++ b/rest/reward_commission.go @@ -4,6 +4,7 @@ import ( "encoding/json" "strings" + "github.com/jim380/Cendermint/config" "go.uber.org/zap" ) @@ -20,10 +21,11 @@ type Commission struct { Commission []Coin `json:"commission"` } -func (rd *RESTData) getRewardsCommission() { +func (rd *RESTData) getRewardsCommission(cfg config.Config) { var rc rewardsAndCommisson - res, err := HttpQuery(RESTAddr + "/distribution/validators/" + OperAddr) + route := getValidatorDistributionByAddressRoute(cfg) + res, err := HttpQuery(RESTAddr + route + OperAddr) if err != nil { zap.L().Fatal("", zap.Bool("Success", false), zap.String("err", err.Error())) } diff --git a/rest/routes.go b/rest/routes.go new file mode 100644 index 0000000..da7caf9 --- /dev/null +++ b/rest/routes.go @@ -0,0 +1,140 @@ +package rest + +import ( + "github.com/jim380/Cendermint/config" +) + +/*********************** + * SDK Routes +************************/ +func getBlockInfoRoute(cfg config.Config) string { + if cfg.IsLegacySDKVersion() { + return "/blocks/latest" + } else { + return "/cosmos/base/tendermint/v1beta1/blocks/latest" + } +} + +func getBlockByHeightRoute(cfg config.Config) string { + if cfg.IsLegacySDKVersion() { + return "/blocks/" + } else { + return "/cosmos/base/tendermint/v1beta1/blocks/" + } +} + +func getValidatorSetByHeightRoute(cfg config.Config) string { + if cfg.IsLegacySDKVersion() { + return "/validatorsets/" + } else { + return "/cosmos/base/tendermint/v1beta1/validatorsets/" + } +} + +func getValidatorDistributionByAddressRoute(cfg config.Config) string { + if cfg.IsLegacySDKVersion() { + return "/distribution/validators/" + } else { + return "/cosmos/distribution/v1beta1/validators/" + } +} + +func getInflationRoute(cfg config.Config) string { + if cfg.Chain == "iris" { + return "/irishub/mint/params" + } else if cfg.IsLegacySDKVersion() { + return "/minting/inflation" + + } else { + return "/cosmos/mint/v1beta1/inflation" + } +} + +func getBalancesByAddressRoute(cfg config.Config) string { + return "/cosmos/bank/v1beta1/balances/" +} + +func getStakingPoolRoute(cfg config.Config) string { + return "/cosmos/staking/v1beta1/pool" +} + +func getSupplyRoute(cfg config.Config) string { + return "/cosmos/bank/v1beta1/supply/" +} + +func getValidatorByAddressRoute(cfg config.Config) string { + return "/cosmos/staking/v1beta1/validators/" +} + +func getValidatorsRoute(cfg config.Config) string { + return "/cosmos/staking/v1beta1/validators" +} + +func getTxByHeightRoute(cfg config.Config) string { + return "/cosmos/tx/v1beta1/txs?events=tx.height=" +} + +func getSlashingParamsRoute(cfg config.Config) string { + return "/cosmos/slashing/v1beta1/params" +} + +func getSigningInfoByAddressRoute(cfg config.Config) string { + return "/cosmos/slashing/v1beta1/signing_infos/" +} + +func getProposalsRoute(cfg config.Config) string { + return "/cosmos/gov/v1beta1/proposals" +} + +func getNodeInfoRoute(cfg config.Config) string { + return "/cosmos/base/tendermint/v1beta1/node_info" +} + +func getUpgradeCurrentPlanRoute(cfg config.Config) string { + return "/cosmos/upgrade/v1beta1/current_plan" +} + +/*********************** + * IBC Routes +************************/ +func getIBCChannelsRoute(cfg config.Config) string { + return "/ibc/core/channel/v1/channels" +} + +func getIBCConnectionsRoute(cfg config.Config) string { + return "/ibc/core/connection/v1/connections" +} + +/*********************** + * Gravity Bridge Routes +************************/ +func getBatchFeesRoute() string { + return "/gravity/v1beta/batchfees" +} + +func getBatchesFeesRoute() string { + return "/gravity/v1beta1/batch/outgoingtx" +} + +func getBridgeFeesRoute() string { + return "https://api.coingecko.com/api/v3/simple/price?ids=ethereum&vs_currencies=usd" +} + +func getBridgeParamsRoute() string { + return "/gravity/v1beta/params" +} + +func getOracleEventNonceByAddressRoute() string { + return "/gravity/v1beta/oracle/eventnonce/" +} + +func getCurrentValidatorSetRoute() string { + return "/gravity/v1beta/valset/current" +} + +/*********************** + * Akash Routes +************************/ +func getDeploymentsRoute() string { + return "/akash/deployment/v1beta2/deployments/list" +} diff --git a/rest/slashing.go b/rest/slashing.go index 2cae068..2c6a698 100644 --- a/rest/slashing.go +++ b/rest/slashing.go @@ -4,6 +4,7 @@ import ( "encoding/json" "strings" + "github.com/jim380/Cendermint/config" "go.uber.org/zap" ) @@ -28,10 +29,11 @@ type SigningInfo struct { MissedBlocksCounter string `json:"missed_blocks_counter"` } -func (rd *RESTData) getSlashingParams() { +func (rd *RESTData) getSlashingParams(cfg config.Config) { var d slashingInfo - res, err := HttpQuery(RESTAddr + "/cosmos/slashing/v1beta1/params") + route := getSlashingParamsRoute(cfg) + res, err := HttpQuery(RESTAddr + route) if err != nil { zap.L().Fatal("", zap.Bool("Success", false), zap.String("err", err.Error())) } @@ -44,10 +46,11 @@ func (rd *RESTData) getSlashingParams() { rd.Slashing.Params = d.Params } -func (rd *RESTData) getSigningInfo(consAddr string) { +func (rd *RESTData) getSigningInfo(cfg config.Config, consAddr string) { var d slashingInfo - res, err := HttpQuery(RESTAddr + "/cosmos/slashing/v1beta1/signing_infos/" + consAddr) + route := getSigningInfoByAddressRoute(cfg) + res, err := HttpQuery(RESTAddr + route + consAddr) if err != nil { zap.L().Fatal("", zap.Bool("Success", false), zap.String("err", err.Error())) } diff --git a/rest/stakingPool.go b/rest/stakingPool.go index 10b4a85..3431e33 100644 --- a/rest/stakingPool.go +++ b/rest/stakingPool.go @@ -6,6 +6,7 @@ import ( "go.uber.org/zap" + "github.com/jim380/Cendermint/config" utils "github.com/jim380/Cendermint/utils" ) @@ -21,10 +22,11 @@ type totalSupply struct { Amount Coin } -func (rd *RESTData) getStakingPool(denom string) { +func (rd *RESTData) getStakingPool(cfg config.Config, denom string) { var sp stakingPool - res, err := HttpQuery(RESTAddr + "/cosmos/staking/v1beta1/pool") + route := getStakingPoolRoute(cfg) + res, err := HttpQuery(RESTAddr + route) if err != nil { zap.L().Fatal("", zap.Bool("Success", false), zap.String("err", "Failed to connect to REST-Server")) } @@ -35,14 +37,15 @@ func (rd *RESTData) getStakingPool(denom string) { zap.L().Info("", zap.Bool("Success", true), zap.String("Bonded tokens", sp.Pool.Bonded_tokens)) } - sp.Pool.Total_supply = getTotalSupply(denom, zap.L()) + sp.Pool.Total_supply = getTotalSupply(cfg, denom, zap.L()) rd.StakingPool = sp } -func getTotalSupply(denom string, log *zap.Logger) float64 { +func getTotalSupply(cfg config.Config, denom string, log *zap.Logger) float64 { var ts totalSupply - res, err := HttpQuery(RESTAddr + "/cosmos/bank/v1beta1/supply/" + denom) + route := getSupplyRoute(cfg) + res, err := HttpQuery(RESTAddr + route + denom) if err != nil { log.Fatal("", zap.Bool("Success", false), zap.String("err", err.Error())) } diff --git a/rest/tx.go b/rest/tx.go index 53aa9c8..5bb104d 100644 --- a/rest/tx.go +++ b/rest/tx.go @@ -6,6 +6,7 @@ import ( "strconv" "strings" + "github.com/jim380/Cendermint/config" "go.uber.org/zap" ) @@ -86,11 +87,12 @@ type txResp []struct { GasUsd string `json:"gas_used"` } -func (rd *RESTData) getTxInfo(currentBlockHeight int64) { +func (rd *RESTData) getTxInfo(cfg config.Config, currentBlockHeight int64) { var txInfo txInfo var txRes txResult - res, err := HttpQuery(RESTAddr + "/cosmos/tx/v1beta1/txs?events=tx.height=" + fmt.Sprintf("%v", currentBlockHeight)) + route := getTxByHeightRoute(cfg) + res, err := HttpQuery(RESTAddr + route + fmt.Sprintf("%v", currentBlockHeight)) if err != nil { zap.L().Fatal("", zap.Bool("Success", false), zap.String("err", err.Error())) } diff --git a/rest/upgrade.go b/rest/upgrade.go index dc34119..8917be7 100644 --- a/rest/upgrade.go +++ b/rest/upgrade.go @@ -5,6 +5,7 @@ import ( "strconv" "strings" + "github.com/jim380/Cendermint/config" "go.uber.org/zap" ) @@ -18,10 +19,11 @@ type upgradeInfo struct { } } -func (rd *RESTData) getUpgradeInfo() { +func (rd *RESTData) getUpgradeInfo(cfg config.Config) { var upgradeInfo upgradeInfo - res, err := HttpQuery(RESTAddr + "/cosmos/upgrade/v1beta1/current_plan") + route := getUpgradeCurrentPlanRoute(cfg) + res, err := HttpQuery(RESTAddr + route) if err != nil { zap.L().Fatal("", zap.Bool("Success", false), zap.String("err", err.Error())) } diff --git a/rest/validators.go b/rest/validators.go index ef9aac6..8e625d8 100644 --- a/rest/validators.go +++ b/rest/validators.go @@ -4,6 +4,7 @@ import ( "encoding/json" "strings" + "github.com/jim380/Cendermint/config" "go.uber.org/zap" ) @@ -44,10 +45,11 @@ type commission_rates struct { Max_change_rate string `json:"max_change_rate"` } -func (rd *RESTData) getValidator() { +func (rd *RESTData) getValidator(cfg config.Config) { var v validators - res, err := HttpQuery(RESTAddr + "/cosmos/staking/v1beta1/validators/" + OperAddr) + route := getValidatorByAddressRoute(cfg) + res, err := HttpQuery(RESTAddr + route + OperAddr) if err != nil { zap.L().Fatal("", zap.Bool("Success", false), zap.String("err", err.Error())) } @@ -58,5 +60,5 @@ func (rd *RESTData) getValidator() { zap.L().Fatal("", zap.Bool("Success", false), zap.String("err", string(res))) } - rd.Validators = v.Validator + rd.Validator = v.Validator } diff --git a/rest/validatorsets.go b/rest/validatorsets.go index 59c949d..e94b516 100644 --- a/rest/validatorsets.go +++ b/rest/validatorsets.go @@ -7,70 +7,117 @@ import ( "strconv" "strings" + "github.com/jim380/Cendermint/config" "go.uber.org/zap" ) -type validatorsets struct { +type validatorsetsLegacy struct { Height string `json:"height"` Result struct { Block_Height string `json:"block_height"` Validators []struct { - ConsAddr string `json:"address"` - ConsPubKey consPubKeyValSet `json:"pub_key"` - ProposerPriority string `json:"proposer_priority"` - VotingPower string `json:"voting_power"` + ConsAddr string `json:"address"` + ConsPubKey consPubKeyValSetLegacy `json:"pub_key"` + ProposerPriority string `json:"proposer_priority"` + VotingPower string `json:"voting_power"` } } } -type consPubKeyValSet struct { +type validatorsets struct { + Block_Height string `json:"block_height"` + Validators []struct { + ConsAddr string `json:"address"` + ConsPubKey consPubKeyValSet `json:"pub_key"` + ProposerPriority string `json:"proposer_priority"` + VotingPower string `json:"voting_power"` + } `json:"validators"` +} + +type consPubKeyValSetLegacy struct { Type string `json:"type"` Key string `json:"value"` } -func (rd *RESTData) getValidatorsets(currentBlockHeight int64) { - var vSets, vSets2, vSets3, vsetTest validatorsets - var vSetsResultFinal map[string][]string - var vSetsResult map[string][]string = make(map[string][]string) - var vSetsResult2 map[string][]string = make(map[string][]string) - var vSetsResult3 map[string][]string = make(map[string][]string) - - shouldRunPages := testPageLimit(currentBlockHeight, &vsetTest, 3) +type consPubKeyValSet struct { + Type string `json:"@type"` + Key string `json:"key"` +} - if shouldRunPages { - runPages(currentBlockHeight, &vSets, vSetsResult, 1) - runPages(currentBlockHeight, &vSets2, vSetsResult2, 2) - runPages(currentBlockHeight, &vSets3, vSetsResult3, 3) +func (rd *RESTData) getValidatorsets(cfg config.Config, currentBlockHeight int64) { + var vSetsResultFinal map[string][]string - for _, value := range vSets.Result.Validators { - // populate the validatorset map => [ConsPubKey][]string{ConsAddr, VotingPower, ProposerPriority} - vSetsResult[value.ConsPubKey.Key] = []string{value.ConsAddr, value.VotingPower, value.ProposerPriority, "0"} + if cfg.IsLegacySDKVersion() { + var vSets, vSets2, vSets3, vsetTest validatorsetsLegacy + var vSetsResult map[string][]string = make(map[string][]string) + var vSetsResult2 map[string][]string = make(map[string][]string) + var vSetsResult3 map[string][]string = make(map[string][]string) + + shouldRunPages := testPageLimit(cfg, currentBlockHeight, &vsetTest, 3) + + if shouldRunPages { + runPages(cfg, currentBlockHeight, &vSets, vSetsResult, 1) + runPages(cfg, currentBlockHeight, &vSets2, vSetsResult2, 2) + runPages(cfg, currentBlockHeight, &vSets3, vSetsResult3, 3) + + for _, value := range vSets.Result.Validators { + // populate the validatorset map => [ConsPubKey][]string{ConsAddr, VotingPower, ProposerPriority} + vSetsResult[value.ConsPubKey.Key] = []string{value.ConsAddr, value.VotingPower, value.ProposerPriority, "0"} + } + + for _, value := range vSets2.Result.Validators { + // populate the validatorset map => [ConsPubKey][]string{ConsAddr, VotingPower, ProposerPriority} + vSetsResult2[value.ConsPubKey.Key] = []string{value.ConsAddr, value.VotingPower, value.ProposerPriority, "0"} + } + + for _, value := range vSets3.Result.Validators { + // populate the validatorset map => [ConsPubKey][]string{ConsAddr, VotingPower, ProposerPriority} + vSetsResult3[value.ConsPubKey.Key] = []string{value.ConsAddr, value.VotingPower, value.ProposerPriority, "0"} + } + vSetsResultTemp := mergeMap(vSetsResult, vSetsResult2) + vSetsResultFinal = mergeMap(vSetsResultTemp, vSetsResult3) + zap.L().Info("", zap.Bool("Success", true), zap.String("Active validators", fmt.Sprint(len(vSets.Result.Validators)+len(vSets2.Result.Validators)+len(vSets3.Result.Validators)))) + } else { + runPages(cfg, currentBlockHeight, &vSets, vSetsResult, 1) + for _, value := range vSets.Result.Validators { + // populate the validatorset map => [ConsPubKey][]string{ConsAddr, VotingPower, ProposerPriority} + vSetsResult[value.ConsPubKey.Key] = []string{value.ConsAddr, value.VotingPower, value.ProposerPriority, "0"} + } + vSetsResultFinal = vSetsResult } + } else { + var vSets validatorsets + var vSetsResult map[string][]string = make(map[string][]string) - for _, value := range vSets2.Result.Validators { - // populate the validatorset map => [ConsPubKey][]string{ConsAddr, VotingPower, ProposerPriority} - vSetsResult2[value.ConsPubKey.Key] = []string{value.ConsAddr, value.VotingPower, value.ProposerPriority, "0"} + route := getValidatorSetByHeightRoute(cfg) + res, err := HttpQuery(RESTAddr + route + fmt.Sprint(currentBlockHeight)) + if err != nil { + zap.L().Fatal("", zap.Bool("Success", false), zap.String("err", err.Error())) } - for _, value := range vSets3.Result.Validators { - // populate the validatorset map => [ConsPubKey][]string{ConsAddr, VotingPower, ProposerPriority} - vSetsResult3[value.ConsPubKey.Key] = []string{value.ConsAddr, value.VotingPower, value.ProposerPriority, "0"} + json.Unmarshal(res, &vSets) + + if strings.Contains(string(res), "not found") { + zap.L().Fatal("", zap.Bool("Success", false), zap.String("err", string(res))) + } else if strings.Contains(string(res), "error:") || strings.Contains(string(res), "error\\\":") { + zap.L().Fatal("", zap.Bool("Success", false), zap.String("err", string(res))) } - vSetsResultTemp := mergeMap(vSetsResult, vSetsResult2) - vSetsResultFinal = mergeMap(vSetsResultTemp, vSetsResult3) - } else { - runPages(currentBlockHeight, &vSets, vSetsResult, 1) - for _, value := range vSets.Result.Validators { + for _, value := range vSets.Validators { // populate the validatorset map => [ConsPubKey][]string{ConsAddr, VotingPower, ProposerPriority} vSetsResult[value.ConsPubKey.Key] = []string{value.ConsAddr, value.VotingPower, value.ProposerPriority, "0"} } + vSetsResultFinal = vSetsResult + zap.L().Info("", zap.Bool("Success", true), zap.String("Active validators", fmt.Sprint(len(vSets.Validators)))) } rd.Validatorsets = Sort(vSetsResultFinal) - zap.L().Info("", zap.Bool("Success", true), zap.String("Active validators", fmt.Sprint(len(vSets.Result.Validators)+len(vSets2.Result.Validators)+len(vSets3.Result.Validators)))) + for key, value := range rd.Validatorsets { + zap.L().Debug("", zap.Bool("Success", true), zap.String(key, strings.Join(value, ", "))) + } + } func Sort(mapValue map[string][]string) map[string][]string { @@ -102,8 +149,11 @@ func mergeMap(a map[string][]string, b map[string][]string) map[string][]string return a } -func runPages(currentBlockHeight int64, vSets *validatorsets, vSetsResult map[string][]string, pages int) { - res, err := HttpQuery(RESTAddr + "/validatorsets/" + fmt.Sprint(currentBlockHeight) + "?page=" + strconv.Itoa(pages) + "&limit=100") +func runPages(cfg config.Config, currentBlockHeight int64, vSets *validatorsetsLegacy, vSetsResult map[string][]string, pages int) { + route := getValidatorSetByHeightRoute(cfg) + + res, err := HttpQuery(RESTAddr + route + fmt.Sprint(currentBlockHeight)) + if err != nil { zap.L().Fatal("", zap.Bool("Success", false), zap.String("err", err.Error())) } @@ -122,10 +172,11 @@ func runPages(currentBlockHeight int64, vSets *validatorsets, vSetsResult map[st } } -func testPageLimit(currentBlockHeight int64, vSets *validatorsets, maxPageNumber int64) bool { +func testPageLimit(cfg config.Config, currentBlockHeight int64, vSets *validatorsetsLegacy, maxPageNumber int64) bool { multiPagesSupported := true - res, err := HttpQuery(RESTAddr + "/validatorsets/" + fmt.Sprint(currentBlockHeight) + "?page=3") + route := getValidatorSetByHeightRoute(cfg) + res, err := HttpQuery(RESTAddr + route + fmt.Sprint(currentBlockHeight) + "?page=3") if err != nil { zap.L().Fatal("", zap.Bool("Success", false), zap.String("err", err.Error())) } diff --git a/utils/converter.go b/utils/converter.go index fb47ec9..72a8db0 100644 --- a/utils/converter.go +++ b/utils/converter.go @@ -1,6 +1,9 @@ package utils import ( + "encoding/base64" + "encoding/hex" + "fmt" "strconv" ) @@ -20,3 +23,27 @@ func BoolToFloat64(b bool) float64 { return result } + +func HexToBase64(hexAddr string) string { + bytes, err := decodeHex([]byte(hexAddr)) + if err != nil { + fmt.Println(err) + } + return string(base64Encode(bytes)) +} + +func decodeHex(input []byte) ([]byte, error) { + db := make([]byte, hex.DecodedLen(len(input))) + _, err := hex.Decode(db, input) + if err != nil { + return nil, err + } + return db, nil +} + +func base64Encode(input []byte) []byte { + eb := make([]byte, base64.StdEncoding.EncodedLen(len(input))) + base64.StdEncoding.Encode(eb, input) + + return eb +}