From 1b398dd27a0df2f3f31b8c6ed0b6492bcb1a28af Mon Sep 17 00:00:00 2001 From: Shahbaz Nazir Date: Wed, 29 Nov 2023 15:37:07 +0100 Subject: [PATCH 01/12] refactor and log improvements --- .gitignore | 6 + cmd/relayer_exporter/relayer_exporter.go | 11 +- config.yaml | 78 +++++--- pkg/chain/chain.go | 17 ++ pkg/collector/collector.go | 216 +--------------------- pkg/collector/ibc_collector.go | 167 +++++++++++++++++ pkg/collector/wallet_balance_collector.go | 71 +++++++ pkg/config/config.go | 78 +++++--- pkg/config/config_test.go | 114 ++++++++---- pkg/ibc/ibc.go | 1 + 10 files changed, 452 insertions(+), 307 deletions(-) create mode 100644 pkg/collector/ibc_collector.go create mode 100644 pkg/collector/wallet_balance_collector.go diff --git a/.gitignore b/.gitignore index b7142e8..149d9a7 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,12 @@ *.so *.dylib +# vscode +.vscode + +# direnv +.envrc + # Test binary, built with `go test -c` *.test diff --git a/cmd/relayer_exporter/relayer_exporter.go b/cmd/relayer_exporter/relayer_exporter.go index 4176d73..567eaad 100644 --- a/cmd/relayer_exporter/relayer_exporter.go +++ b/cmd/relayer_exporter/relayer_exporter.go @@ -8,6 +8,7 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" + "go.uber.org/zap" "github.com/archway-network/relayer_exporter/pkg/collector" "github.com/archway-network/relayer_exporter/pkg/config" @@ -45,11 +46,12 @@ func main() { log.Info( fmt.Sprintf( - "Getting IBC paths from %s/%s/%s on GitHub", + "Github IBC registry: %s/%s", cfg.GitHub.Org, cfg.GitHub.Repo, - cfg.GitHub.IBCDir, ), + zap.String("Mainnet Directory", cfg.GitHub.IBCDir), + zap.String("Testnet Directory", cfg.GitHub.TestnetsIBCDir), ) // TODO: Add a feature to refresh paths at configured interval @@ -58,7 +60,10 @@ func main() { log.Fatal(err.Error()) } - rpcs := cfg.GetRPCsMap() + rpcs, err := cfg.GetRPCsMap(paths) + if err != nil { + log.Fatal(err.Error()) + } ibcCollector := collector.IBCCollector{ RPCs: rpcs, diff --git a/config.yaml b/config.yaml index 5eb3574..a9ce3db 100644 --- a/config.yaml +++ b/config.yaml @@ -1,26 +1,28 @@ ---- +github: + org: archway-network + repo: networks + dir: _IBC + testnetsDir: testnets/_IBC rpc: + # mainnets - chainName: archway chainId: archway-1 url: https://rpc.mainnet.archway.io:443 - chainName: agoric chainId: agoric-3 - url: https://main.rpc.agoric.net:443 + url: https://agoric.rpc.kjnodes.com:443 - chainName: axelar chainId: axelar-dojo-1 - url: https://axelar-rpc.polkachu.com:443 - - chainName: axelartestnet - chainId: axelar-testnet-lisbon-3 - url: https://rpc-axelar-testnet.imperator.co:443 - - chainName: archwaytestnet - chainId: constantine-3 - url: https://rpc.constantine.archway.tech:443 + url: https://rpc-1.axelar.nodes.guru:443 - chainName: bitcanna chainId: bitcanna-1 url: https://rpc.bitcanna.io:443 - chainName: cosmoshub chainId: cosmoshub-4 url: https://cosmoshub-rpc.stakely.io:443 + - chainName: decentr + chainId: mainnet-3 + url: https://poseidon.mainnet.decentr.xyz:443 - chainName: jackal chainId: jackal-1 url: https://jackal-rpc.polkachu.com:443 @@ -29,45 +31,61 @@ rpc: url: https://juno-rpc.publicnode.com:443 - chainName: kujira chainId: kaiyo-1 - url: https://kujira-rpc.polkachu.com:443 + url: https://kujira-rpc.publicnode.com:443 - chainName: noble chainId: noble-1 url: https://noble-rpc.polkachu.com:443 - chainName: nois chainId: nois-1 url: https://nois.rpc.kjnodes.com:443 - - chainName: osmosistestnet - chainId: osmo-test-5 - url: https://rpc.osmotest5.osmosis.zone:443 + - chainName: omniflixhub + chainId: omniflixhub-1 + url: https://omniflix.kingnodes.com:443 - chainName: osmosis chainId: osmosis-1 url: https://osmosis-rpc.stakely.io:443 - chainName: quicksilver chainId: quicksilver-2 url: https://rpc.quicksilver.zone:443 - - chainName: akashtestnet - chainId: sandbox-01 - url: https://rpc.sandbox-01.aksh.pw:443 - chainName: umee chainId: umee-1 url: https://rpc-umee.mzonder.com:443 - chainName: gravitybridge chainId: gravity-bridge-3 url: https://gravitychain.io:26657 - - chainName: omniflixhub - chainId: omniflixhub-1 - url: https://rpc-omniflix.mzonder.com:443 - - chainName: decentr - chainId: mainnet-3 - url: https://poseidon.mainnet.decentr.xyz:443 + - chainName: secretnetwork + chainId: secret-4 + url: https://rpc.secret.express:443 + - chainName: terra2 + chainId: phoenix-1 + url: https://terra-rpc.stakely.io:443 + - chainName: comdex + chainId: comdex-1 + url: https://rpc.comdex.one:443 + - chainName: neutron + chainId: neutron-1 + url: https://rpc-kralum.neutron-1.neutron.org:443 + - chainName: stargaze + chainId: stargaze-1 + url: https://rpc.stargaze-apis.com:443 -github: - org: archway-network - repo: networks - dir: _IBC - testnetsDir: testnets/_IBC + # testnets + - chainName: archwaytestnet + chainId: constantine-3 + url: https://rpc.constantine.archway.tech:443 + - chainName: axelartestnet + chainId: axelar-testnet-lisbon-3 + url: https://axelar-testnet-rpc.qubelabs.io:443 + - chainName: osmosistestnet + chainId: osmo-test-5 + url: https://rpc.osmotest5.osmosis.zone:443 accounts: - - address: archway1l2al7y78500h5akvgt8exwnkpmf2zmk8ky9ht3 - chainName: archwaytestnet - denom: aconst + # Foundation + - address: archway1gpyqzc0aerc85cpk2cm8ec6zkc95x5yqrakskv + chainName: archway + denom: aarch + # PhiLabs + - address: archway1ktka5q3cnsy3ar7qwj2huzz6qj9q4ys7h74l9y + chainName: archway + denom: aarch \ No newline at end of file diff --git a/pkg/chain/chain.go b/pkg/chain/chain.go index 224179f..4219d09 100644 --- a/pkg/chain/chain.go +++ b/pkg/chain/chain.go @@ -2,6 +2,7 @@ package chain import ( "context" + "fmt" "github.com/cosmos/relayer/v2/relayer" "github.com/cosmos/relayer/v2/relayer/chains/cosmos" @@ -54,3 +55,19 @@ func PrepChain(info Info) (*relayer.Chain, error) { return chain, nil } + +func ValidateChainInfo(info Info) error { + if info.ChainID == "" { + return fmt.Errorf("missing chain ID: %v", info) + } + + if info.RPCAddr == "" { + return fmt.Errorf("missing RPC address: %v", info) + } + + if info.ClientID == "" { + return fmt.Errorf("missing client ID: %v", info) + } + + return nil +} diff --git a/pkg/collector/collector.go b/pkg/collector/collector.go index 5cd6ddd..7b55480 100644 --- a/pkg/collector/collector.go +++ b/pkg/collector/collector.go @@ -1,229 +1,17 @@ package collector import ( - "fmt" - "math/big" - "reflect" "regexp" "strings" - "sync" - - "github.com/prometheus/client_golang/prometheus" - "go.uber.org/zap" "github.com/archway-network/relayer_exporter/pkg/config" - "github.com/archway-network/relayer_exporter/pkg/ibc" - log "github.com/archway-network/relayer_exporter/pkg/logger" ) const ( - successStatus = "success" - errorStatus = "error" - clientExpiryMetricName = "cosmos_ibc_client_expiry" - walletBalanceMetricName = "cosmos_wallet_balance" - channelStuckPacketsMetricName = "cosmos_ibc_stuck_packets" -) - -var ( - clientExpiry = prometheus.NewDesc( - clientExpiryMetricName, - "Returns light client expiry in unixtime.", - []string{ - "host_chain_id", - "client_id", - "target_chain_id", - "discord_ids", - "status", - }, - nil, - ) - channelStuckPackets = prometheus.NewDesc( - channelStuckPacketsMetricName, - "Returns stuck packets for a channel.", - []string{ - "src_channel_id", - "dst_channel_id", - "src_chain_id", - "dst_chain_id", - "src_chain_name", - "dst_chain_name", - "discord_ids", - "status", - }, - nil, - ) - walletBalance = prometheus.NewDesc( - walletBalanceMetricName, - "Returns wallet balance for an address on a chain.", - []string{"account", "chain_id", "denom", "status"}, nil, - ) + successStatus = "success" + errorStatus = "error" ) -type IBCCollector struct { - RPCs *map[string]config.RPC - Paths []*config.IBCData -} - -type WalletBalanceCollector struct { - RPCs *map[string]config.RPC - Accounts []config.Account -} - -func (cc IBCCollector) Describe(ch chan<- *prometheus.Desc) { - ch <- clientExpiry - ch <- channelStuckPackets -} - -func (cc IBCCollector) Collect(ch chan<- prometheus.Metric) { - log.Debug( - "Start collecting", - zap.String( - "metrics", - fmt.Sprintf("%s, %s", clientExpiryMetricName, channelStuckPacketsMetricName), - ), - ) - - var wg sync.WaitGroup - - for _, p := range cc.Paths { - wg.Add(1) - - go func(path *config.IBCData) { - defer wg.Done() - - discordIDs := getDiscordIDs(path.Operators) - - // Client info - ci, err := ibc.GetClientsInfo(path, cc.RPCs) - status := successStatus - - if err != nil { - status = errorStatus - - log.Error(err.Error()) - } - - ch <- prometheus.MustNewConstMetric( - clientExpiry, - prometheus.GaugeValue, - float64(ci.ChainAClientExpiration.Unix()), - []string{ - (*cc.RPCs)[path.Chain1.ChainName].ChainID, - path.Chain1.ClientID, - (*cc.RPCs)[path.Chain2.ChainName].ChainID, - discordIDs, - status, - }..., - ) - - ch <- prometheus.MustNewConstMetric( - clientExpiry, - prometheus.GaugeValue, - float64(ci.ChainBClientExpiration.Unix()), - []string{ - (*cc.RPCs)[path.Chain2.ChainName].ChainID, - path.Chain2.ClientID, - (*cc.RPCs)[path.Chain1.ChainName].ChainID, - discordIDs, - status, - }..., - ) - - // Stuck packets - status = successStatus - - stuckPackets, err := ibc.GetChannelsInfo(path, cc.RPCs) - if err != nil { - status = errorStatus - - log.Error(err.Error()) - } - - if !reflect.DeepEqual(stuckPackets, ibc.ChannelsInfo{}) { - for _, sp := range stuckPackets.Channels { - ch <- prometheus.MustNewConstMetric( - channelStuckPackets, - prometheus.GaugeValue, - float64(sp.StuckPackets.Source), - []string{ - sp.Source, - sp.Destination, - (*cc.RPCs)[path.Chain1.ChainName].ChainID, - (*cc.RPCs)[path.Chain2.ChainName].ChainID, - path.Chain1.ChainName, - path.Chain2.ChainName, - discordIDs, - status, - }..., - ) - - ch <- prometheus.MustNewConstMetric( - channelStuckPackets, - prometheus.GaugeValue, - float64(sp.StuckPackets.Destination), - []string{ - sp.Destination, - sp.Source, - (*cc.RPCs)[path.Chain2.ChainName].ChainID, - (*cc.RPCs)[path.Chain1.ChainName].ChainID, - path.Chain2.ChainName, - path.Chain1.ChainName, - discordIDs, - status, - }..., - ) - } - } - }(p) - } - - wg.Wait() - - log.Debug("Stop collecting", zap.String("metric", clientExpiryMetricName)) -} - -func (wb WalletBalanceCollector) Describe(ch chan<- *prometheus.Desc) { - ch <- walletBalance -} - -func (wb WalletBalanceCollector) Collect(ch chan<- prometheus.Metric) { - log.Debug("Start collecting", zap.String("metric", walletBalanceMetricName)) - - var wg sync.WaitGroup - - for _, a := range wb.Accounts { - wg.Add(1) - - go func(account config.Account) { - defer wg.Done() - - balance := 0.0 - status := successStatus - - err := account.GetBalance(wb.RPCs) - if err != nil { - status = errorStatus - - log.Error(err.Error(), zap.Any("account", account)) - } else { - // Convert to a big float to get a float64 for metrics - balance, _ = big.NewFloat(0.0).SetInt(account.Balance.BigInt()).Float64() - } - - ch <- prometheus.MustNewConstMetric( - walletBalance, - prometheus.GaugeValue, - balance, - []string{account.Address, (*wb.RPCs)[account.ChainName].ChainID, account.Denom, status}..., - ) - }(a) - } - - wg.Wait() - - log.Debug("Stop collecting", zap.String("metric", walletBalanceMetricName)) -} - func getDiscordIDs(ops []config.Operator) string { var ids []string diff --git a/pkg/collector/ibc_collector.go b/pkg/collector/ibc_collector.go new file mode 100644 index 0000000..53edd74 --- /dev/null +++ b/pkg/collector/ibc_collector.go @@ -0,0 +1,167 @@ +package collector + +import ( + "fmt" + "reflect" + "sync" + + "github.com/prometheus/client_golang/prometheus" + "go.uber.org/zap" + + "github.com/archway-network/relayer_exporter/pkg/config" + "github.com/archway-network/relayer_exporter/pkg/ibc" + log "github.com/archway-network/relayer_exporter/pkg/logger" +) + +const ( + clientExpiryMetricName = "cosmos_ibc_client_expiry" + channelStuckPacketsMetricName = "cosmos_ibc_stuck_packets" +) + +var ( + clientExpiry = prometheus.NewDesc( + clientExpiryMetricName, + "Returns light client expiry in unixtime.", + []string{ + "host_chain_id", + "client_id", + "target_chain_id", + "discord_ids", + "status", + }, + nil, + ) + channelStuckPackets = prometheus.NewDesc( + channelStuckPacketsMetricName, + "Returns stuck packets for a channel.", + []string{ + "src_channel_id", + "dst_channel_id", + "src_chain_id", + "dst_chain_id", + "src_chain_name", + "dst_chain_name", + "discord_ids", + "status", + }, + nil, + ) +) + +type IBCCollector struct { + RPCs *map[string]config.RPC + Paths []*config.IBCData +} + +func (cc IBCCollector) Describe(ch chan<- *prometheus.Desc) { + ch <- clientExpiry + ch <- channelStuckPackets +} + +func (cc IBCCollector) Collect(ch chan<- prometheus.Metric) { + log.Debug( + "Start collecting", + zap.String( + "metrics", + fmt.Sprintf("%s, %s", clientExpiryMetricName, channelStuckPacketsMetricName), + ), + ) + + var wg sync.WaitGroup + + for _, p := range cc.Paths { + wg.Add(1) + + go func(path *config.IBCData) { + defer wg.Done() + + discordIDs := getDiscordIDs(path.Operators) + + // Client info + ci, err := ibc.GetClientsInfo(path, cc.RPCs) + status := successStatus + + if err != nil { + status = errorStatus + + log.Error(err.Error()) + } + + ch <- prometheus.MustNewConstMetric( + clientExpiry, + prometheus.GaugeValue, + float64(ci.ChainAClientExpiration.Unix()), + []string{ + (*cc.RPCs)[path.Chain1.ChainName].ChainID, + path.Chain1.ClientID, + (*cc.RPCs)[path.Chain2.ChainName].ChainID, + discordIDs, + status, + }..., + ) + + ch <- prometheus.MustNewConstMetric( + clientExpiry, + prometheus.GaugeValue, + float64(ci.ChainBClientExpiration.Unix()), + []string{ + (*cc.RPCs)[path.Chain2.ChainName].ChainID, + path.Chain2.ClientID, + (*cc.RPCs)[path.Chain1.ChainName].ChainID, + discordIDs, + status, + }..., + ) + + // Stuck packets + status = successStatus + + stuckPackets, err := ibc.GetChannelsInfo(path, cc.RPCs) + if err != nil { + status = errorStatus + + log.Error(err.Error()) + } + + if !reflect.DeepEqual(stuckPackets, ibc.ChannelsInfo{}) { + for _, sp := range stuckPackets.Channels { + ch <- prometheus.MustNewConstMetric( + channelStuckPackets, + prometheus.GaugeValue, + float64(sp.StuckPackets.Source), + []string{ + sp.Source, + sp.Destination, + (*cc.RPCs)[path.Chain1.ChainName].ChainID, + (*cc.RPCs)[path.Chain2.ChainName].ChainID, + path.Chain1.ChainName, + path.Chain2.ChainName, + discordIDs, + status, + }..., + ) + + ch <- prometheus.MustNewConstMetric( + channelStuckPackets, + prometheus.GaugeValue, + float64(sp.StuckPackets.Destination), + []string{ + sp.Destination, + sp.Source, + (*cc.RPCs)[path.Chain2.ChainName].ChainID, + (*cc.RPCs)[path.Chain1.ChainName].ChainID, + path.Chain2.ChainName, + path.Chain1.ChainName, + discordIDs, + status, + }..., + ) + } + } + }(p) + } + + wg.Wait() + + log.Debug("Stop collecting", zap.String("metric", clientExpiryMetricName)) +} diff --git a/pkg/collector/wallet_balance_collector.go b/pkg/collector/wallet_balance_collector.go new file mode 100644 index 0000000..91d2f3b --- /dev/null +++ b/pkg/collector/wallet_balance_collector.go @@ -0,0 +1,71 @@ +package collector + +import ( + "math/big" + "sync" + + "github.com/prometheus/client_golang/prometheus" + "go.uber.org/zap" + + "github.com/archway-network/relayer_exporter/pkg/config" + log "github.com/archway-network/relayer_exporter/pkg/logger" +) + +const ( + walletBalanceMetricName = "cosmos_wallet_balance" +) + +var ( + walletBalance = prometheus.NewDesc( + walletBalanceMetricName, + "Returns wallet balance for an address on a chain.", + []string{"account", "chain_id", "denom", "status"}, nil, + ) +) + +type WalletBalanceCollector struct { + RPCs *map[string]config.RPC + Accounts []config.Account +} + +func (wb WalletBalanceCollector) Describe(ch chan<- *prometheus.Desc) { + ch <- walletBalance +} + +func (wb WalletBalanceCollector) Collect(ch chan<- prometheus.Metric) { + log.Debug("Start collecting", zap.String("metric", walletBalanceMetricName)) + + var wg sync.WaitGroup + + for _, a := range wb.Accounts { + wg.Add(1) + + go func(account config.Account) { + defer wg.Done() + + balance := 0.0 + status := successStatus + + err := account.GetBalance(wb.RPCs) + if err != nil { + status = errorStatus + + log.Error(err.Error(), zap.Any("account", account)) + } else { + // Convert to a big float to get a float64 for metrics + balance, _ = big.NewFloat(0.0).SetInt(account.Balance.BigInt()).Float64() + } + + ch <- prometheus.MustNewConstMetric( + walletBalance, + prometheus.GaugeValue, + balance, + []string{account.Address, (*wb.RPCs)[account.ChainName].ChainID, account.Denom, status}..., + ) + }(a) + } + + wg.Wait() + + log.Debug("Stop collecting", zap.String("metric", walletBalanceMetricName)) +} diff --git a/pkg/config/config.go b/pkg/config/config.go index 34b2dbe..91324bf 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "errors" + "fmt" "os" "strings" @@ -19,6 +20,7 @@ import ( const ibcPathSuffix = ".json" var ErrGitHubClient = errors.New("GitHub client not provided") +var ErrMissingRPCConfigMsg = "missing RPC config for chain: %s" type Account struct { Address string `yaml:"address"` @@ -47,37 +49,29 @@ type Config struct { } `yaml:"github"` } -type IBCData struct { - Schema string `json:"$schema"` +type IBCChainMeta struct { + ChainName string `json:"chain_name"` + ClientID string `json:"client_id"` + ConnectionID string `json:"connection_id"` +} + +type Channel struct { Chain1 struct { - ChainName string `json:"chain_name"` - ClientID string `json:"client_id"` - ConnectionID string `json:"connection_id"` + ChannelID string `json:"channel_id"` + PortID string `json:"port_id"` } `json:"chain_1"` Chain2 struct { - ChainName string `json:"chain_name"` - ClientID string `json:"client_id"` - ConnectionID string `json:"connection_id"` + ChannelID string `json:"channel_id"` + PortID string `json:"port_id"` } `json:"chain_2"` - Channels []struct { - Chain1 struct { - ChannelID string `json:"channel_id"` - PortID string `json:"port_id"` - } `json:"chain_1"` - Chain2 struct { - ChannelID string `json:"channel_id"` - PortID string `json:"port_id"` - } `json:"chain_2"` - Ordering string `json:"ordering"` - Version string `json:"version"` - Tags struct { - Status string `json:"status"` - Preferred bool `json:"preferred"` - Dex string `json:"dex"` - Properties string `json:"properties"` - } `json:"tags,omitempty"` - } `json:"channels"` - Operators []Operator `json:"operators"` + Ordering string `json:"ordering"` + Version string `json:"version"` + Tags struct { + Status string `json:"status"` + Preferred bool `json:"preferred"` + Dex string `json:"dex"` + Properties string `json:"properties"` + } `json:"tags,omitempty"` } type Operator struct { @@ -92,6 +86,14 @@ type Operator struct { Discord Discord `json:"discord"` } +type IBCData struct { + Schema string `json:"$schema"` + Chain1 IBCChainMeta `json:"chain_1"` + Chain2 IBCChainMeta `json:"chain_2"` + Channels []Channel `json:"channels"` + Operators []Operator `json:"operators"` +} + type Discord struct { Handle string `json:"handle"` ID string `json:"id"` @@ -119,7 +121,11 @@ func (a *Account) GetBalance(rpcs *map[string]RPC) error { return nil } -func (c *Config) GetRPCsMap() *map[string]RPC { +// GetRPCsMap uses the provided config file to return a map of chain +// chain_names to RPCs. It uses IBCData already extracted from +// github IBC registry to validate config for missing RPCs and raises +// an error if any are missing. +func (c *Config) GetRPCsMap(ibcPaths []*IBCData) (*map[string]RPC, error) { rpcs := map[string]RPC{} for _, rpc := range c.RPCs { @@ -130,7 +136,20 @@ func (c *Config) GetRPCsMap() *map[string]RPC { rpcs[rpc.ChainName] = rpc } - return &rpcs + // Validate RPCs exist for each IBC path + for _, ibcPath := range ibcPaths { + // Check RPC for chain 1 + if _, ok := rpcs[ibcPath.Chain1.ChainName]; !ok { + return &rpcs, fmt.Errorf("missing RPC config for chain: %s", ibcPath.Chain1.ChainName) + } + + // Check RPC for chain 2 + if _, ok := rpcs[ibcPath.Chain2.ChainName]; !ok { + return &rpcs, fmt.Errorf(ErrMissingRPCConfigMsg, ibcPath.Chain2.ChainName) + } + } + + return &rpcs, nil } func (c *Config) IBCPaths() ([]*IBCData, error) { @@ -176,6 +195,7 @@ func (c *Config) getPaths(dir string, client *github.Client) ([]*IBCData, error) for _, file := range ibcDir { if strings.HasSuffix(*file.Path, ibcPathSuffix) { + log.Debug(fmt.Sprintf("Fetching IBC data for %s/%s/%s", c.GitHub.Org, c.GitHub.Repo, *file.Path)) content, _, _, err := client.Repositories.GetContents( ctx, c.GitHub.Org, diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index 61e1588..d5348b0 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -2,49 +2,101 @@ package config import ( "errors" + "fmt" "testing" "github.com/stretchr/testify/assert" ) -func TestGetRPCsMap(t *testing.T) { - rpcs := []RPC{ +func TestGetRPC(t *testing.T) { + testCases := []struct { + name string + rpcs []RPC + paths []*IBCData + resp map[string]RPC + err error + }{ { - ChainName: "archway", - ChainID: "archway-1", - URL: "https://rpc.mainnet.archway.io:443", + name: "No Missing or Invalid RPCs", + rpcs: []RPC{ + { + ChainName: "archway", + ChainID: "archway-1", + URL: "https://rpc.mainnet.archway.io:443", + }, + { + ChainName: "archwaytestnet", + ChainID: "constantine-3", + URL: "https://rpc.constantine.archway.tech:443", + Timeout: "2s", + }, + }, + paths: []*IBCData{}, + resp: map[string]RPC{ + "archway": { + ChainName: "archway", + ChainID: "archway-1", + URL: "https://rpc.mainnet.archway.io:443", + Timeout: "5s", + }, + "archwaytestnet": { + ChainName: "archwaytestnet", + ChainID: "constantine-3", + URL: "https://rpc.constantine.archway.tech:443", + Timeout: "2s", + }, + }, + err: nil, }, { - ChainName: "archwaytestnet", - ChainID: "constantine-3", - URL: "https://rpc.constantine.archway.tech:443", - Timeout: "2s", + name: "Missing RPCs", + rpcs: []RPC{ + { + ChainName: "archway", + ChainID: "archway-1", + URL: "https://rpc.mainnet.archway.io:443", + }, + }, + paths: []*IBCData{ + { + Schema: "$schema", + Chain1: IBCChainMeta{ + ChainName: "archway", + ClientID: "Client1", + ConnectionID: "Connection1", + }, + Chain2: IBCChainMeta{ + ChainName: "archwaytestnet", + ClientID: "Client2", + ConnectionID: "Connection2", + }, + Channels: []Channel{}, + Operators: []Operator{}, + }, + }, + resp: map[string]RPC{ + "archway": { + ChainName: "archway", + ChainID: "archway-1", + URL: "https://rpc.mainnet.archway.io:443", + Timeout: "5s", + }, + }, + err: fmt.Errorf(ErrMissingRPCConfigMsg, "archwaytestnet"), }, } - cfg := Config{ - GlobalRPCTimeout: "5s", - RPCs: rpcs, + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + cfg := Config{ + GlobalRPCTimeout: "5s", + RPCs: tc.rpcs, + } + res, err := cfg.GetRPCsMap(tc.paths) + assert.ErrorIs(t, err, tc.err) + assert.Equal(t, &tc.resp, res) + }) } - - exp := map[string]RPC{ - "archway": { - ChainName: "archway", - ChainID: "archway-1", - URL: "https://rpc.mainnet.archway.io:443", - Timeout: "5s", - }, - "archwaytestnet": { - ChainName: "archwaytestnet", - ChainID: "constantine-3", - URL: "https://rpc.constantine.archway.tech:443", - Timeout: "2s", - }, - } - - res := cfg.GetRPCsMap() - - assert.Equal(t, &exp, res) } func TestGetPaths(t *testing.T) { diff --git a/pkg/ibc/ibc.go b/pkg/ibc/ibc.go index 2c26c1b..d3d6050 100644 --- a/pkg/ibc/ibc.go +++ b/pkg/ibc/ibc.go @@ -72,6 +72,7 @@ func GetClientsInfo(ibc *config.IBCData, rpcs *map[string]config.RPC) (ClientsIn ctx := context.Background() + fmt.Sprintf("Querying client expiration for %v <-> %v", cdA, cdB) clientsInfo.ChainAClientExpiration, clientsInfo.ChainAClientInfo, err = relayer.QueryClientExpiration( ctx, chainA, From 016783a945c703c32725a5a25246ea9cdf7e3eff Mon Sep 17 00:00:00 2001 From: Shahbaz Nazir Date: Wed, 29 Nov 2023 16:49:12 +0100 Subject: [PATCH 02/12] add test cases --- pkg/config/config_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index d5348b0..8b180d4 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -93,7 +93,7 @@ func TestGetRPC(t *testing.T) { RPCs: tc.rpcs, } res, err := cfg.GetRPCsMap(tc.paths) - assert.ErrorIs(t, err, tc.err) + assert.Equal(t, err, tc.err) assert.Equal(t, &tc.resp, res) }) } From eaecb18b3e4b3a1cfae83dc1cfa358425c3a88ae Mon Sep 17 00:00:00 2001 From: Shahbaz Nazir Date: Wed, 29 Nov 2023 17:31:16 +0100 Subject: [PATCH 03/12] use existing logger instead of adding new one --- pkg/chain/chain.go | 6 ++---- pkg/logger/logger.go | 4 ++++ 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/pkg/chain/chain.go b/pkg/chain/chain.go index 4219d09..82c9906 100644 --- a/pkg/chain/chain.go +++ b/pkg/chain/chain.go @@ -4,9 +4,9 @@ import ( "context" "fmt" + log "github.com/archway-network/relayer_exporter/pkg/logger" "github.com/cosmos/relayer/v2/relayer" "github.com/cosmos/relayer/v2/relayer/chains/cosmos" - "go.uber.org/zap" ) const ( @@ -22,8 +22,6 @@ type Info struct { } func PrepChain(info Info) (*relayer.Chain, error) { - logger := zap.NewNop() - timeout := rpcTimeout if info.Timeout != "" { timeout = info.Timeout @@ -46,7 +44,7 @@ func PrepChain(info Info) (*relayer.Chain, error) { return nil, err } - chain := relayer.NewChain(logger, provider, false) + chain := relayer.NewChain(log.GetLogger(), provider, false) err = chain.SetPath(&relayer.PathEnd{ClientID: info.ClientID}) if err != nil { diff --git a/pkg/logger/logger.go b/pkg/logger/logger.go index 3e808d3..61ae8b8 100644 --- a/pkg/logger/logger.go +++ b/pkg/logger/logger.go @@ -30,6 +30,10 @@ func init() { logger.Level() } +func GetLogger() *zap.Logger { + return logger +} + func Info(message string, fields ...zap.Field) { logger.Info(message, fields...) } From 6adf410a60d01811e477b8bd9456969c4b6f3440 Mon Sep 17 00:00:00 2001 From: Shahbaz Nazir Date: Fri, 1 Dec 2023 14:11:18 +0100 Subject: [PATCH 04/12] consolidate contexts --- cmd/relayer_exporter/relayer_exporter.go | 4 +++- pkg/collector/ibc_collector.go | 7 ++++--- pkg/collector/wallet_balance_collector.go | 5 +++-- pkg/config/config.go | 14 +++++--------- pkg/config/config_test.go | 4 +++- pkg/ibc/ibc.go | 12 ++++-------- 6 files changed, 22 insertions(+), 24 deletions(-) diff --git a/cmd/relayer_exporter/relayer_exporter.go b/cmd/relayer_exporter/relayer_exporter.go index 567eaad..cc67f48 100644 --- a/cmd/relayer_exporter/relayer_exporter.go +++ b/cmd/relayer_exporter/relayer_exporter.go @@ -1,6 +1,7 @@ package main import ( + "context" "flag" "fmt" "net/http" @@ -54,8 +55,9 @@ func main() { zap.String("Testnet Directory", cfg.GitHub.TestnetsIBCDir), ) + ctx := context.Background() // TODO: Add a feature to refresh paths at configured interval - paths, err := cfg.IBCPaths() + paths, err := cfg.IBCPaths(ctx) if err != nil { log.Fatal(err.Error()) } diff --git a/pkg/collector/ibc_collector.go b/pkg/collector/ibc_collector.go index 53edd74..564280f 100644 --- a/pkg/collector/ibc_collector.go +++ b/pkg/collector/ibc_collector.go @@ -1,6 +1,7 @@ package collector import ( + "context" "fmt" "reflect" "sync" @@ -66,7 +67,7 @@ func (cc IBCCollector) Collect(ch chan<- prometheus.Metric) { fmt.Sprintf("%s, %s", clientExpiryMetricName, channelStuckPacketsMetricName), ), ) - + ctx := context.Background() var wg sync.WaitGroup for _, p := range cc.Paths { @@ -78,7 +79,7 @@ func (cc IBCCollector) Collect(ch chan<- prometheus.Metric) { discordIDs := getDiscordIDs(path.Operators) // Client info - ci, err := ibc.GetClientsInfo(path, cc.RPCs) + ci, err := ibc.GetClientsInfo(ctx, path, cc.RPCs) status := successStatus if err != nil { @@ -116,7 +117,7 @@ func (cc IBCCollector) Collect(ch chan<- prometheus.Metric) { // Stuck packets status = successStatus - stuckPackets, err := ibc.GetChannelsInfo(path, cc.RPCs) + stuckPackets, err := ibc.GetChannelsInfo(ctx, path, cc.RPCs) if err != nil { status = errorStatus diff --git a/pkg/collector/wallet_balance_collector.go b/pkg/collector/wallet_balance_collector.go index 91d2f3b..ea18909 100644 --- a/pkg/collector/wallet_balance_collector.go +++ b/pkg/collector/wallet_balance_collector.go @@ -1,6 +1,7 @@ package collector import ( + "context" "math/big" "sync" @@ -34,8 +35,8 @@ func (wb WalletBalanceCollector) Describe(ch chan<- *prometheus.Desc) { func (wb WalletBalanceCollector) Collect(ch chan<- prometheus.Metric) { log.Debug("Start collecting", zap.String("metric", walletBalanceMetricName)) - var wg sync.WaitGroup + ctx := context.Background() for _, a := range wb.Accounts { wg.Add(1) @@ -46,7 +47,7 @@ func (wb WalletBalanceCollector) Collect(ch chan<- prometheus.Metric) { balance := 0.0 status := successStatus - err := account.GetBalance(wb.RPCs) + err := account.GetBalance(ctx, wb.RPCs) if err != nil { status = errorStatus diff --git a/pkg/config/config.go b/pkg/config/config.go index 91324bf..e1c527c 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -99,7 +99,7 @@ type Discord struct { ID string `json:"id"` } -func (a *Account) GetBalance(rpcs *map[string]RPC) error { +func (a *Account) GetBalance(ctx context.Context, rpcs *map[string]RPC) error { chain, err := chain.PrepChain(chain.Info{ ChainID: (*rpcs)[a.ChainName].ChainID, RPCAddr: (*rpcs)[a.ChainName].URL, @@ -109,8 +109,6 @@ func (a *Account) GetBalance(rpcs *map[string]RPC) error { return err } - ctx := context.Background() - coins, err := chain.ChainProvider.QueryBalanceWithAddress(ctx, a.Address) if err != nil { return err @@ -152,7 +150,7 @@ func (c *Config) GetRPCsMap(ibcPaths []*IBCData) (*map[string]RPC, error) { return &rpcs, nil } -func (c *Config) IBCPaths() ([]*IBCData, error) { +func (c *Config) IBCPaths(ctx context.Context) ([]*IBCData, error) { client := github.NewClient(nil) if c.GitHub.Token != "" { @@ -161,14 +159,14 @@ func (c *Config) IBCPaths() ([]*IBCData, error) { client = github.NewClient(nil).WithAuthToken(c.GitHub.Token) } - paths, err := c.getPaths(c.GitHub.IBCDir, client) + paths, err := c.getPaths(ctx, c.GitHub.IBCDir, client) if err != nil { return nil, err } testnetsPaths := []*IBCData{} if c.GitHub.TestnetsIBCDir != "" { - testnetsPaths, err = c.getPaths(c.GitHub.TestnetsIBCDir, client) + testnetsPaths, err = c.getPaths(ctx, c.GitHub.TestnetsIBCDir, client) if err != nil { return nil, err } @@ -179,13 +177,11 @@ func (c *Config) IBCPaths() ([]*IBCData, error) { return paths, nil } -func (c *Config) getPaths(dir string, client *github.Client) ([]*IBCData, error) { +func (c *Config) getPaths(ctx context.Context, dir string, client *github.Client) ([]*IBCData, error) { if client == nil { return nil, ErrGitHubClient } - ctx := context.Background() - _, ibcDir, _, err := client.Repositories.GetContents(ctx, c.GitHub.Org, c.GitHub.Repo, dir, nil) if err != nil { return nil, err diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index 8b180d4..2932546 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -1,6 +1,7 @@ package config import ( + "context" "errors" "fmt" "testing" @@ -101,10 +102,11 @@ func TestGetRPC(t *testing.T) { func TestGetPaths(t *testing.T) { cfg := Config{} + ctx := context.Background() expError := ErrGitHubClient - _, err := cfg.getPaths("_IBC", nil) + _, err := cfg.getPaths(ctx, "_IBC", nil) if err == nil { t.Fatalf("Expected error %q, got no error", expError) } diff --git a/pkg/ibc/ibc.go b/pkg/ibc/ibc.go index d3d6050..c42ea46 100644 --- a/pkg/ibc/ibc.go +++ b/pkg/ibc/ibc.go @@ -39,7 +39,7 @@ type Channel struct { } } -func GetClientsInfo(ibc *config.IBCData, rpcs *map[string]config.RPC) (ClientsInfo, error) { +func GetClientsInfo(ctx context.Context, ibc *config.IBCData, rpcs *map[string]config.RPC) (ClientsInfo, error) { clientsInfo := ClientsInfo{} cdA := chain.Info{ @@ -51,7 +51,7 @@ func GetClientsInfo(ibc *config.IBCData, rpcs *map[string]config.RPC) (ClientsIn chainA, err := chain.PrepChain(cdA) if err != nil { - return ClientsInfo{}, fmt.Errorf("Error: %w for %v", err, cdA) + return ClientsInfo{}, fmt.Errorf("%w for %v", err, cdA) } clientsInfo.ChainA = chainA @@ -65,14 +65,11 @@ func GetClientsInfo(ibc *config.IBCData, rpcs *map[string]config.RPC) (ClientsIn chainB, err := chain.PrepChain(cdB) if err != nil { - return ClientsInfo{}, fmt.Errorf("Error: %w for %v", err, cdB) + return ClientsInfo{}, fmt.Errorf("%w for %v", err, cdB) } clientsInfo.ChainB = chainB - ctx := context.Background() - - fmt.Sprintf("Querying client expiration for %v <-> %v", cdA, cdB) clientsInfo.ChainAClientExpiration, clientsInfo.ChainAClientInfo, err = relayer.QueryClientExpiration( ctx, chainA, @@ -94,8 +91,7 @@ func GetClientsInfo(ibc *config.IBCData, rpcs *map[string]config.RPC) (ClientsIn return clientsInfo, nil } -func GetChannelsInfo(ibc *config.IBCData, rpcs *map[string]config.RPC) (ChannelsInfo, error) { - ctx := context.Background() +func GetChannelsInfo(ctx context.Context, ibc *config.IBCData, rpcs *map[string]config.RPC) (ChannelsInfo, error) { channelInfo := ChannelsInfo{} // Init channel data From ff86cacc4422c65d76b3c7c940df77f03ab5dae3 Mon Sep 17 00:00:00 2001 From: Shahbaz Nazir Date: Fri, 1 Dec 2023 14:30:24 +0100 Subject: [PATCH 05/12] fix: context again --- pkg/chain/chain.go | 4 ++-- pkg/config/config.go | 2 +- pkg/ibc/ibc.go | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/pkg/chain/chain.go b/pkg/chain/chain.go index 82c9906..c7977ec 100644 --- a/pkg/chain/chain.go +++ b/pkg/chain/chain.go @@ -21,7 +21,7 @@ type Info struct { Timeout string } -func PrepChain(info Info) (*relayer.Chain, error) { +func PrepChain(ctx context.Context, info Info) (*relayer.Chain, error) { timeout := rpcTimeout if info.Timeout != "" { timeout = info.Timeout @@ -39,7 +39,7 @@ func PrepChain(info Info) (*relayer.Chain, error) { return nil, err } - err = provider.Init(context.Background()) + err = provider.Init(ctx) if err != nil { return nil, err } diff --git a/pkg/config/config.go b/pkg/config/config.go index e1c527c..b0c58a6 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -100,7 +100,7 @@ type Discord struct { } func (a *Account) GetBalance(ctx context.Context, rpcs *map[string]RPC) error { - chain, err := chain.PrepChain(chain.Info{ + chain, err := chain.PrepChain(ctx, chain.Info{ ChainID: (*rpcs)[a.ChainName].ChainID, RPCAddr: (*rpcs)[a.ChainName].URL, Timeout: (*rpcs)[a.ChainName].Timeout, diff --git a/pkg/ibc/ibc.go b/pkg/ibc/ibc.go index c42ea46..972c7b4 100644 --- a/pkg/ibc/ibc.go +++ b/pkg/ibc/ibc.go @@ -49,7 +49,7 @@ func GetClientsInfo(ctx context.Context, ibc *config.IBCData, rpcs *map[string]c ClientID: ibc.Chain1.ClientID, } - chainA, err := chain.PrepChain(cdA) + chainA, err := chain.PrepChain(ctx, cdA) if err != nil { return ClientsInfo{}, fmt.Errorf("%w for %v", err, cdA) } @@ -63,7 +63,7 @@ func GetClientsInfo(ctx context.Context, ibc *config.IBCData, rpcs *map[string]c ClientID: ibc.Chain2.ClientID, } - chainB, err := chain.PrepChain(cdB) + chainB, err := chain.PrepChain(ctx, cdB) if err != nil { return ClientsInfo{}, fmt.Errorf("%w for %v", err, cdB) } @@ -119,7 +119,7 @@ func GetChannelsInfo(ctx context.Context, ibc *config.IBCData, rpcs *map[string] ClientID: ibc.Chain1.ClientID, } - chainA, err := chain.PrepChain(cdA) + chainA, err := chain.PrepChain(ctx, cdA) if err != nil { return ChannelsInfo{}, fmt.Errorf("Error: %w for %v", err, cdA) } @@ -131,7 +131,7 @@ func GetChannelsInfo(ctx context.Context, ibc *config.IBCData, rpcs *map[string] ClientID: ibc.Chain2.ClientID, } - chainB, err := chain.PrepChain(cdB) + chainB, err := chain.PrepChain(ctx, cdB) if err != nil { return ChannelsInfo{}, fmt.Errorf("Error: %w for %v", err, cdB) } From f3a158c6b41b6e6a3df05841cc30d3a3fb2ef29b Mon Sep 17 00:00:00 2001 From: Shahbaz Nazir Date: Mon, 4 Dec 2023 17:45:54 +0100 Subject: [PATCH 06/12] add config validations --- go.mod | 5 ++++ go.sum | 19 +++++++++----- pkg/config/config.go | 59 ++++++++++++++++++++++++++++++++++++++------ pkg/ibc/ibc.go | 6 +++-- 4 files changed, 73 insertions(+), 16 deletions(-) diff --git a/go.mod b/go.mod index 5b329fa..1a921a7 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/caarlos0/env/v9 v9.0.0 github.com/cosmos/ibc-go/v7 v7.2.0 github.com/cosmos/relayer/v2 v2.4.1 + github.com/go-playground/validator/v10 v10.16.0 github.com/google/go-github/v55 v55.0.0 github.com/prometheus/client_golang v1.15.0 github.com/stretchr/testify v1.8.4 @@ -73,9 +74,12 @@ require ( github.com/ethereum/go-ethereum v1.10.26 // indirect github.com/felixge/httpsnoop v1.0.2 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/gabriel-vasile/mimetype v1.4.2 // indirect github.com/go-kit/kit v0.12.0 // indirect github.com/go-kit/log v0.2.1 // indirect github.com/go-logfmt/logfmt v0.6.0 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-stack/stack v1.8.0 // indirect github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect github.com/gogo/googleapis v1.4.1 // indirect @@ -116,6 +120,7 @@ require ( github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/jmhodges/levigo v1.0.0 // indirect github.com/klauspost/compress v1.16.3 // indirect + github.com/leodido/go-urn v1.2.4 // indirect github.com/lib/pq v1.10.7 // indirect github.com/libp2p/go-buffer-pool v0.1.0 // indirect github.com/linxGnu/grocksdb v1.7.16 // indirect diff --git a/go.sum b/go.sum index af2fa97..10251ef 100644 --- a/go.sum +++ b/go.sum @@ -463,6 +463,8 @@ github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4 github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= +github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= @@ -486,13 +488,17 @@ github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KE github.com/go-ole/go-ole v1.2.1 h1:2lOsA72HgjxAuMlKpFiCbHTvu44PIVkZ5hqm3RSdI/E= github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= -github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= -github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= -github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= +github.com/go-playground/validator/v10 v10.16.0 h1:x+plE831WK4vaKHO/jpgUGsvLKIqRRkz6M78GuJAfGE= +github.com/go-playground/validator/v10 v10.16.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= @@ -772,8 +778,8 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= -github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= -github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= +github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= +github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= @@ -1026,6 +1032,7 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= diff --git a/pkg/config/config.go b/pkg/config/config.go index b0c58a6..ca7a153 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -6,10 +6,12 @@ import ( "errors" "fmt" "os" + "strconv" "strings" "cosmossdk.io/math" "github.com/caarlos0/env/v9" + "github.com/go-playground/validator/v10" "github.com/google/go-github/v55/github" "gopkg.in/yaml.v3" @@ -30,23 +32,23 @@ type Account struct { } type RPC struct { - ChainName string `yaml:"chainName"` - ChainID string `yaml:"chainId"` - URL string `yaml:"url"` + ChainName string `yaml:"chainName" validate:"required"` + ChainID string `yaml:"chainId" validate:"required"` + URL string `yaml:"url" validate:"required,http_url,has_port"` Timeout string `yaml:"timeout"` } type Config struct { Accounts []Account `yaml:"accounts"` GlobalRPCTimeout string `env:"GLOBAL_RPC_TIMEOUT" envDefault:"5s"` - RPCs []RPC `yaml:"rpc"` + RPCs []RPC `yaml:"rpc" validate` GitHub struct { - Org string `yaml:"org"` - Repo string `yaml:"repo"` - IBCDir string `yaml:"dir"` + Org string `yaml:"org" validate:"required"` + Repo string `yaml:"repo" validate:"required"` + IBCDir string `yaml:"dir" validate:"required"` TestnetsIBCDir string `yaml:"testnetsDir"` Token string `env:"GITHUB_TOKEN"` - } `yaml:"github"` + } `yaml:"github" validate:"required"` } type IBCChainMeta struct { @@ -221,6 +223,42 @@ func (c *Config) getPaths(ctx context.Context, dir string, client *github.Client return ibcs, nil } +func (c *Config) Validate() error { + validate := validator.New(validator.WithRequiredStructEnabled()) + + // register custom validation for http url as expected by go relayer i.e. + // http_url must have port defined. + // https://github.com/cosmos/relayer/blob/259b1278264180a2aefc2085f1b55753849c4815/cregistry/chain_info.go#L115 + validate.RegisterValidation("has_port", func(fl validator.FieldLevel) bool { + val := fl.Field().String() + val_arr := strings.Split(val, ":") + port := val_arr[len(val_arr)-1] + + // Port must be a iny <= 65535. + if portNum, err := strconv.ParseInt( + port, 10, 32, + ); err != nil || portNum > 65535 || portNum < 1 { + return false + } + return true + }) + + // validate top level fields + if err := validate.Struct(c); err != nil { + return err + + } + + // validate RPCs + for _, rpc := range c.RPCs { + if err := validate.Struct(rpc); err != nil { + return fmt.Errorf("%v for RPC config: %+v", err, rpc) + } + } + + return nil +} + func NewConfig(configPath string) (*Config, error) { config := &Config{} @@ -239,5 +277,10 @@ func NewConfig(configPath string) (*Config, error) { return nil, err } + err = config.Validate() + if err != nil { + return nil, err + } + return config, nil } diff --git a/pkg/ibc/ibc.go b/pkg/ibc/ibc.go index 972c7b4..894e1e2 100644 --- a/pkg/ibc/ibc.go +++ b/pkg/ibc/ibc.go @@ -76,7 +76,7 @@ func GetClientsInfo(ctx context.Context, ibc *config.IBCData, rpcs *map[string]c chainB, ) if err != nil { - return ClientsInfo{}, fmt.Errorf("Error: %w path %v <-> %v", err, cdA, cdB) + return ClientsInfo{}, fmt.Errorf("%w path %v <-> %v", err, cdA, cdB) } clientsInfo.ChainBClientExpiration, clientsInfo.ChainBClientInfo, err = relayer.QueryClientExpiration( @@ -85,7 +85,7 @@ func GetClientsInfo(ctx context.Context, ibc *config.IBCData, rpcs *map[string]c chainA, ) if err != nil { - return ClientsInfo{}, fmt.Errorf("Error: %w path %v <-> %v", err, cdB, cdA) + return ClientsInfo{}, fmt.Errorf("%w path %v <-> %v", err, cdB, cdA) } return clientsInfo, nil @@ -105,6 +105,8 @@ func GetChannelsInfo(ctx context.Context, ibc *config.IBCData, rpcs *map[string] channelInfo.Channels = append(channelInfo.Channels, channel) } + fmt.Printf("Type: %T Value %v\n", (*rpcs)[ibc.Chain2.ChainName], (*rpcs)[ibc.Chain2.ChainName]) + fmt.Printf("Type: %T Value %v\n", (*rpcs)[ibc.Chain2.ChainName], (*rpcs)[ibc.Chain2.ChainName]) if (*rpcs)[ibc.Chain1.ChainName].ChainID == "" || (*rpcs)[ibc.Chain2.ChainName].ChainID == "" { return channelInfo, fmt.Errorf( "Error: RPC data is missing, cannot retrieve channel data: %v", From e5715a059f7f30501e6591e519bbac759378bf20 Mon Sep 17 00:00:00 2001 From: Shahbaz Nazir Date: Mon, 4 Dec 2023 17:58:28 +0100 Subject: [PATCH 07/12] validate accounts --- pkg/config/config.go | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/pkg/config/config.go b/pkg/config/config.go index ca7a153..a261497 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -25,9 +25,9 @@ var ErrGitHubClient = errors.New("GitHub client not provided") var ErrMissingRPCConfigMsg = "missing RPC config for chain: %s" type Account struct { - Address string `yaml:"address"` - Denom string `yaml:"denom"` - ChainName string `yaml:"chainName"` + Address string `yaml:"address" validate:"required"` + Denom string `yaml:"denom" validate:"required"` + ChainName string `yaml:"chainName" validate:"required"` Balance math.Int } @@ -41,7 +41,7 @@ type RPC struct { type Config struct { Accounts []Account `yaml:"accounts"` GlobalRPCTimeout string `env:"GLOBAL_RPC_TIMEOUT" envDefault:"5s"` - RPCs []RPC `yaml:"rpc" validate` + RPCs []RPC `yaml:"rpc"` GitHub struct { Org string `yaml:"org" validate:"required"` Repo string `yaml:"repo" validate:"required"` @@ -256,6 +256,13 @@ func (c *Config) Validate() error { } } + // validate accounts + for _, account := range c.Accounts { + if err := validate.Struct(account); err != nil { + return fmt.Errorf("%v for accounts config: %+v", err, account) + } + } + return nil } From c968c87d82c334e52ee052352457f4d9cea6fbb5 Mon Sep 17 00:00:00 2001 From: Shahbaz Nazir Date: Wed, 6 Dec 2023 10:37:14 +0100 Subject: [PATCH 08/12] log improvements --- pkg/ibc/ibc.go | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/pkg/ibc/ibc.go b/pkg/ibc/ibc.go index 894e1e2..d0541c4 100644 --- a/pkg/ibc/ibc.go +++ b/pkg/ibc/ibc.go @@ -105,15 +105,6 @@ func GetChannelsInfo(ctx context.Context, ibc *config.IBCData, rpcs *map[string] channelInfo.Channels = append(channelInfo.Channels, channel) } - fmt.Printf("Type: %T Value %v\n", (*rpcs)[ibc.Chain2.ChainName], (*rpcs)[ibc.Chain2.ChainName]) - fmt.Printf("Type: %T Value %v\n", (*rpcs)[ibc.Chain2.ChainName], (*rpcs)[ibc.Chain2.ChainName]) - if (*rpcs)[ibc.Chain1.ChainName].ChainID == "" || (*rpcs)[ibc.Chain2.ChainName].ChainID == "" { - return channelInfo, fmt.Errorf( - "Error: RPC data is missing, cannot retrieve channel data: %v", - ibc.Channels, - ) - } - cdA := chain.Info{ ChainID: (*rpcs)[ibc.Chain1.ChainName].ChainID, RPCAddr: (*rpcs)[ibc.Chain1.ChainName].URL, @@ -123,7 +114,7 @@ func GetChannelsInfo(ctx context.Context, ibc *config.IBCData, rpcs *map[string] chainA, err := chain.PrepChain(ctx, cdA) if err != nil { - return ChannelsInfo{}, fmt.Errorf("Error: %w for %v", err, cdA) + return ChannelsInfo{}, fmt.Errorf("error: %w for %+v", err, cdA) } cdB := chain.Info{ @@ -135,14 +126,14 @@ func GetChannelsInfo(ctx context.Context, ibc *config.IBCData, rpcs *map[string] chainB, err := chain.PrepChain(ctx, cdB) if err != nil { - return ChannelsInfo{}, fmt.Errorf("Error: %w for %v", err, cdB) + return ChannelsInfo{}, fmt.Errorf("error: %w for %+v", err, cdB) } // test that RPC endpoints are working if _, _, err := relayer.QueryLatestHeights( ctx, chainA, chainB, ); err != nil { - return channelInfo, fmt.Errorf("Error: %w for %v", err, cdA) + return channelInfo, fmt.Errorf("error: %w for %v", err, cdA) } for i, c := range channelInfo.Channels { From 582235b805cbbc5605d11e0e32a2248525ce656c Mon Sep 17 00:00:00 2001 From: Shahbaz Nazir Date: Wed, 6 Dec 2023 10:38:24 +0100 Subject: [PATCH 09/12] remove un used function --- pkg/chain/chain.go | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/pkg/chain/chain.go b/pkg/chain/chain.go index c7977ec..3dcbd9a 100644 --- a/pkg/chain/chain.go +++ b/pkg/chain/chain.go @@ -2,7 +2,6 @@ package chain import ( "context" - "fmt" log "github.com/archway-network/relayer_exporter/pkg/logger" "github.com/cosmos/relayer/v2/relayer" @@ -53,19 +52,3 @@ func PrepChain(ctx context.Context, info Info) (*relayer.Chain, error) { return chain, nil } - -func ValidateChainInfo(info Info) error { - if info.ChainID == "" { - return fmt.Errorf("missing chain ID: %v", info) - } - - if info.RPCAddr == "" { - return fmt.Errorf("missing RPC address: %v", info) - } - - if info.ClientID == "" { - return fmt.Errorf("missing client ID: %v", info) - } - - return nil -} From a35102a684410bf50f32d691990ac30c59e61afa Mon Sep 17 00:00:00 2001 From: Shahbaz Nazir Date: Wed, 6 Dec 2023 10:57:50 +0100 Subject: [PATCH 10/12] fix lint --- config.yaml | 2 +- pkg/collector/collector.go | 3 +++ pkg/collector/ibc_collector.go | 3 +-- pkg/collector/wallet_balance_collector.go | 13 +++++-------- pkg/config/config.go | 16 ++++++++++------ 5 files changed, 20 insertions(+), 17 deletions(-) diff --git a/config.yaml b/config.yaml index a9ce3db..f9b2e14 100644 --- a/config.yaml +++ b/config.yaml @@ -88,4 +88,4 @@ accounts: # PhiLabs - address: archway1ktka5q3cnsy3ar7qwj2huzz6qj9q4ys7h74l9y chainName: archway - denom: aarch \ No newline at end of file + denom: aarch diff --git a/pkg/collector/collector.go b/pkg/collector/collector.go index 7b55480..09d58a7 100644 --- a/pkg/collector/collector.go +++ b/pkg/collector/collector.go @@ -1,6 +1,7 @@ package collector import ( + "context" "regexp" "strings" @@ -12,6 +13,8 @@ const ( errorStatus = "error" ) +var ctx = context.Background() + func getDiscordIDs(ops []config.Operator) string { var ids []string diff --git a/pkg/collector/ibc_collector.go b/pkg/collector/ibc_collector.go index 564280f..efabaa4 100644 --- a/pkg/collector/ibc_collector.go +++ b/pkg/collector/ibc_collector.go @@ -1,7 +1,6 @@ package collector import ( - "context" "fmt" "reflect" "sync" @@ -67,7 +66,7 @@ func (cc IBCCollector) Collect(ch chan<- prometheus.Metric) { fmt.Sprintf("%s, %s", clientExpiryMetricName, channelStuckPacketsMetricName), ), ) - ctx := context.Background() + var wg sync.WaitGroup for _, p := range cc.Paths { diff --git a/pkg/collector/wallet_balance_collector.go b/pkg/collector/wallet_balance_collector.go index ea18909..d91fb09 100644 --- a/pkg/collector/wallet_balance_collector.go +++ b/pkg/collector/wallet_balance_collector.go @@ -1,7 +1,6 @@ package collector import ( - "context" "math/big" "sync" @@ -16,12 +15,10 @@ const ( walletBalanceMetricName = "cosmos_wallet_balance" ) -var ( - walletBalance = prometheus.NewDesc( - walletBalanceMetricName, - "Returns wallet balance for an address on a chain.", - []string{"account", "chain_id", "denom", "status"}, nil, - ) +var walletBalance = prometheus.NewDesc( + walletBalanceMetricName, + "Returns wallet balance for an address on a chain.", + []string{"account", "chain_id", "denom", "status"}, nil, ) type WalletBalanceCollector struct { @@ -35,8 +32,8 @@ func (wb WalletBalanceCollector) Describe(ch chan<- *prometheus.Desc) { func (wb WalletBalanceCollector) Collect(ch chan<- prometheus.Metric) { log.Debug("Start collecting", zap.String("metric", walletBalanceMetricName)) + var wg sync.WaitGroup - ctx := context.Background() for _, a := range wb.Accounts { wg.Add(1) diff --git a/pkg/config/config.go b/pkg/config/config.go index a261497..1663239 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -21,8 +21,10 @@ import ( const ibcPathSuffix = ".json" -var ErrGitHubClient = errors.New("GitHub client not provided") -var ErrMissingRPCConfigMsg = "missing RPC config for chain: %s" +var ( + ErrGitHubClient = errors.New("GitHub client not provided") + ErrMissingRPCConfigMsg = "missing RPC config for chain: %s" +) type Account struct { Address string `yaml:"address" validate:"required"` @@ -229,10 +231,10 @@ func (c *Config) Validate() error { // register custom validation for http url as expected by go relayer i.e. // http_url must have port defined. // https://github.com/cosmos/relayer/blob/259b1278264180a2aefc2085f1b55753849c4815/cregistry/chain_info.go#L115 - validate.RegisterValidation("has_port", func(fl validator.FieldLevel) bool { + err := validate.RegisterValidation("has_port", func(fl validator.FieldLevel) bool { val := fl.Field().String() - val_arr := strings.Split(val, ":") - port := val_arr[len(val_arr)-1] + valArr := strings.Split(val, ":") + port := valArr[len(valArr)-1] // Port must be a iny <= 65535. if portNum, err := strconv.ParseInt( @@ -242,11 +244,13 @@ func (c *Config) Validate() error { } return true }) + if err != nil { + return err + } // validate top level fields if err := validate.Struct(c); err != nil { return err - } // validate RPCs From fa1bc1571e6c9def40432323bfefd4ad2f771706 Mon Sep 17 00:00:00 2001 From: Shahbaz Nazir Date: Thu, 7 Dec 2023 13:31:14 +0100 Subject: [PATCH 11/12] address comments --- pkg/config/config.go | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/pkg/config/config.go b/pkg/config/config.go index 1663239..808a998 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -5,6 +5,7 @@ import ( "encoding/json" "errors" "fmt" + "net/url" "os" "strconv" "strings" @@ -142,7 +143,7 @@ func (c *Config) GetRPCsMap(ibcPaths []*IBCData) (*map[string]RPC, error) { for _, ibcPath := range ibcPaths { // Check RPC for chain 1 if _, ok := rpcs[ibcPath.Chain1.ChainName]; !ok { - return &rpcs, fmt.Errorf("missing RPC config for chain: %s", ibcPath.Chain1.ChainName) + return &rpcs, fmt.Errorf(ErrMissingRPCConfigMsg, ibcPath.Chain1.ChainName) } // Check RPC for chain 2 @@ -233,8 +234,12 @@ func (c *Config) Validate() error { // https://github.com/cosmos/relayer/blob/259b1278264180a2aefc2085f1b55753849c4815/cregistry/chain_info.go#L115 err := validate.RegisterValidation("has_port", func(fl validator.FieldLevel) bool { val := fl.Field().String() - valArr := strings.Split(val, ":") - port := valArr[len(valArr)-1] + urlParsed, err := url.Parse(val) + if err != nil { + return false + } + + port := urlParsed.Port() // Port must be a iny <= 65535. if portNum, err := strconv.ParseInt( @@ -244,6 +249,7 @@ func (c *Config) Validate() error { } return true }) + if err != nil { return err } From 05615ecfcd9521e2e0b7a927af09f8e58395aac1 Mon Sep 17 00:00:00 2001 From: Shahbaz Nazir Date: Thu, 7 Dec 2023 15:24:19 +0100 Subject: [PATCH 12/12] lint please --- pkg/config/config.go | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/config/config.go b/pkg/config/config.go index 808a998..18f7238 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -249,7 +249,6 @@ func (c *Config) Validate() error { } return true }) - if err != nil { return err }