Skip to content

Commit

Permalink
feat: parse v1 proposals (#48)
Browse files Browse the repository at this point in the history
* feat: parse v1 proposals

* feat: fix defaults
  • Loading branch information
freak12techno authored Jun 4, 2023
1 parent 1b2f6a3 commit b27cf2b
Show file tree
Hide file tree
Showing 23 changed files with 248 additions and 97 deletions.
10 changes: 10 additions & 0 deletions config.example.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,16 @@ wallets = [
{ address = "bitsong14rvn7anf22e00vj5x3al4w50ns78s7n4t8yxcy", alias = "Validator wallet" },
{ address = "bitsong125hdkukw4pu2urhj4nv366q0avdqv24t0vprxs" },
]
# Some chains have a new proposals structure (v1) compared to an older one (v1beta1),
# when there are 2 or more actual proposals inside a single one (namely, Quicksilver).
# On such chains, querying proposals with an older endpoint when there are proposals
# with 2 or more proposals inside causes an error like this:
# "codespace sdk code 29: invalid type: can't convert a gov/v1 Proposal to gov/v1beta1 Proposal
# when amount of proposal messages is more than one". If you see this error, consider switching to
# a newer endpoint. Keep in mind that some chains do not implement the newer format.
# The possible values are: "v1" (newer format), "v1beta1" (older format).
# Defaults to "v1beta1"
proposals-type = "v1beta1"
# Custom explorer links patterns. They are overridden if mintscan-prefix is specified.
[chains.explorer]
# A pattern for proposal link for explorer, if there's no Mintscan support
Expand Down
4 changes: 3 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ go 1.18

require (
github.com/BurntSushi/toml v1.1.0
github.com/mcuadros/go-defaults v1.2.0
github.com/creasty/defaults v1.7.0
github.com/robfig/cron/v3 v3.0.1
github.com/rs/zerolog v1.26.1
github.com/spf13/cobra v1.4.0
Expand All @@ -15,7 +15,9 @@ require (
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
)
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I
github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creasty/defaults v1.7.0 h1:eNdqZvc5B509z18lD8yc212CAqJNvfT1Jq6L8WowdBA=
github.com/creasty/defaults v1.7.0/go.mod h1:iGzKe6pbEHnpMPtfDXZEr0NVxWnPTjb1bbDy08fPzYM=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
Expand All @@ -24,8 +26,6 @@ github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mcuadros/go-defaults v1.2.0 h1:FODb8WSf0uGaY8elWJAkoLL0Ri6AlZ1bFlenk56oZtc=
github.com/mcuadros/go-defaults v1.2.0/go.mod h1:WEZtHEVIGYVDqkKSWBdWKUVdRyKlMfulPaGDWIVeCWY=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
Expand Down
30 changes: 14 additions & 16 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,23 @@ package config

import (
"fmt"
"main/pkg/logger"
"os"

"main/pkg/config/types"
configTypes "main/pkg/config/types"

"github.com/BurntSushi/toml"
"github.com/mcuadros/go-defaults"
"github.com/creasty/defaults"
)

type Config struct {
PagerDutyConfig PagerDutyConfig `toml:"pagerduty"`
TelegramConfig TelegramConfig `toml:"telegram"`
LogConfig LogConfig `toml:"log"`
StatePath string `toml:"state-path"`
MutesPath string `toml:"mutes-path"`
Chains types.Chains `toml:"chains"`
Interval string `toml:"interval" default:"* * * * *"`
PagerDutyConfig PagerDutyConfig `toml:"pagerduty"`
TelegramConfig TelegramConfig `toml:"telegram"`
LogConfig configTypes.LogConfig `toml:"log"`
StatePath string `toml:"state-path"`
MutesPath string `toml:"mutes-path"`
Chains configTypes.Chains `toml:"chains"`
Interval string `toml:"interval" default:"* * * * *"`
}

type PagerDutyConfig struct {
Expand All @@ -30,11 +31,6 @@ type TelegramConfig struct {
TelegramToken string `toml:"token"`
}

type LogConfig struct {
LogLevel string `toml:"level" default:"info"`
JSONOutput bool `toml:"json" default:"false"`
}

func (c *Config) Validate() error {
if len(c.Chains) == 0 {
return fmt.Errorf("no chains provided")
Expand Down Expand Up @@ -62,11 +58,13 @@ func GetConfig(path string) (*Config, error) {
return nil, err
}

defaults.SetDefaults(configStruct)
if err := defaults.Set(configStruct); err != nil {
logger.GetDefaultLogger().Fatal().Err(err).Msg("Error setting default config values")
}

for _, chain := range configStruct.Chains {
if chain.MintscanPrefix != "" {
chain.Explorer = &types.Explorer{
chain.Explorer = &configTypes.Explorer{
ProposalLinkPattern: fmt.Sprintf("https://mintscan.io/%s/proposals/%%s", chain.MintscanPrefix),
WalletLinkPattern: fmt.Sprintf("https://mintscan.io/%s/account/%%s", chain.MintscanPrefix),
}
Expand Down
31 changes: 25 additions & 6 deletions pkg/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,10 @@ func TestValidateChainWithValidConfig(t *testing.T) {
t.Parallel()

chain := configTypes.Chain{
Name: "chain",
LCDEndpoints: []string{"endpoint"},
Wallets: []*configTypes.Wallet{{Address: "wallet"}},
Name: "chain",
LCDEndpoints: []string{"endpoint"},
Wallets: []*configTypes.Wallet{{Address: "wallet"}},
ProposalsType: "v1",
}

err := chain.Validate()
Expand Down Expand Up @@ -105,15 +106,33 @@ func TestValidateConfigInvalidChain(t *testing.T) {
assert.NotEqual(t, err, nil, "Error should be presented!")
}

func TestValidateConfigWrongProposalType(t *testing.T) {
t.Parallel()

config := Config{
Chains: []*configTypes.Chain{
{
Name: "chain",
LCDEndpoints: []string{"endpoint"},
Wallets: []*configTypes.Wallet{{Address: "wallet"}},
ProposalsType: "test",
},
},
}
err := config.Validate()
assert.NotEqual(t, err, nil, "Error should be presented!")
}

func TestValidateConfigValidChain(t *testing.T) {
t.Parallel()

config := Config{
Chains: []*configTypes.Chain{
{
Name: "chain",
LCDEndpoints: []string{"endpoint"},
Wallets: []*configTypes.Wallet{{Address: "wallet"}},
Name: "chain",
LCDEndpoints: []string{"endpoint"},
Wallets: []*configTypes.Wallet{{Address: "wallet"}},
ProposalsType: "v1",
},
},
}
Expand Down
17 changes: 14 additions & 3 deletions pkg/config/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package types
import (
"fmt"
"main/pkg/types"
"main/pkg/utils"
)

type Explorer struct {
Expand All @@ -28,6 +29,7 @@ type Chain struct {
PrettyName string `toml:"pretty-name"`
KeplrName string `toml:"keplr-name"`
LCDEndpoints []string `toml:"lcd-endpoints"`
ProposalsType string `toml:"proposals-type" default:"v1beta1"`
Wallets []*Wallet `toml:"wallets"`
MintscanPrefix string `toml:"mintscan-prefix"`
Explorer *Explorer `toml:"explorer"`
Expand All @@ -46,6 +48,10 @@ func (c *Chain) Validate() error {
return fmt.Errorf("no wallets provided")
}

if !utils.Contains([]string{"v1beta1", "v1"}, c.ProposalsType) {
return fmt.Errorf("wrong proposals type: expected one of 'v1beta1', 'v1', but got %s", c.ProposalsType)
}

for index, wallet := range c.Wallets {
if wallet.Address == "" {
return fmt.Errorf("wallet #%d: address is empty", index)
Expand Down Expand Up @@ -85,12 +91,12 @@ func (c Chain) GetExplorerProposalsLinks(proposalID string) []types.Link {

func (c Chain) GetProposalLink(proposal types.Proposal) types.Link {
if c.Explorer == nil || c.Explorer.ProposalLinkPattern == "" {
return types.Link{Name: proposal.Content.Title}
return types.Link{Name: proposal.Title}
}

return types.Link{
Name: proposal.Content.Title,
Href: fmt.Sprintf(c.Explorer.ProposalLinkPattern, proposal.ProposalID),
Name: proposal.Title,
Href: fmt.Sprintf(c.Explorer.ProposalLinkPattern, proposal.ID),
}
}

Expand Down Expand Up @@ -122,3 +128,8 @@ func (c Chains) FindByName(name string) *Chain {

return nil
}

type LogConfig struct {
LogLevel string `toml:"level" default:"info"`
JSONOutput bool `toml:"json" default:"false"`
}
5 changes: 2 additions & 3 deletions pkg/logger/logger.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
package logger

import (
configTypes "main/pkg/config/types"
"os"

"main/pkg/config"

"github.com/rs/zerolog"
)

Expand All @@ -13,7 +12,7 @@ func GetDefaultLogger() *zerolog.Logger {
return &log
}

func GetLogger(config config.LogConfig) *zerolog.Logger {
func GetLogger(config configTypes.LogConfig) *zerolog.Logger {
log := zerolog.New(zerolog.ConsoleWriter{Out: os.Stdout}).With().Timestamp().Logger()

if config.JSONOutput {
Expand Down
4 changes: 2 additions & 2 deletions pkg/report/generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ func (g *Generator) GenerateReport(oldState, newState state.State) reporters.Rep
if newVote.HasVoted() && !oldVote.HasVoted() {
g.Logger.Debug().
Str("chain", chainName).
Str("proposal", proposal.ProposalID).
Str("proposal", proposal.ID).
Str("wallet", wallet).
Msg("Wallet hasn't voted before but voted now - closing an alert")

Expand All @@ -108,7 +108,7 @@ func (g *Generator) GenerateReport(oldState, newState state.State) reporters.Rep
if newVote.HasVoted() && oldVote.HasVoted() && newVote.Vote.Option != oldVote.Vote.Option {
g.Logger.Debug().
Str("chain", chainName).
Str("proposal", proposal.ProposalID).
Str("proposal", proposal.ID).
Str("wallet", wallet).
Msg("Wallet changed its vote - sending an alert")

Expand Down
26 changes: 10 additions & 16 deletions pkg/report/generator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,7 @@ func TestReportGeneratorWithVoteError(t *testing.T) {
ProposalVotes: map[string]state.WalletVotes{
"proposal": {
Proposal: types.Proposal{
ProposalID: "proposal",
Content: &types.ProposalContent{},
ID: "proposal",
},
Votes: map[string]state.ProposalVote{
"wallet": {
Expand All @@ -73,7 +72,7 @@ func TestReportGeneratorWithVoteError(t *testing.T) {

entry, ok := report.Entries[0].(events.VoteQueryError)
assert.True(t, ok, "Expected to have a vote query error!")
assert.Equal(t, entry.Proposal.ProposalID, "proposal", "Proposal ID mismatch!")
assert.Equal(t, entry.Proposal.ID, "proposal", "Proposal ID mismatch!")
}

func TestReportGeneratorWithNotVoted(t *testing.T) {
Expand All @@ -88,8 +87,7 @@ func TestReportGeneratorWithNotVoted(t *testing.T) {
ProposalVotes: map[string]state.WalletVotes{
"proposal": {
Proposal: types.Proposal{
ProposalID: "proposal",
Content: &types.ProposalContent{},
ID: "proposal",
},
Votes: map[string]state.ProposalVote{
"wallet": {},
Expand All @@ -109,7 +107,7 @@ func TestReportGeneratorWithNotVoted(t *testing.T) {

entry, ok := report.Entries[0].(events.NotVotedEvent)
assert.True(t, ok, "Expected to have not voted type!")
assert.Equal(t, entry.Proposal.ProposalID, "proposal", "Proposal ID mismatch!")
assert.Equal(t, entry.Proposal.ID, "proposal", "Proposal ID mismatch!")
}

func TestReportGeneratorWithVoted(t *testing.T) {
Expand All @@ -123,8 +121,7 @@ func TestReportGeneratorWithVoted(t *testing.T) {
ProposalVotes: map[string]state.WalletVotes{
"proposal": {
Proposal: types.Proposal{
ProposalID: "proposal",
Content: &types.ProposalContent{},
ID: "proposal",
},
Votes: map[string]state.ProposalVote{
"wallet": {},
Expand All @@ -140,8 +137,7 @@ func TestReportGeneratorWithVoted(t *testing.T) {
ProposalVotes: map[string]state.WalletVotes{
"proposal": {
Proposal: types.Proposal{
ProposalID: "proposal",
Content: &types.ProposalContent{},
ID: "proposal",
},
Votes: map[string]state.ProposalVote{
"wallet": {
Expand All @@ -165,7 +161,7 @@ func TestReportGeneratorWithVoted(t *testing.T) {

entry, ok := report.Entries[0].(events.VotedEvent)
assert.True(t, ok, "Expected to have voted type!")
assert.Equal(t, entry.Proposal.ProposalID, "proposal", "Proposal ID mismatch!")
assert.Equal(t, entry.Proposal.ID, "proposal", "Proposal ID mismatch!")
}

func TestReportGeneratorWithRevoted(t *testing.T) {
Expand All @@ -179,8 +175,7 @@ func TestReportGeneratorWithRevoted(t *testing.T) {
ProposalVotes: map[string]state.WalletVotes{
"proposal": {
Proposal: types.Proposal{
ProposalID: "proposal",
Content: &types.ProposalContent{},
ID: "proposal",
},
Votes: map[string]state.ProposalVote{
"wallet": {
Expand All @@ -200,8 +195,7 @@ func TestReportGeneratorWithRevoted(t *testing.T) {
ProposalVotes: map[string]state.WalletVotes{
"proposal": {
Proposal: types.Proposal{
ProposalID: "proposal",
Content: &types.ProposalContent{},
ID: "proposal",
},
Votes: map[string]state.ProposalVote{
"wallet": {
Expand All @@ -225,5 +219,5 @@ func TestReportGeneratorWithRevoted(t *testing.T) {

entry, ok := report.Entries[0].(events.RevotedEvent)
assert.True(t, ok, "Expected to have revoted type!")
assert.Equal(t, entry.Proposal.ProposalID, "proposal", "Proposal ID mismatch!")
assert.Equal(t, entry.Proposal.ID, "proposal", "Proposal ID mismatch!")
}
14 changes: 7 additions & 7 deletions pkg/reporters/pagerduty/pagerduty.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ func (r *Reporter) NewAlertFromReportEntry(eventRaw entry.ReportEntry) (Alert, e
dedupKey := fmt.Sprintf(
"cosmos-proposals-checker alert chain=%s proposal=%s wallet=%s",
event.GetChain().Name,
event.GetProposal().ProposalID,
event.GetProposal().ID,
event.GetWallet().AddressOrAlias(),
)

Expand All @@ -75,7 +75,7 @@ func (r *Reporter) NewAlertFromReportEntry(eventRaw entry.ReportEntry) (Alert, e
}

links := []Link{}
explorerLinks := event.GetChain().GetExplorerProposalsLinks(event.GetProposal().ProposalID)
explorerLinks := event.GetChain().GetExplorerProposalsLinks(event.GetProposal().ID)
for _, link := range explorerLinks {
links = append(links, Link{
Href: link.Href,
Expand All @@ -88,19 +88,19 @@ func (r *Reporter) NewAlertFromReportEntry(eventRaw entry.ReportEntry) (Alert, e
Summary: fmt.Sprintf(
"Wallet %s hasn't voted on proposal %s on %s: %s",
event.GetWallet().AddressOrAlias(),
event.GetProposal().ProposalID,
event.GetProposal().ID,
event.GetChain().GetName(),
event.GetProposal().Content.Title,
event.GetProposal().Title,
),
Timestamp: time.Now().Format(time.RFC3339),
Severity: "error",
Source: hostname,
CustomDetails: map[string]string{
"Wallet": event.GetWallet().AddressOrAlias(),
"Chain": event.GetChain().GetName(),
"Proposal ID": event.GetProposal().ProposalID,
"Proposal title": event.GetProposal().Content.Title,
"Proposal description": event.GetProposal().Content.Description,
"Proposal ID": event.GetProposal().ID,
"Proposal title": event.GetProposal().Title,
"Proposal description": event.GetProposal().Description,
},
},
Links: links,
Expand Down
Loading

0 comments on commit b27cf2b

Please sign in to comment.