feat!: add error handling to staking hooks (#9571)

<!--
The default pull request template is for types feat, fix, or refactor.
For other templates, add one of the following parameters to the url:
- template=docs.md
- template=other.md
-->

## Description

- Adds error handling for staking hooks

<!-- Add a description of the changes that this PR introduces and the files that
are the most critical to review. -->

---

### Author Checklist

*All items are required. Please add a note to the item if the item is not applicable and
please add links to any relevant follow up issues.*

I have...

- [x] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title
- [x] added `!` to the type prefix if API or client breaking change
- [x] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting))
- [ ] provided a link to the relevant issue or specification
- [ ] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules)
- [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing)
- [ ] added a changelog entry to `CHANGELOG.md`
- [ ] included comments for [documenting Go code](https://blog.golang.org/godoc)
- [x] updated the relevant documentation or specification
- [ ] reviewed "Files changed" and left comments if necessary
- [ ] confirmed all CI checks have passed

### Reviewers Checklist

*All items are required. Please add a note if the item is not applicable and please add
your handle next to the items reviewed if you only reviewed selected items.*

I have...

- [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title
- [ ] confirmed `!` in the type prefix if API or client breaking change
- [ ] confirmed all author checklist items have been addressed 
- [ ] reviewed state machine logic
- [ ] reviewed API design and naming
- [ ] reviewed documentation is accurate
- [ ] reviewed tests and test coverage
- [ ] manually tested (if applicable)
This commit is contained in:
Federico Kunze Küllmer
2021-06-30 10:13:07 +00:00
committed by GitHub
parent aef416f91e
commit f5b11bc328
12 changed files with 203 additions and 106 deletions
+10 -5
View File
@@ -2,7 +2,6 @@ package staking
import (
"fmt"
"log"
abci "github.com/tendermint/tendermint/abci/types"
tmtypes "github.com/tendermint/tendermint/types"
@@ -44,7 +43,9 @@ func InitGenesis(
// Call the creation hook if not exported
if !data.Exported {
keeper.AfterValidatorCreated(ctx, validator.GetOperator())
if err := keeper.AfterValidatorCreated(ctx, validator.GetOperator()); err != nil {
panic(err)
}
}
// update timeslice if necessary
@@ -70,13 +71,17 @@ func InitGenesis(
// Call the before-creation hook if not exported
if !data.Exported {
keeper.BeforeDelegationCreated(ctx, delegatorAddress, delegation.GetValidatorAddr())
if err := keeper.BeforeDelegationCreated(ctx, delegatorAddress, delegation.GetValidatorAddr()); err != nil {
panic(err)
}
}
keeper.SetDelegation(ctx, delegation)
// Call the after-modification hook if not exported
if !data.Exported {
keeper.AfterDelegationModified(ctx, delegatorAddress, delegation.GetValidatorAddr())
if err := keeper.AfterDelegationModified(ctx, delegatorAddress, delegation.GetValidatorAddr()); err != nil {
panic(err)
}
}
}
@@ -149,7 +154,7 @@ func InitGenesis(
var err error
res, err = keeper.ApplyAndReturnValidatorSetUpdates(ctx)
if err != nil {
log.Fatal(err)
panic(err)
}
}
+24 -8
View File
@@ -101,15 +101,19 @@ func (k Keeper) SetDelegation(ctx sdk.Context, delegation types.Delegation) {
}
// remove a delegation
func (k Keeper) RemoveDelegation(ctx sdk.Context, delegation types.Delegation) {
func (k Keeper) RemoveDelegation(ctx sdk.Context, delegation types.Delegation) error {
delegatorAddress, err := sdk.AccAddressFromBech32(delegation.DelegatorAddress)
if err != nil {
panic(err)
}
// TODO: Consider calling hooks outside of the store wrapper functions, it's unobvious.
k.BeforeDelegationRemoved(ctx, delegatorAddress, delegation.GetValidatorAddr())
if err := k.BeforeDelegationRemoved(ctx, delegatorAddress, delegation.GetValidatorAddr()); err != nil {
return err
}
store := ctx.KVStore(k.storeKey)
store.Delete(types.GetDelegationKey(delegatorAddress, delegation.GetValidatorAddr()))
return nil
}
// return a given amount of all the delegator unbonding-delegations
@@ -563,9 +567,13 @@ func (k Keeper) Delegate(
// call the appropriate hook if present
if found {
k.BeforeDelegationSharesModified(ctx, delAddr, validator.GetOperator())
err = k.BeforeDelegationSharesModified(ctx, delAddr, validator.GetOperator())
} else {
k.BeforeDelegationCreated(ctx, delAddr, validator.GetOperator())
err = k.BeforeDelegationCreated(ctx, delAddr, validator.GetOperator())
}
if err != nil {
return sdk.ZeroDec(), err
}
delegatorAddress, err := sdk.AccAddressFromBech32(delegation.DelegatorAddress)
@@ -621,7 +629,9 @@ func (k Keeper) Delegate(
k.SetDelegation(ctx, delegation)
// Call the after-modification hook
k.AfterDelegationModified(ctx, delegatorAddress, delegation.GetValidatorAddr())
if err := k.AfterDelegationModified(ctx, delegatorAddress, delegation.GetValidatorAddr()); err != nil {
return newShares, err
}
return newShares, nil
}
@@ -637,7 +647,9 @@ func (k Keeper) Unbond(
}
// call the before-delegation-modified hook
k.BeforeDelegationSharesModified(ctx, delAddr, valAddr)
if err := k.BeforeDelegationSharesModified(ctx, delAddr, valAddr); err != nil {
return amount, err
}
// ensure that we have enough shares to remove
if delegation.Shares.LT(shares) {
@@ -670,11 +682,15 @@ func (k Keeper) Unbond(
// remove the delegation
if delegation.Shares.IsZero() {
k.RemoveDelegation(ctx, delegation)
err = k.RemoveDelegation(ctx, delegation)
} else {
k.SetDelegation(ctx, delegation)
// call the after delegation modification hook
k.AfterDelegationModified(ctx, delegatorAddress, delegation.GetValidatorAddr())
err = k.AfterDelegationModified(ctx, delegatorAddress, delegation.GetValidatorAddr())
}
if err != nil {
return amount, err
}
// remove the shares and coins from the validator
+29 -19
View File
@@ -9,71 +9,81 @@ import (
var _ types.StakingHooks = Keeper{}
// AfterValidatorCreated - call hook if registered
func (k Keeper) AfterValidatorCreated(ctx sdk.Context, valAddr sdk.ValAddress) {
func (k Keeper) AfterValidatorCreated(ctx sdk.Context, valAddr sdk.ValAddress) error {
if k.hooks != nil {
k.hooks.AfterValidatorCreated(ctx, valAddr)
return k.hooks.AfterValidatorCreated(ctx, valAddr)
}
return nil
}
// BeforeValidatorModified - call hook if registered
func (k Keeper) BeforeValidatorModified(ctx sdk.Context, valAddr sdk.ValAddress) {
func (k Keeper) BeforeValidatorModified(ctx sdk.Context, valAddr sdk.ValAddress) error {
if k.hooks != nil {
k.hooks.BeforeValidatorModified(ctx, valAddr)
return k.hooks.BeforeValidatorModified(ctx, valAddr)
}
return nil
}
// AfterValidatorRemoved - call hook if registered
func (k Keeper) AfterValidatorRemoved(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) {
func (k Keeper) AfterValidatorRemoved(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) error {
if k.hooks != nil {
k.hooks.AfterValidatorRemoved(ctx, consAddr, valAddr)
return k.hooks.AfterValidatorRemoved(ctx, consAddr, valAddr)
}
return nil
}
// AfterValidatorBonded - call hook if registered
func (k Keeper) AfterValidatorBonded(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) {
func (k Keeper) AfterValidatorBonded(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) error {
if k.hooks != nil {
k.hooks.AfterValidatorBonded(ctx, consAddr, valAddr)
return k.hooks.AfterValidatorBonded(ctx, consAddr, valAddr)
}
return nil
}
// AfterValidatorBeginUnbonding - call hook if registered
func (k Keeper) AfterValidatorBeginUnbonding(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) {
func (k Keeper) AfterValidatorBeginUnbonding(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) error {
if k.hooks != nil {
k.hooks.AfterValidatorBeginUnbonding(ctx, consAddr, valAddr)
return k.hooks.AfterValidatorBeginUnbonding(ctx, consAddr, valAddr)
}
return nil
}
// BeforeDelegationCreated - call hook if registered
func (k Keeper) BeforeDelegationCreated(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) {
func (k Keeper) BeforeDelegationCreated(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) error {
if k.hooks != nil {
k.hooks.BeforeDelegationCreated(ctx, delAddr, valAddr)
return k.hooks.BeforeDelegationCreated(ctx, delAddr, valAddr)
}
return nil
}
// BeforeDelegationSharesModified - call hook if registered
func (k Keeper) BeforeDelegationSharesModified(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) {
func (k Keeper) BeforeDelegationSharesModified(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) error {
if k.hooks != nil {
k.hooks.BeforeDelegationSharesModified(ctx, delAddr, valAddr)
return k.hooks.BeforeDelegationSharesModified(ctx, delAddr, valAddr)
}
return nil
}
// BeforeDelegationRemoved - call hook if registered
func (k Keeper) BeforeDelegationRemoved(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) {
func (k Keeper) BeforeDelegationRemoved(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) error {
if k.hooks != nil {
k.hooks.BeforeDelegationRemoved(ctx, delAddr, valAddr)
}
return nil
}
// AfterDelegationModified - call hook if registered
func (k Keeper) AfterDelegationModified(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) {
func (k Keeper) AfterDelegationModified(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) error {
if k.hooks != nil {
k.hooks.AfterDelegationModified(ctx, delAddr, valAddr)
return k.hooks.AfterDelegationModified(ctx, delAddr, valAddr)
}
return nil
}
// BeforeValidatorSlashed - call hook if registered
func (k Keeper) BeforeValidatorSlashed(ctx sdk.Context, valAddr sdk.ValAddress, fraction sdk.Dec) {
func (k Keeper) BeforeValidatorSlashed(ctx sdk.Context, valAddr sdk.ValAddress, fraction sdk.Dec) error {
if k.hooks != nil {
k.hooks.BeforeValidatorSlashed(ctx, valAddr, fraction)
return k.hooks.BeforeValidatorSlashed(ctx, valAddr, fraction)
}
return nil
}
+6 -2
View File
@@ -96,7 +96,9 @@ func (k msgServer) CreateValidator(goCtx context.Context, msg *types.MsgCreateVa
k.SetNewValidatorByPowerIndex(ctx, validator)
// call the after-creation hook
k.AfterValidatorCreated(ctx, validator.GetOperator())
if err := k.AfterValidatorCreated(ctx, validator.GetOperator()); err != nil {
return nil, err
}
// move coins from the msg.Address account to a (self-delegation) delegator account
// the validator account and global shares are updated within here
@@ -150,7 +152,9 @@ func (k msgServer) EditValidator(goCtx context.Context, msg *types.MsgEditValida
}
// call the before-modification hook since we're about to update the commission
k.BeforeValidatorModified(ctx, valAddr)
if err := k.BeforeValidatorModified(ctx, valAddr); err != nil {
return nil, err
}
validator.Commission = commission
}
+8 -8
View File
@@ -9,19 +9,19 @@ occurred within staking. These events can be registered to execute either
right `Before` or `After` the staking event (as per the hook name). The
following hooks can registered with staking:
- `AfterValidatorCreated(Context, ValAddress)`
- `AfterValidatorCreated(Context, ValAddress) error`
- called when a validator is created
- `BeforeValidatorModified(Context, ValAddress)`
- `BeforeValidatorModified(Context, ValAddress) error`
- called when a validator's state is changed
- `AfterValidatorRemoved(Context, ConsAddress, ValAddress)`
- `AfterValidatorRemoved(Context, ConsAddress, ValAddress) error`
- called when a validator is deleted
- `AfterValidatorBonded(Context, ConsAddress, ValAddress)`
- `AfterValidatorBonded(Context, ConsAddress, ValAddress) error`
- called when a validator is bonded
- `AfterValidatorBeginUnbonding(Context, ConsAddress, ValAddress)`
- `AfterValidatorBeginUnbonding(Context, ConsAddress, ValAddress) error`
- called when a validator begins unbonding
- `BeforeDelegationCreated(Context, AccAddress, ValAddress)`
- `BeforeDelegationCreated(Context, AccAddress, ValAddress) error`
- called when a delegation is created
- `BeforeDelegationSharesModified(Context, AccAddress, ValAddress)`
- `BeforeDelegationSharesModified(Context, AccAddress, ValAddress) error`
- called when a delegation's shares are modified
- `BeforeDelegationRemoved(Context, AccAddress, ValAddress)`
- `BeforeDelegationRemoved(Context, AccAddress, ValAddress) error`
- called when a delegation is removed
+10 -10
View File
@@ -89,16 +89,16 @@ type DelegationSet interface {
// StakingHooks event hooks for staking validator object (noalias)
type StakingHooks interface {
AfterValidatorCreated(ctx sdk.Context, valAddr sdk.ValAddress) // Must be called when a validator is created
BeforeValidatorModified(ctx sdk.Context, valAddr sdk.ValAddress) // Must be called when a validator's state changes
AfterValidatorRemoved(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) // Must be called when a validator is deleted
AfterValidatorCreated(ctx sdk.Context, valAddr sdk.ValAddress) error // Must be called when a validator is created
BeforeValidatorModified(ctx sdk.Context, valAddr sdk.ValAddress) error // Must be called when a validator's state changes
AfterValidatorRemoved(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) error // Must be called when a validator is deleted
AfterValidatorBonded(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) // Must be called when a validator is bonded
AfterValidatorBeginUnbonding(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) // Must be called when a validator begins unbonding
AfterValidatorBonded(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) error // Must be called when a validator is bonded
AfterValidatorBeginUnbonding(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) error // Must be called when a validator begins unbonding
BeforeDelegationCreated(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) // Must be called when a delegation is created
BeforeDelegationSharesModified(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) // Must be called when a delegation's shares are modified
BeforeDelegationRemoved(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) // Must be called when a delegation is removed
AfterDelegationModified(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress)
BeforeValidatorSlashed(ctx sdk.Context, valAddr sdk.ValAddress, fraction sdk.Dec)
BeforeDelegationCreated(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) error // Must be called when a delegation is created
BeforeDelegationSharesModified(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) error // Must be called when a delegation's shares are modified
BeforeDelegationRemoved(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) error // Must be called when a delegation is removed
AfterDelegationModified(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) error
BeforeValidatorSlashed(ctx sdk.Context, valAddr sdk.ValAddress, fraction sdk.Dec) error
}
+53 -20
View File
@@ -5,59 +5,92 @@ import (
)
// combine multiple staking hooks, all hook functions are run in array sequence
var _ StakingHooks = &MultiStakingHooks{}
type MultiStakingHooks []StakingHooks
func NewMultiStakingHooks(hooks ...StakingHooks) MultiStakingHooks {
return hooks
}
func (h MultiStakingHooks) AfterValidatorCreated(ctx sdk.Context, valAddr sdk.ValAddress) {
func (h MultiStakingHooks) AfterValidatorCreated(ctx sdk.Context, valAddr sdk.ValAddress) error {
for i := range h {
h[i].AfterValidatorCreated(ctx, valAddr)
if err := h[i].AfterValidatorCreated(ctx, valAddr); err != nil {
return err
}
}
return nil
}
func (h MultiStakingHooks) BeforeValidatorModified(ctx sdk.Context, valAddr sdk.ValAddress) {
func (h MultiStakingHooks) BeforeValidatorModified(ctx sdk.Context, valAddr sdk.ValAddress) error {
for i := range h {
h[i].BeforeValidatorModified(ctx, valAddr)
if err := h[i].BeforeValidatorModified(ctx, valAddr); err != nil {
return err
}
}
return nil
}
func (h MultiStakingHooks) AfterValidatorRemoved(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) {
func (h MultiStakingHooks) AfterValidatorRemoved(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) error {
for i := range h {
h[i].AfterValidatorRemoved(ctx, consAddr, valAddr)
if err := h[i].AfterValidatorRemoved(ctx, consAddr, valAddr); err != nil {
return err
}
}
return nil
}
func (h MultiStakingHooks) AfterValidatorBonded(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) {
func (h MultiStakingHooks) AfterValidatorBonded(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) error {
for i := range h {
h[i].AfterValidatorBonded(ctx, consAddr, valAddr)
if err := h[i].AfterValidatorBonded(ctx, consAddr, valAddr); err != nil {
return err
}
}
return nil
}
func (h MultiStakingHooks) AfterValidatorBeginUnbonding(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) {
func (h MultiStakingHooks) AfterValidatorBeginUnbonding(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) error {
for i := range h {
h[i].AfterValidatorBeginUnbonding(ctx, consAddr, valAddr)
if err := h[i].AfterValidatorBeginUnbonding(ctx, consAddr, valAddr); err != nil {
return err
}
}
return nil
}
func (h MultiStakingHooks) BeforeDelegationCreated(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) {
func (h MultiStakingHooks) BeforeDelegationCreated(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) error {
for i := range h {
h[i].BeforeDelegationCreated(ctx, delAddr, valAddr)
if err := h[i].BeforeDelegationCreated(ctx, delAddr, valAddr); err != nil {
return err
}
}
return nil
}
func (h MultiStakingHooks) BeforeDelegationSharesModified(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) {
func (h MultiStakingHooks) BeforeDelegationSharesModified(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) error {
for i := range h {
h[i].BeforeDelegationSharesModified(ctx, delAddr, valAddr)
if err := h[i].BeforeDelegationSharesModified(ctx, delAddr, valAddr); err != nil {
return err
}
}
return nil
}
func (h MultiStakingHooks) BeforeDelegationRemoved(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) {
func (h MultiStakingHooks) BeforeDelegationRemoved(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) error {
for i := range h {
h[i].BeforeDelegationRemoved(ctx, delAddr, valAddr)
if err := h[i].BeforeDelegationRemoved(ctx, delAddr, valAddr); err != nil {
return err
}
}
return nil
}
func (h MultiStakingHooks) AfterDelegationModified(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) {
func (h MultiStakingHooks) AfterDelegationModified(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) error {
for i := range h {
h[i].AfterDelegationModified(ctx, delAddr, valAddr)
if err := h[i].AfterDelegationModified(ctx, delAddr, valAddr); err != nil {
return err
}
}
return nil
}
func (h MultiStakingHooks) BeforeValidatorSlashed(ctx sdk.Context, valAddr sdk.ValAddress, fraction sdk.Dec) {
func (h MultiStakingHooks) BeforeValidatorSlashed(ctx sdk.Context, valAddr sdk.ValAddress, fraction sdk.Dec) error {
for i := range h {
h[i].BeforeValidatorSlashed(ctx, valAddr, fraction)
if err := h[i].BeforeValidatorSlashed(ctx, valAddr, fraction); err != nil {
return err
}
}
return nil
}