From 3b973cd57fe1c199609d06dc36c03306fb94b824 Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Wed, 20 Mar 2024 22:14:07 +0800 Subject: [PATCH 1/5] fix: re-add support for gov v1beta1 msgs --- cmd/parse/gov/proposal.go | 20 +++++-- modules/gov/handle_msg.go | 105 ++++++++++++++++++++---------------- modules/gov/module.go | 9 ++-- modules/gov/utils_events.go | 63 ++++++++++++++++++++++ utils/events/events.go | 25 +++++++++ 5 files changed, 169 insertions(+), 53 deletions(-) create mode 100644 modules/gov/utils_events.go create mode 100644 utils/events/events.go diff --git a/cmd/parse/gov/proposal.go b/cmd/parse/gov/proposal.go index e0aaeb9dc..787b48f98 100644 --- a/cmd/parse/gov/proposal.go +++ b/cmd/parse/gov/proposal.go @@ -124,7 +124,11 @@ func refreshProposalDetails(parseCtx *parser.Context, proposalID uint64, govModu // Handle the MsgSubmitProposal messages for index, msg := range tx.GetMsgs() { - if _, ok := msg.(*govtypesv1.MsgSubmitProposal); !ok { + _, isMsgSubmitProposalV1 := msg.(*govtypesv1.MsgSubmitProposal) + _, isMsgSubmitProposalV1Beta1 := msg.(*govtypesv1beta1.MsgSubmitProposal) + + // Skip if the message is not a submit proposal message + if !isMsgSubmitProposalV1 && !isMsgSubmitProposalV1Beta1 { continue } @@ -155,7 +159,11 @@ func refreshProposalDeposits(parseCtx *parser.Context, proposalID uint64, govMod // Handle the MsgDeposit messages for index, msg := range junoTx.GetMsgs() { - if _, ok := msg.(*govtypesv1.MsgDeposit); !ok { + _, isMsgDepositV1beta1 := msg.(*govtypesv1beta1.MsgDeposit) + _, isMsgDepositV1 := msg.(*govtypesv1.MsgDeposit) + + // Skip if the message is not a deposit message + if !isMsgDepositV1 && !isMsgDepositV1beta1 { continue } @@ -187,7 +195,13 @@ func refreshProposalVotes(parseCtx *parser.Context, proposalID uint64, govModule // Handle the MsgVote messages for index, msg := range junoTx.GetMsgs() { - if msgVote, ok := msg.(*govtypesv1.MsgVote); !ok { + _, isMsgVoteV1 := msg.(*govtypesv1.MsgVote) + _, isMsgVoteV1Beta1 := msg.(*govtypesv1beta1.MsgVote) + _, isMsgVoteWeightedV1 := msg.(*govtypesv1.MsgVoteWeighted) + _, isMsgVoteWeightedV1Beta1 := msg.(*govtypesv1beta1.MsgVoteWeighted) + + // Skip if the message is not a vote message + if !isMsgVoteV1 && !isMsgVoteV1Beta1 && !isMsgVoteWeightedV1 && !isMsgVoteWeightedV1Beta1 { continue } else { // check if requested proposal ID is the same as proposal ID returned diff --git a/modules/gov/handle_msg.go b/modules/gov/handle_msg.go index 8e8573b82..1a62ba618 100644 --- a/modules/gov/handle_msg.go +++ b/modules/gov/handle_msg.go @@ -2,21 +2,19 @@ package gov import ( "fmt" - "strconv" "strings" "time" - "github.com/cosmos/cosmos-sdk/x/authz" - - "github.com/forbole/callisto/v4/types" + "github.com/forbole/bdjuno/v4/types" "google.golang.org/grpc/codes" sdk "github.com/cosmos/cosmos-sdk/types" - distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" + "github.com/cosmos/cosmos-sdk/x/authz" + govtypesv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" + govtypesv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" - gov "github.com/cosmos/cosmos-sdk/x/gov/types" - juno "github.com/forbole/juno/v5/types" + juno "github.com/forbole/juno/v4/types" ) // HandleMsgExec implements modules.AuthzMessageModule @@ -32,37 +30,35 @@ func (m *Module) HandleMsg(index int, msg sdk.Msg, tx *juno.Tx) error { switch cosmosMsg := msg.(type) { case *govtypesv1.MsgSubmitProposal: - return m.handleMsgSubmitProposal(tx, index, cosmosMsg) + return m.handleSubmitProposalEvent(tx, cosmosMsg.Proposer, tx.Logs[index].Events) + case *govtypesv1beta1.MsgSubmitProposal: + return m.handleSubmitProposalEvent(tx, cosmosMsg.Proposer, tx.Logs[index].Events) case *govtypesv1.MsgDeposit: - return m.handleMsgDeposit(tx, cosmosMsg) + return m.handleDepositEvent(tx, cosmosMsg.Depositor, tx.Logs[index].Events) + case *govtypesv1beta1.MsgDeposit: + return m.handleDepositEvent(tx, cosmosMsg.Depositor, tx.Logs[index].Events) case *govtypesv1.MsgVote: - return m.handleMsgVote(tx, cosmosMsg) + return m.handleVoteEvent(tx, cosmosMsg.Voter, tx.Logs[index].Events) + case *govtypesv1beta1.MsgVote: + return m.handleVoteEvent(tx, cosmosMsg.Voter, tx.Logs[index].Events) case *govtypesv1.MsgVoteWeighted: - return m.handleMsgVoteWeighted(tx, cosmosMsg) + return m.handleVoteEvent(tx, cosmosMsg.Voter, tx.Logs[index].Events) + case *govtypesv1beta1.MsgVoteWeighted: + return m.handleVoteEvent(tx, cosmosMsg.Voter, tx.Logs[index].Events) } return nil } -// handleMsgSubmitProposal allows to properly handle a MsgSubmitProposal -func (m *Module) handleMsgSubmitProposal(tx *juno.Tx, index int, msg *govtypesv1.MsgSubmitProposal) error { +// handleSubmitProposalEvent allows to properly handle a handleSubmitProposalEvent +func (m *Module) handleSubmitProposalEvent(tx *juno.Tx, proposer string, events sdk.StringEvents) error { // Get the proposal id - event, err := tx.FindEventByType(index, gov.EventTypeSubmitProposal) - if err != nil { - return fmt.Errorf("error while searching for EventTypeSubmitProposal: %s", err) - } - - id, err := tx.FindAttributeByKey(event, gov.AttributeKeyProposalID) + proposalID, err := ProposalIDFromEvents(events) if err != nil { - return fmt.Errorf("error while searching for AttributeKeyProposalID: %s", err) - } - - proposalID, err := strconv.ParseUint(id, 10, 64) - if err != nil { - return fmt.Errorf("error while parsing proposal id: %s", err) + return fmt.Errorf("error while getting proposal id: %s", err) } // Get the proposal @@ -108,39 +104,44 @@ func (m *Module) handleMsgSubmitProposal(tx *juno.Tx, index int, msg *govtypesv1 return fmt.Errorf("error while storing proposal recipient: %s", err) } + // Unpack the proposal interfaces + err = proposal.UnpackInterfaces(m.cdc) + if err != nil { + return fmt.Errorf("error while unpacking proposal interfaces: %s", err) + } + // Store the proposal proposalObj := types.NewProposal( - proposal.Id, - proposal.Title, - proposal.Summary, - proposal.Metadata, - msg.Messages, + proposal.ProposalId, + proposal.GetContent().ProposalRoute(), + proposal.GetContent().ProposalType(), + proposal.GetContent(), proposal.Status.String(), *proposal.SubmitTime, *proposal.DepositEndTime, proposal.VotingStartTime, proposal.VotingEndTime, - msg.Proposer, + proposer, ) err = m.db.SaveProposals([]types.Proposal{proposalObj}) if err != nil { - return err + return fmt.Errorf("error while saving proposal: %s", err) } - txTimestamp, err := time.Parse(time.RFC3339, tx.Timestamp) + // Submit proposal must have a deposit event with depositor equal to the proposer + return m.handleDepositEvent(tx, proposer, events) +} + +// handleDepositEvent allows to properly handle a handleDepositEvent +func (m *Module) handleDepositEvent(tx *juno.Tx, depositor string, events sdk.StringEvents) error { + // Get the proposal id + proposalID, err := ProposalIDFromEvents(events) if err != nil { - return fmt.Errorf("error while parsing time: %s", err) + return fmt.Errorf("error while getting proposal id: %s", err) } - // Store the deposit - deposit := types.NewDeposit(proposal.Id, msg.Proposer, msg.InitialDeposit, txTimestamp, tx.TxHash, tx.Height) - return m.db.SaveDeposits([]types.Deposit{deposit}) -} - -// handleMsgDeposit allows to properly handle a MsgDeposit -func (m *Module) handleMsgDeposit(tx *juno.Tx, msg *govtypesv1.MsgDeposit) error { - deposit, err := m.source.ProposalDeposit(tx.Height, msg.ProposalId, msg.Depositor) + deposit, err := m.source.ProposalDeposit(tx.Height, proposalID, depositor) if err != nil { return fmt.Errorf("error while getting proposal deposit: %s", err) } @@ -150,18 +151,30 @@ func (m *Module) handleMsgDeposit(tx *juno.Tx, msg *govtypesv1.MsgDeposit) error } return m.db.SaveDeposits([]types.Deposit{ - types.NewDeposit(msg.ProposalId, msg.Depositor, deposit.Amount, txTimestamp, tx.TxHash, tx.Height), + types.NewDeposit(proposalID, depositor, deposit.Amount, txTimestamp, tx.Height), }) } -// handleMsgVote allows to properly handle a MsgVote -func (m *Module) handleMsgVote(tx *juno.Tx, msg *govtypesv1.MsgVote) error { +// handleVoteEvent allows to properly handle a handleVoteEvent +func (m *Module) handleVoteEvent(tx *juno.Tx, voter string, events sdk.StringEvents) error { + // Get the proposal id + proposalID, err := ProposalIDFromEvents(events) + if err != nil { + return fmt.Errorf("error while getting proposal id: %s", err) + } + txTimestamp, err := time.Parse(time.RFC3339, tx.Timestamp) if err != nil { return fmt.Errorf("error while parsing time: %s", err) } - vote := types.NewVote(msg.ProposalId, msg.Voter, msg.Option, "1.0", txTimestamp, tx.Height) + // Get the vote option + voteOption, err := VoteOptionFromEvents(events) + if err != nil { + return fmt.Errorf("error while getting vote option: %s", err) + } + + vote := types.NewVote(proposalID, voter, voteOption, txTimestamp, tx.Height) err = m.db.SaveVote(vote) if err != nil { diff --git a/modules/gov/module.go b/modules/gov/module.go index 5db42c0ad..34ca290f3 100644 --- a/modules/gov/module.go +++ b/modules/gov/module.go @@ -11,10 +11,11 @@ import ( ) var ( - _ modules.Module = &Module{} - _ modules.GenesisModule = &Module{} - _ modules.BlockModule = &Module{} - _ modules.MessageModule = &Module{} + _ modules.Module = &Module{} + _ modules.GenesisModule = &Module{} + _ modules.BlockModule = &Module{} + _ modules.MessageModule = &Module{} + _ modules.AuthzMessageModule = &Module{} ) // Module represent x/gov module diff --git a/modules/gov/utils_events.go b/modules/gov/utils_events.go new file mode 100644 index 000000000..4b426b6ba --- /dev/null +++ b/modules/gov/utils_events.go @@ -0,0 +1,63 @@ +package gov + +import ( + "encoding/json" + "fmt" + "strconv" + "strings" + + sdk "github.com/cosmos/cosmos-sdk/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + govtypesv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" + eventsutil "github.com/forbole/bdjuno/v4/utils/events" +) + +// ProposalIDFromEvent returns the proposal id from the given events +func ProposalIDFromEvents(events sdk.StringEvents) (uint64, error) { + for _, event := range events { + attribute, ok := eventsutil.FindAttributeByKey(event, govtypes.AttributeKeyProposalID) + if ok { + return strconv.ParseUint(attribute.Value, 10, 64) + } + } + + return 0, fmt.Errorf("no proposal id found") +} + +// VoteOptionFromEvents returns the vote option from the given events +func VoteOptionFromEvents(events sdk.StringEvents) (govtypesv1.VoteOption, error) { + for _, event := range events { + attribute, ok := eventsutil.FindAttributeByKey(event, govtypes.AttributeKeyOption) + if ok { + return parseVoteOption(attribute.Value) + } + } + + return 0, fmt.Errorf("no vote option found") +} + +// parseVoteOption returns the vote option from the given string +// option value in string could be 2 cases, for example: +// 1. "{\"option\":1,\"weight\":\"1.000000000000000000\"}" +// 2. "option:VOTE_OPTION_NO weight:\"1.000000000000000000\"" +func parseVoteOption(optionValue string) (govtypesv1.VoteOption, error) { + // try parse option value as json + type voteOptionJSON struct { + Option govtypesv1.VoteOption `json:"option"` + } + var voteOptionParsedJSON voteOptionJSON + err := json.Unmarshal([]byte(optionValue), &voteOptionParsedJSON) + if err == nil { + return voteOptionParsedJSON.Option, nil + } + + // try parse option value as string + // option:VOTE_OPTION_NO weight:"1.000000000000000000" + voteOptionParsed := strings.Split(optionValue, " ") + voteOption, err := govtypesv1.VoteOptionFromString(strings.ReplaceAll(voteOptionParsed[0], "option:", "")) + if err != nil { + return 0, fmt.Errorf("failed to parse vote option %s: %s", optionValue, err) + } + + return voteOption, nil +} diff --git a/utils/events/events.go b/utils/events/events.go new file mode 100644 index 000000000..5b175dbcd --- /dev/null +++ b/utils/events/events.go @@ -0,0 +1,25 @@ +package events + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// FindEventByType returns the event with the given type +func FindEventByType(events sdk.StringEvents, eventType string) (sdk.StringEvent, bool) { + for _, event := range events { + if event.Type == eventType { + return event, true + } + } + return sdk.StringEvent{}, false +} + +// FindAttributeByKey returns the attribute with the given key +func FindAttributeByKey(event sdk.StringEvent, key string) (sdk.Attribute, bool) { + for _, attribute := range event.Attributes { + if attribute.Key == key { + return attribute, true + } + } + return sdk.Attribute{}, false +} From 9a1459125b9b89d97bdb3a3d97d8df75fd28c0ce Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Thu, 21 Mar 2024 12:04:29 +0800 Subject: [PATCH 2/5] chore: fix name and version conflict --- modules/gov/handle_msg.go | 4 ++-- modules/gov/utils_events.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/gov/handle_msg.go b/modules/gov/handle_msg.go index 1a62ba618..1bc133247 100644 --- a/modules/gov/handle_msg.go +++ b/modules/gov/handle_msg.go @@ -5,7 +5,7 @@ import ( "strings" "time" - "github.com/forbole/bdjuno/v4/types" + "github.com/forbole/callisto/v4/types" "google.golang.org/grpc/codes" sdk "github.com/cosmos/cosmos-sdk/types" @@ -14,7 +14,7 @@ import ( govtypesv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" govtypesv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" - juno "github.com/forbole/juno/v4/types" + juno "github.com/forbole/juno/v5/types" ) // HandleMsgExec implements modules.AuthzMessageModule diff --git a/modules/gov/utils_events.go b/modules/gov/utils_events.go index 4b426b6ba..01bacbdd8 100644 --- a/modules/gov/utils_events.go +++ b/modules/gov/utils_events.go @@ -9,7 +9,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" govtypesv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" - eventsutil "github.com/forbole/bdjuno/v4/utils/events" + eventsutil "github.com/forbole/callisto/v4/utils/events" ) // ProposalIDFromEvent returns the proposal id from the given events From e2670f539e5c4201542ac4acde4927a71db3df4e Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Thu, 21 Mar 2024 18:20:40 +0800 Subject: [PATCH 3/5] fix: fix confilct on version --- cmd/parse/gov/proposal.go | 49 +++++++++++++++++++++++-------------- modules/gov/handle_msg.go | 38 ++++++++-------------------- modules/gov/utils_events.go | 33 +++++++++++++------------ 3 files changed, 58 insertions(+), 62 deletions(-) diff --git a/cmd/parse/gov/proposal.go b/cmd/parse/gov/proposal.go index 787b48f98..cb954fad1 100644 --- a/cmd/parse/gov/proposal.go +++ b/cmd/parse/gov/proposal.go @@ -10,6 +10,7 @@ import ( modulestypes "github.com/forbole/callisto/v4/modules/types" govtypesv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" + govtypesv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" parsecmdtypes "github.com/forbole/juno/v5/cmd/parse/types" "github.com/forbole/juno/v5/parser" "github.com/forbole/juno/v5/types/config" @@ -195,29 +196,39 @@ func refreshProposalVotes(parseCtx *parser.Context, proposalID uint64, govModule // Handle the MsgVote messages for index, msg := range junoTx.GetMsgs() { - _, isMsgVoteV1 := msg.(*govtypesv1.MsgVote) - _, isMsgVoteV1Beta1 := msg.(*govtypesv1beta1.MsgVote) - _, isMsgVoteWeightedV1 := msg.(*govtypesv1.MsgVoteWeighted) - _, isMsgVoteWeightedV1Beta1 := msg.(*govtypesv1beta1.MsgVoteWeighted) + var msgProposalID uint64 + + switch cosmosMsg := msg.(type) { + case *govtypesv1.MsgVote: + msgProposalID = cosmosMsg.ProposalId + + case *govtypesv1beta1.MsgVote: + msgProposalID = cosmosMsg.ProposalId + + case *govtypesv1.MsgVoteWeighted: + msgProposalID = cosmosMsg.ProposalId + + case *govtypesv1beta1.MsgVoteWeighted: + msgProposalID = cosmosMsg.ProposalId // Skip if the message is not a vote message - if !isMsgVoteV1 && !isMsgVoteV1Beta1 && !isMsgVoteWeightedV1 && !isMsgVoteWeightedV1Beta1 { + default: continue - } else { - // check if requested proposal ID is the same as proposal ID returned - // from the msg as some txs may contain multiple MsgVote msgs - // for different proposals which can cause error if one of the proposals - // info is not stored in database - if proposalID == msgVote.ProposalId { - err = govModule.HandleMsg(index, msg, junoTx) - if err != nil { - return fmt.Errorf("error while handling MsgVote: %s", err) - } - } else { - // skip votes for proposals with IDs - // different than requested in the query - continue + } + + // check if requested proposal ID is the same as proposal ID returned + // from the msg as some txs may contain multiple MsgVote msgs + // for different proposals which can cause error if one of the proposals + // info is not stored in database + if proposalID == msgProposalID { + err = govModule.HandleMsg(index, msg, junoTx) + if err != nil { + return fmt.Errorf("error while handling MsgVote: %s", err) } + } else { + // skip votes for proposals with IDs + // different than requested in the query + continue } } } diff --git a/modules/gov/handle_msg.go b/modules/gov/handle_msg.go index 1bc133247..d8f0de4b2 100644 --- a/modules/gov/handle_msg.go +++ b/modules/gov/handle_msg.go @@ -10,7 +10,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/authz" - + distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" govtypesv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" govtypesv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" @@ -112,10 +112,11 @@ func (m *Module) handleSubmitProposalEvent(tx *juno.Tx, proposer string, events // Store the proposal proposalObj := types.NewProposal( - proposal.ProposalId, - proposal.GetContent().ProposalRoute(), - proposal.GetContent().ProposalType(), - proposal.GetContent(), + proposal.Id, + proposal.Title, + proposal.Summary, + proposal.Metadata, + proposal.Messages, proposal.Status.String(), *proposal.SubmitTime, *proposal.DepositEndTime, @@ -151,7 +152,7 @@ func (m *Module) handleDepositEvent(tx *juno.Tx, depositor string, events sdk.St } return m.db.SaveDeposits([]types.Deposit{ - types.NewDeposit(proposalID, depositor, deposit.Amount, txTimestamp, tx.Height), + types.NewDeposit(proposalID, depositor, deposit.Amount, txTimestamp, tx.TxHash, tx.Height), }) } @@ -169,12 +170,12 @@ func (m *Module) handleVoteEvent(tx *juno.Tx, voter string, events sdk.StringEve } // Get the vote option - voteOption, err := VoteOptionFromEvents(events) + weightVoteOption, err := WeightVoteOptionFromEvents(events) if err != nil { return fmt.Errorf("error while getting vote option: %s", err) } - vote := types.NewVote(proposalID, voter, voteOption, txTimestamp, tx.Height) + vote := types.NewVote(proposalID, voter, weightVoteOption.Option, weightVoteOption.Weight, txTimestamp, tx.Height) err = m.db.SaveVote(vote) if err != nil { @@ -182,24 +183,5 @@ func (m *Module) handleVoteEvent(tx *juno.Tx, voter string, events sdk.StringEve } // update tally result for given proposal - return m.UpdateProposalTallyResult(msg.ProposalId, tx.Height) -} - -// handleMsgVoteWeighted allows to properly handle a MsgVoteWeighted -func (m *Module) handleMsgVoteWeighted(tx *juno.Tx, msg *govtypesv1.MsgVoteWeighted) error { - txTimestamp, err := time.Parse(time.RFC3339, tx.Timestamp) - if err != nil { - return fmt.Errorf("error while parsing time: %s", err) - } - - for _, option := range msg.Options { - vote := types.NewVote(msg.ProposalId, msg.Voter, option.Option, option.Weight, txTimestamp, tx.Height) - err = m.db.SaveVote(vote) - if err != nil { - return fmt.Errorf("error while saving weighted vote for address %s: %s", msg.Voter, err) - } - } - - // update tally result for given proposal - return m.UpdateProposalTallyResult(msg.ProposalId, tx.Height) + return m.UpdateProposalTallyResult(proposalID, tx.Height) } diff --git a/modules/gov/utils_events.go b/modules/gov/utils_events.go index 01bacbdd8..467d36171 100644 --- a/modules/gov/utils_events.go +++ b/modules/gov/utils_events.go @@ -24,40 +24,43 @@ func ProposalIDFromEvents(events sdk.StringEvents) (uint64, error) { return 0, fmt.Errorf("no proposal id found") } -// VoteOptionFromEvents returns the vote option from the given events -func VoteOptionFromEvents(events sdk.StringEvents) (govtypesv1.VoteOption, error) { +// WeightVoteOptionFromEvents returns the vote option from the given events +func WeightVoteOptionFromEvents(events sdk.StringEvents) (govtypesv1.WeightedVoteOption, error) { for _, event := range events { attribute, ok := eventsutil.FindAttributeByKey(event, govtypes.AttributeKeyOption) if ok { - return parseVoteOption(attribute.Value) + return parseWeightVoteOption(attribute.Value) } } - return 0, fmt.Errorf("no vote option found") + return govtypesv1.WeightedVoteOption{}, fmt.Errorf("no vote option found") } -// parseVoteOption returns the vote option from the given string -// option value in string could be 2 cases, for example: +// parseWeightVoteOption returns the vote option from the given string +// option value in string has 2 cases, for example: // 1. "{\"option\":1,\"weight\":\"1.000000000000000000\"}" // 2. "option:VOTE_OPTION_NO weight:\"1.000000000000000000\"" -func parseVoteOption(optionValue string) (govtypesv1.VoteOption, error) { +func parseWeightVoteOption(optionValue string) (govtypesv1.WeightedVoteOption, error) { // try parse option value as json - type voteOptionJSON struct { - Option govtypesv1.VoteOption `json:"option"` - } - var voteOptionParsedJSON voteOptionJSON - err := json.Unmarshal([]byte(optionValue), &voteOptionParsedJSON) + var weightedVoteOption govtypesv1.WeightedVoteOption + err := json.Unmarshal([]byte(optionValue), &weightedVoteOption) if err == nil { - return voteOptionParsedJSON.Option, nil + return weightedVoteOption, nil } // try parse option value as string // option:VOTE_OPTION_NO weight:"1.000000000000000000" voteOptionParsed := strings.Split(optionValue, " ") + if len(voteOptionParsed) != 2 { + return govtypesv1.WeightedVoteOption{}, fmt.Errorf("failed to parse vote option %s", optionValue) + } + voteOption, err := govtypesv1.VoteOptionFromString(strings.ReplaceAll(voteOptionParsed[0], "option:", "")) if err != nil { - return 0, fmt.Errorf("failed to parse vote option %s: %s", optionValue, err) + return govtypesv1.WeightedVoteOption{}, fmt.Errorf("failed to parse vote option %s: %s", optionValue, err) } + weight := strings.ReplaceAll(voteOptionParsed[1], "weight:", "") + weight = strings.ReplaceAll(weight, "\\", "") - return voteOption, nil + return govtypesv1.WeightedVoteOption{Option: voteOption, Weight: weight}, nil } From 0516d238ae34cfc975ce9dd3537646e6744fe24c Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Thu, 21 Mar 2024 19:00:53 +0800 Subject: [PATCH 4/5] chore: add unit test for parsing weight vote option --- modules/gov/utils_events.go | 6 +-- modules/gov/utils_events_test.go | 71 ++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 3 deletions(-) create mode 100644 modules/gov/utils_events_test.go diff --git a/modules/gov/utils_events.go b/modules/gov/utils_events.go index 467d36171..772d9e802 100644 --- a/modules/gov/utils_events.go +++ b/modules/gov/utils_events.go @@ -41,14 +41,14 @@ func WeightVoteOptionFromEvents(events sdk.StringEvents) (govtypesv1.WeightedVot // 1. "{\"option\":1,\"weight\":\"1.000000000000000000\"}" // 2. "option:VOTE_OPTION_NO weight:\"1.000000000000000000\"" func parseWeightVoteOption(optionValue string) (govtypesv1.WeightedVoteOption, error) { - // try parse option value as json + // try parse json option value var weightedVoteOption govtypesv1.WeightedVoteOption err := json.Unmarshal([]byte(optionValue), &weightedVoteOption) if err == nil { return weightedVoteOption, nil } - // try parse option value as string + // try parse string option value // option:VOTE_OPTION_NO weight:"1.000000000000000000" voteOptionParsed := strings.Split(optionValue, " ") if len(voteOptionParsed) != 2 { @@ -60,7 +60,7 @@ func parseWeightVoteOption(optionValue string) (govtypesv1.WeightedVoteOption, e return govtypesv1.WeightedVoteOption{}, fmt.Errorf("failed to parse vote option %s: %s", optionValue, err) } weight := strings.ReplaceAll(voteOptionParsed[1], "weight:", "") - weight = strings.ReplaceAll(weight, "\\", "") + weight = strings.ReplaceAll(weight, "\"", "") return govtypesv1.WeightedVoteOption{Option: voteOption, Weight: weight}, nil } diff --git a/modules/gov/utils_events_test.go b/modules/gov/utils_events_test.go new file mode 100644 index 000000000..8605e653b --- /dev/null +++ b/modules/gov/utils_events_test.go @@ -0,0 +1,71 @@ +package gov_test + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + govtypesv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" + "github.com/forbole/callisto/v4/modules/gov" + "github.com/stretchr/testify/require" +) + +func TestWeightVoteOptionFromEvents(t *testing.T) { + tests := []struct { + name string + events sdk.StringEvents + expected govtypesv1.WeightedVoteOption + shouldErr bool + }{ + { + "json option from vote event returns properly", + sdk.StringEvents{ + sdk.StringEvent{ + Type: "vote", + Attributes: []sdk.Attribute{ + sdk.NewAttribute(govtypes.AttributeKeyOption, "{\"option\":1,\"weight\":\"1.000000000000000000\"}"), + }, + }, + }, + govtypesv1.WeightedVoteOption{Option: govtypesv1.OptionYes, Weight: "1.000000000000000000"}, + false, + }, + { + "string option from vote event returns properly", + sdk.StringEvents{ + sdk.StringEvent{ + Type: "vote", + Attributes: []sdk.Attribute{ + sdk.NewAttribute(govtypes.AttributeKeyOption, "option:VOTE_OPTION_NO weight:\"1.000000000000000000\""), + }, + }, + }, + govtypesv1.WeightedVoteOption{Option: govtypesv1.OptionNo, Weight: "1.000000000000000000"}, + false, + }, + { + "invalid option from vote event returns error", + sdk.StringEvents{ + sdk.StringEvent{ + Type: "vote", + Attributes: []sdk.Attribute{ + sdk.NewAttribute("other", "value"), + }, + }, + }, + govtypesv1.WeightedVoteOption{}, + true, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + result, err := gov.WeightVoteOptionFromEvents(test.events) + if test.shouldErr { + require.Error(t, err) + } else { + require.Equal(t, test.expected, result) + } + }) + } +} From 3936cc6693dc527c9ba1ddc0317794902ccbdcab Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Tue, 26 Mar 2024 12:56:11 +0800 Subject: [PATCH 5/5] chore: improve gov cmd with switch pattern --- cmd/parse/gov/proposal.go | 33 ++++++++++++--------------------- 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/cmd/parse/gov/proposal.go b/cmd/parse/gov/proposal.go index cb954fad1..899455972 100644 --- a/cmd/parse/gov/proposal.go +++ b/cmd/parse/gov/proposal.go @@ -125,17 +125,13 @@ func refreshProposalDetails(parseCtx *parser.Context, proposalID uint64, govModu // Handle the MsgSubmitProposal messages for index, msg := range tx.GetMsgs() { - _, isMsgSubmitProposalV1 := msg.(*govtypesv1.MsgSubmitProposal) - _, isMsgSubmitProposalV1Beta1 := msg.(*govtypesv1beta1.MsgSubmitProposal) - // Skip if the message is not a submit proposal message - if !isMsgSubmitProposalV1 && !isMsgSubmitProposalV1Beta1 { - continue - } - - err = govModule.HandleMsg(index, msg, tx) - if err != nil { - return fmt.Errorf("error while handling MsgSubmitProposal: %s", err) + switch msg.(type) { + case *govtypesv1.MsgSubmitProposal, *govtypesv1beta1.MsgSubmitProposal: + err = govModule.HandleMsg(index, msg, tx) + if err != nil { + return fmt.Errorf("error while handling MsgSubmitProposal: %s", err) + } } } @@ -160,17 +156,12 @@ func refreshProposalDeposits(parseCtx *parser.Context, proposalID uint64, govMod // Handle the MsgDeposit messages for index, msg := range junoTx.GetMsgs() { - _, isMsgDepositV1beta1 := msg.(*govtypesv1beta1.MsgDeposit) - _, isMsgDepositV1 := msg.(*govtypesv1.MsgDeposit) - - // Skip if the message is not a deposit message - if !isMsgDepositV1 && !isMsgDepositV1beta1 { - continue - } - - err = govModule.HandleMsg(index, msg, junoTx) - if err != nil { - return fmt.Errorf("error while handling MsgDeposit: %s", err) + switch msg.(type) { + case *govtypesv1.MsgDeposit, *govtypesv1beta1.MsgDeposit: + err = govModule.HandleMsg(index, msg, junoTx) + if err != nil { + return fmt.Errorf("error while handling MsgDeposit: %s", err) + } } } }