Skip to content

Commit

Permalink
feat: added fix command (#228)
Browse files Browse the repository at this point in the history
## Description
This PR implements the `fix` command that allows to fix BDJuno issues without having to re-index the whole database at once.

Closes #227 
Adds a way to fix #208 

## Checklist
- [ ] Targeted PR against correct branch.
- [ ] Linked to Github issue with discussion and accepted design OR link to spec that describes this work.
- [ ] Wrote unit tests.  
- [ ] Re-reviewed `Files changed` in the Github PR explorer.
  • Loading branch information
RiccardoM authored Oct 18, 2021
1 parent 42f4b7b commit b81e781
Show file tree
Hide file tree
Showing 18 changed files with 443 additions and 35 deletions.
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ all: lint build test-unit
### Build flags ###
###############################################################################

LD_FLAGS = -X github.com/desmos-labs/juno/version.Version=$(VERSION) \
-X github.com/desmos-labs/juno/version.Commit=$(COMMIT)
LD_FLAGS = -X github.com/forbole/juno/version.Version=$(VERSION) \
-X github.com/forbole/juno/version.Commit=$(COMMIT)

BUILD_FLAGS := -ldflags '$(LD_FLAGS)'

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
[![Go Report Card](https://goreportcard.com/badge/github.com/forbole/bdjuno)](https://goreportcard.com/report/github.com/forbole/bdjuno)
![Codecov branch](https://img.shields.io/codecov/c/github/forbole/bdjuno/cosmos/v0.40.x)

BDJuno (shorthand for BigDipper Juno) is the [Juno](https://github.com/desmos-labs/juno) implementation
BDJuno (shorthand for BigDipper Juno) is the [Juno](https://github.com/forbole/juno) implementation
for [BigDipper](https://github.com/forbole/big-dipper).

It extends the custom Juno behavior by adding different handlers and custom operations to make it easier for BigDipper
Expand Down
4 changes: 3 additions & 1 deletion cmd/bdjuno/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
parsecmd "github.com/forbole/juno/v2/cmd/parse"
"github.com/forbole/juno/v2/modules/messages"

fixcmd "github.com/forbole/bdjuno/v2/cmd/fix"
migratecmd "github.com/forbole/bdjuno/v2/cmd/migrate"

"github.com/forbole/bdjuno/v2/types/config"
Expand All @@ -32,7 +33,8 @@ func main() {
cmd.VersionCmd(),
initcmd.InitCmd(cfg.GetInitConfig()),
parsecmd.ParseCmd(cfg.GetParseConfig()),
migratecmd.MigrateCmd(),
migratecmd.NewMigrateCmd(),
fixcmd.NewFixCmd(cfg.GetParseConfig()),
)

executor := cmd.PrepareRootCmd(cfg.GetName(), rootCmd)
Expand Down
40 changes: 40 additions & 0 deletions cmd/fix/fix.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package fix

import (
"github.com/forbole/juno/v2/cmd/parse"
"github.com/spf13/cobra"

fixgov "github.com/forbole/bdjuno/v2/cmd/fix/gov"
fixstaking "github.com/forbole/bdjuno/v2/cmd/fix/staking"
)

// NewFixCmd returns the Cobra command allowing to fix some BDJuno bugs without having to re-sync the whole database
func NewFixCmd(parseCfg *parse.Config) *cobra.Command {
cmd := &cobra.Command{
Use: "fix",
Short: "Apply some fixes without the need to re-syncing the whole database from scratch",
PersistentPreRunE: runPersistentPreRuns(parse.ReadConfig(parseCfg)),
}

cmd.AddCommand(
fixgov.NewGovCmd(parseCfg),
fixstaking.NewStakingCmd(parseCfg),
)

return cmd
}

func runPersistentPreRuns(preRun func(_ *cobra.Command, _ []string) error) func(_ *cobra.Command, _ []string) error {
return func(cmd *cobra.Command, args []string) error {
if root := cmd.Root(); root != nil {
if root.PersistentPreRunE != nil {
err := root.PersistentPreRunE(root, args)
if err != nil {
return err
}
}
}

return preRun(cmd, args)
}
}
20 changes: 20 additions & 0 deletions cmd/fix/gov/cmd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package gov

import (
"github.com/forbole/juno/v2/cmd/parse"
"github.com/spf13/cobra"
)

// NewGovCmd returns the Cobra command allowing to fix various things related to the x/gov module
func NewGovCmd(parseConfig *parse.Config) *cobra.Command {
cmd := &cobra.Command{
Use: "gov",
Short: "Fix things related to the x/gov module",
}

cmd.AddCommand(
proposalCmd(parseConfig),
)

return cmd
}
152 changes: 152 additions & 0 deletions cmd/fix/gov/proposal.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
package gov

import (
"encoding/hex"
"fmt"

govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
"github.com/forbole/juno/v2/cmd/parse"
"github.com/forbole/juno/v2/types/config"
"github.com/spf13/cobra"

"github.com/forbole/bdjuno/v2/database"
"github.com/forbole/bdjuno/v2/modules"
"github.com/forbole/bdjuno/v2/modules/gov"
"github.com/forbole/bdjuno/v2/utils"
)

// proposalCmd returns the Cobra command allowing to fix all things related to a proposal
func proposalCmd(parseConfig *parse.Config) *cobra.Command {
return &cobra.Command{
Use: "proposal [id]",
Short: "Get the description, votes and everything related to a proposal given its id",
RunE: func(cmd *cobra.Command, args []string) error {
proposalID := args[0]

parseCtx, err := parse.GetParsingContext(parseConfig)
if err != nil {
return err
}

sources, err := modules.BuildSources(config.Cfg.Node, parseCtx.EncodingConfig)
if err != nil {
return err
}

// Get the database
db := database.Cast(parseCtx.Database)

// Build the gov module
govModule := gov.NewModule(parseCtx.EncodingConfig.Marshaler, sources.GovSource, nil, nil, nil, db)

err = refreshProposalDetails(parseCtx, proposalID, govModule)
if err != nil {
return err
}

err = refreshProposalDeposits(parseCtx, proposalID, govModule)
if err != nil {
return err
}

err = refreshProposalVotes(parseCtx, proposalID, govModule)
if err != nil {
return err
}

return nil
},
}
}

func refreshProposalDetails(parseCtx *parse.Context, proposalID string, govModule *gov.Module) error {
// Get the tx that created the proposal
txs, err := utils.QueryTxs(parseCtx.Node, fmt.Sprintf("submit_proposal.proposal_id=%s", proposalID))
if err != nil {
return err
}

if len(txs) > 1 {
return fmt.Errorf("expecting only one create proposal transaction, found %d", len(txs))
}

// Get the tx details
tx, err := parseCtx.Node.Tx(hex.EncodeToString(txs[0].Tx.Hash()))
if err != nil {
return err
}

// Handle the MsgSubmitProposal messages
for index, msg := range tx.GetMsgs() {
if _, ok := msg.(*govtypes.MsgSubmitProposal); !ok {
continue
}

err = govModule.HandleMsg(index, msg, tx)
if err != nil {
return fmt.Errorf("error while handling MsgSubmitProposal: %s", err)
}
}

return nil
}

func refreshProposalDeposits(parseCtx *parse.Context, proposalID string, govModule *gov.Module) error {
// Get the tx that deposited to the proposal
txs, err := utils.QueryTxs(parseCtx.Node, fmt.Sprintf("proposal_deposit.proposal_id=%s", proposalID))
if err != nil {
return err
}

for _, tx := range txs {
// Get the tx details
junoTx, err := parseCtx.Node.Tx(hex.EncodeToString(tx.Tx.Hash()))
if err != nil {
return err
}

// Handle the MsgDeposit messages
for index, msg := range junoTx.GetMsgs() {
if _, ok := msg.(*govtypes.MsgDeposit); !ok {
continue
}

err = govModule.HandleMsg(index, msg, junoTx)
if err != nil {
return fmt.Errorf("error while handling MsgDeposit: %s", err)
}
}
}

return nil
}

func refreshProposalVotes(parseCtx *parse.Context, proposalID string, govModule *gov.Module) error {
// Get the tx that voted the proposal
txs, err := utils.QueryTxs(parseCtx.Node, fmt.Sprintf("proposal_vote.proposal_id=%s", proposalID))
if err != nil {
return err
}

for _, tx := range txs {
// Get the tx details
junoTx, err := parseCtx.Node.Tx(hex.EncodeToString(tx.Tx.Hash()))
if err != nil {
return err
}

// Handle the MsgVote messages
for index, msg := range junoTx.GetMsgs() {
if _, ok := msg.(*govtypes.MsgVote); !ok {
continue
}

err = govModule.HandleMsg(index, msg, junoTx)
if err != nil {
return fmt.Errorf("error while handling MsgVote: %s", err)
}
}
}

return nil
}
21 changes: 21 additions & 0 deletions cmd/fix/staking/cmd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package staking

import (
"github.com/forbole/juno/v2/cmd/parse"
"github.com/spf13/cobra"
)

// NewStakingCmd returns the Cobra command that allows to fix all the things related to the x/staking module
func NewStakingCmd(parseConfig *parse.Config) *cobra.Command {
cmd := &cobra.Command{
Use: "staking",
Short: "Fix things related to the x/staking module",
}

cmd.AddCommand(
validatorsCmd(parseConfig),
slashesCmd(parseConfig),
)

return cmd
}
69 changes: 69 additions & 0 deletions cmd/fix/staking/slashes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package staking

import (
"fmt"

"github.com/forbole/juno/v2/cmd/parse"
"github.com/forbole/juno/v2/types/config"
"github.com/spf13/cobra"

"github.com/forbole/bdjuno/v2/database"
"github.com/forbole/bdjuno/v2/modules"
"github.com/forbole/bdjuno/v2/modules/staking"
)

// slashesCmd returns a Cobra command that allows to fix the delegations for all the slashed validators.
func slashesCmd(parseConfig *parse.Config) *cobra.Command {
return &cobra.Command{
Use: "slashes",
Short: "Fix the delegations for all the slashed validators, taking their delegations from the latest known height",
RunE: func(cmd *cobra.Command, args []string) error {
parseCtx, err := parse.GetParsingContext(parseConfig)
if err != nil {
return err
}

sources, err := modules.BuildSources(config.Cfg.Node, parseCtx.EncodingConfig)
if err != nil {
return err
}

// Get the database
db := database.Cast(parseCtx.Database)

// Get latest height
height, err := parseCtx.Node.LatestHeight()
if err != nil {
return fmt.Errorf("error while getting latest block height: %s", err)
}

// Get all validators
validators, err := sources.StakingSource.GetValidatorsWithStatus(height, "")
if err != nil {
return fmt.Errorf("error while getting validators: %s", err)
}

for _, validator := range validators {
// Get the validator delegations
delegations, err := sources.StakingSource.GetValidatorDelegations(height, validator.OperatorAddress)
if err != nil {
return fmt.Errorf("error while getting validator delegations: %s", err)
}

// Delete the old delegations
err = db.DeleteValidatorDelegations(validator.OperatorAddress)
if err != nil {
return fmt.Errorf("error while deleting validator delegations: %s", err)
}

// Save the delegations
err = db.SaveDelegations(staking.ConvertDelegationsResponses(height, delegations))
if err != nil {
return fmt.Errorf("error while saving delegations: %s", err)
}
}

return nil
},
}
}
Loading

0 comments on commit b81e781

Please sign in to comment.