diff --git a/CHANGELOG.md b/CHANGELOG.md index 31b00c0cc098..1744af7de28b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (slashing) [#548](https://github.com/osmosis-labs/cosmos-sdk/pull/548) Implement v0.50 slashing bitmap logic * (slashing) [#543](https://github.com/osmosis-labs/cosmos-sdk/pull/543) Make slashing not write sign info every block * (authz) [#513](https://github.com/osmosis-labs/cosmos-sdk/pull/513) Limit expired authz grant pruning to 200 per block +* (gov) [#514](https://github.com/osmosis-labs/cosmos-sdk/pull/514) Let gov hooks return an error ## IAVL v23 v1 Releases diff --git a/server/config/config.go b/server/config/config.go index 0923b6fd9aec..f191e5e7f96f 100644 --- a/server/config/config.go +++ b/server/config/config.go @@ -304,17 +304,18 @@ func (c *Config) GetMinGasPrices() sdk.DecCoins { func DefaultConfig() *Config { return &Config{ BaseConfig: BaseConfig{ - MinGasPrices: defaultMinGasPrices, - InterBlockCache: true, - Pruning: pruningtypes.PruningOptionDefault, - PruningKeepRecent: "0", - PruningInterval: "0", - MinRetainBlocks: 0, - IndexEvents: make([]string, 0), - IAVLCacheSize: 781250, - IAVLDisableFastNode: false, - IAVLLazyLoading: false, - AppDBBackend: "", + MinGasPrices: defaultMinGasPrices, + InterBlockCache: true, + Pruning: pruningtypes.PruningOptionDefault, + PruningKeepRecent: "0", + PruningInterval: "0", + MinRetainBlocks: 0, + IndexEvents: make([]string, 0), + IAVLCacheSize: 781250, + IAVLDisableFastNode: false, + IAVLFastNodeModuleWhitelist: []string{"lockup"}, + IAVLLazyLoading: false, + AppDBBackend: "", }, Telemetry: telemetry.Config{ Enabled: false, diff --git a/server/start.go b/server/start.go index cc8ce2a09055..c50fa8ca90d7 100644 --- a/server/start.go +++ b/server/start.go @@ -939,6 +939,7 @@ func addStartNodeFlags(cmd *cobra.Command, defaultNodeHome string) { cmd.Flags().Uint32(FlagStateSyncSnapshotKeepRecent, 2, "State sync snapshot to keep") cmd.Flags().Bool(FlagDisableIAVLFastNode, false, "Disable fast node for IAVL tree") + cmd.Flags().StringArray(FlagIAVLFastNodeModuleWhitelist, []string{"lockup"}, "Comma separated list of modules to enable fast node for") cmd.Flags().Int(FlagMempoolMaxTxs, mempool.DefaultMaxTx, "Sets MaxTx value for the app-side mempool") diff --git a/x/gov/abci.go b/x/gov/abci.go index f2f6e0150abc..6739b86895ea 100644 --- a/x/gov/abci.go +++ b/x/gov/abci.go @@ -30,7 +30,13 @@ func EndBlocker(ctx sdk.Context, keeper *keeper.Keeper) { } // called when proposal become inactive - keeper.Hooks().AfterProposalFailedMinDeposit(ctx, proposal.Id) + cacheCtx, writeCache := ctx.CacheContext() + err := keeper.Hooks().AfterProposalFailedMinDeposit(cacheCtx, proposal.Id) + if err == nil { // purposely ignoring the error here not to halt the chain if the hook fails + writeCache() + } else { + keeper.Logger(ctx).Error("failed to execute AfterProposalFailedMinDeposit hook", "error", err) + } ctx.EventManager().EmitEvent( sdk.NewEvent( @@ -142,7 +148,13 @@ func EndBlocker(ctx sdk.Context, keeper *keeper.Keeper) { keeper.SetProposal(ctx, proposal) // when proposal become active - keeper.Hooks().AfterProposalVotingPeriodEnded(ctx, proposal.Id) + cacheCtx, writeCache := ctx.CacheContext() + err := keeper.Hooks().AfterProposalVotingPeriodEnded(cacheCtx, proposal.Id) + if err == nil { // purposely ignoring the error here not to halt the chain if the hook fails + writeCache() + } else { + keeper.Logger(ctx).Error("failed to execute AfterProposalVotingPeriodEnded hook", "error", err) + } logger.Info( "proposal tallied", diff --git a/x/gov/keeper/deposit.go b/x/gov/keeper/deposit.go index e7f015e1c215..5a9463093bc5 100644 --- a/x/gov/keeper/deposit.go +++ b/x/gov/keeper/deposit.go @@ -148,7 +148,10 @@ func (keeper Keeper) AddDeposit(ctx sdk.Context, proposalID uint64, depositorAdd } // called when deposit has been added to a proposal, however the proposal may not be active - keeper.Hooks().AfterProposalDeposit(ctx, proposalID, depositorAddr) + err = keeper.Hooks().AfterProposalDeposit(ctx, proposalID, depositorAddr) + if err != nil { + return false, err + } ctx.EventManager().EmitEvent( sdk.NewEvent( diff --git a/x/gov/keeper/hooks_test.go b/x/gov/keeper/hooks_test.go index 48068877e513..17b58172d3c1 100644 --- a/x/gov/keeper/hooks_test.go +++ b/x/gov/keeper/hooks_test.go @@ -25,24 +25,29 @@ type MockGovHooksReceiver struct { AfterProposalVotingPeriodEndedValid bool } -func (h *MockGovHooksReceiver) AfterProposalSubmission(ctx sdk.Context, proposalID uint64) { +func (h *MockGovHooksReceiver) AfterProposalSubmission(ctx sdk.Context, proposalID uint64) error { h.AfterProposalSubmissionValid = true + return nil } -func (h *MockGovHooksReceiver) AfterProposalDeposit(ctx sdk.Context, proposalID uint64, depositorAddr sdk.AccAddress) { +func (h *MockGovHooksReceiver) AfterProposalDeposit(ctx sdk.Context, proposalID uint64, depositorAddr sdk.AccAddress) error { h.AfterProposalDepositValid = true + return nil } -func (h *MockGovHooksReceiver) AfterProposalVote(ctx sdk.Context, proposalID uint64, voterAddr sdk.AccAddress) { +func (h *MockGovHooksReceiver) AfterProposalVote(ctx sdk.Context, proposalID uint64, voterAddr sdk.AccAddress) error { h.AfterProposalVoteValid = true + return nil } -func (h *MockGovHooksReceiver) AfterProposalFailedMinDeposit(ctx sdk.Context, proposalID uint64) { +func (h *MockGovHooksReceiver) AfterProposalFailedMinDeposit(ctx sdk.Context, proposalID uint64) error { h.AfterProposalFailedMinDepositValid = true + return nil } -func (h *MockGovHooksReceiver) AfterProposalVotingPeriodEnded(ctx sdk.Context, proposalID uint64) { +func (h *MockGovHooksReceiver) AfterProposalVotingPeriodEnded(ctx sdk.Context, proposalID uint64) error { h.AfterProposalVotingPeriodEndedValid = true + return nil } func TestHooks(t *testing.T) { diff --git a/x/gov/keeper/proposal.go b/x/gov/keeper/proposal.go index 09e77fa69cd9..bfa3803500be 100644 --- a/x/gov/keeper/proposal.go +++ b/x/gov/keeper/proposal.go @@ -95,7 +95,10 @@ func (keeper Keeper) SubmitProposal(ctx sdk.Context, messages []sdk.Msg, metadat keeper.SetProposalID(ctx, proposalID+1) // called right after a proposal is submitted - keeper.Hooks().AfterProposalSubmission(ctx, proposalID) + err = keeper.Hooks().AfterProposalSubmission(ctx, proposalID) + if err != nil { + return v1.Proposal{}, err + } ctx.EventManager().EmitEvent( sdk.NewEvent( diff --git a/x/gov/keeper/vote.go b/x/gov/keeper/vote.go index 2f24c37d2e93..ec20dd4155a3 100644 --- a/x/gov/keeper/vote.go +++ b/x/gov/keeper/vote.go @@ -32,7 +32,10 @@ func (keeper Keeper) AddVote(ctx sdk.Context, proposalID uint64, voterAddr sdk.A keeper.SetVote(ctx, vote) // called after a vote on a proposal is cast - keeper.Hooks().AfterProposalVote(ctx, proposalID, voterAddr) + err = keeper.Hooks().AfterProposalVote(ctx, proposalID, voterAddr) + if err != nil { + return err + } ctx.EventManager().EmitEvent( sdk.NewEvent( diff --git a/x/gov/types/expected_keepers.go b/x/gov/types/expected_keepers.go index 70f0e5fbe994..0a0da4e9e1c0 100644 --- a/x/gov/types/expected_keepers.go +++ b/x/gov/types/expected_keepers.go @@ -56,11 +56,11 @@ type BankKeeper interface { // GovHooks event hooks for governance proposal object (noalias) type GovHooks interface { - AfterProposalSubmission(ctx sdk.Context, proposalID uint64) // Must be called after proposal is submitted - AfterProposalDeposit(ctx sdk.Context, proposalID uint64, depositorAddr sdk.AccAddress) // Must be called after a deposit is made - AfterProposalVote(ctx sdk.Context, proposalID uint64, voterAddr sdk.AccAddress) // Must be called after a vote on a proposal is cast - AfterProposalFailedMinDeposit(ctx sdk.Context, proposalID uint64) // Must be called when proposal fails to reach min deposit - AfterProposalVotingPeriodEnded(ctx sdk.Context, proposalID uint64) // Must be called when proposal's finishes it's voting period + AfterProposalSubmission(ctx sdk.Context, proposalID uint64) error // Must be called after proposal is submitted + AfterProposalDeposit(ctx sdk.Context, proposalID uint64, depositorAddr sdk.AccAddress) error // Must be called after a deposit is made + AfterProposalVote(ctx sdk.Context, proposalID uint64, voterAddr sdk.AccAddress) error // Must be called after a vote on a proposal is cast + AfterProposalFailedMinDeposit(ctx sdk.Context, proposalID uint64) error // Must be called when proposal fails to reach min deposit + AfterProposalVotingPeriodEnded(ctx sdk.Context, proposalID uint64) error // Must be called when proposal's finishes it's voting period } type GovHooksWrapper struct{ GovHooks } diff --git a/x/gov/types/hooks.go b/x/gov/types/hooks.go index 0a361687d9d2..f4c3d0ff4ae3 100644 --- a/x/gov/types/hooks.go +++ b/x/gov/types/hooks.go @@ -13,32 +13,79 @@ func NewMultiGovHooks(hooks ...GovHooks) MultiGovHooks { return hooks } -func (h MultiGovHooks) AfterProposalSubmission(ctx sdk.Context, proposalID uint64) { +func (h MultiGovHooks) AfterProposalSubmission(ctx sdk.Context, proposalID uint64) error { + var errs error for i := range h { - h[i].AfterProposalSubmission(ctx, proposalID) + errs = JoinErrors(errs, h[i].AfterProposalSubmission(ctx, proposalID)) } + return errs } -func (h MultiGovHooks) AfterProposalDeposit(ctx sdk.Context, proposalID uint64, depositorAddr sdk.AccAddress) { +func (h MultiGovHooks) AfterProposalDeposit(ctx sdk.Context, proposalID uint64, depositorAddr sdk.AccAddress) error { + var errs error for i := range h { - h[i].AfterProposalDeposit(ctx, proposalID, depositorAddr) + errs = JoinErrors(errs, h[i].AfterProposalDeposit(ctx, proposalID, depositorAddr)) } + return errs } -func (h MultiGovHooks) AfterProposalVote(ctx sdk.Context, proposalID uint64, voterAddr sdk.AccAddress) { +func (h MultiGovHooks) AfterProposalVote(ctx sdk.Context, proposalID uint64, voterAddr sdk.AccAddress) error { + var errs error for i := range h { - h[i].AfterProposalVote(ctx, proposalID, voterAddr) + errs = JoinErrors(errs, h[i].AfterProposalVote(ctx, proposalID, voterAddr)) } + return errs } -func (h MultiGovHooks) AfterProposalFailedMinDeposit(ctx sdk.Context, proposalID uint64) { +func (h MultiGovHooks) AfterProposalFailedMinDeposit(ctx sdk.Context, proposalID uint64) error { + var errs error for i := range h { - h[i].AfterProposalFailedMinDeposit(ctx, proposalID) + errs = JoinErrors(errs, h[i].AfterProposalFailedMinDeposit(ctx, proposalID)) } + return errs } -func (h MultiGovHooks) AfterProposalVotingPeriodEnded(ctx sdk.Context, proposalID uint64) { +func (h MultiGovHooks) AfterProposalVotingPeriodEnded(ctx sdk.Context, proposalID uint64) error { + var errs error for i := range h { - h[i].AfterProposalVotingPeriodEnded(ctx, proposalID) + errs = JoinErrors(errs, h[i].AfterProposalVotingPeriodEnded(ctx, proposalID)) } + return errs +} + +// implementation of errors.Join() in Go 1.20, until we upgrade to that version +func JoinErrors(errs ...error) error { + n := 0 + for _, err := range errs { + if err != nil { + n++ + } + } + if n == 0 { + return nil + } + e := &joinError{ + errs: make([]error, 0, n), + } + for _, err := range errs { + if err != nil { + e.errs = append(e.errs, err) + } + } + return e +} + +type joinError struct { + errs []error +} + +func (e *joinError) Error() string { + var b []byte + for i, err := range e.errs { + if i > 0 { + b = append(b, '\n') + } + b = append(b, err.Error()...) + } + return string(b) }