diff --git a/cmd/gaia/app/app.go b/cmd/gaia/app/app.go index 3213029e04..5b1dbd6722 100644 --- a/cmd/gaia/app/app.go +++ b/cmd/gaia/app/app.go @@ -96,17 +96,19 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, baseAppOptio auth.ProtoBaseAccount, // prototype ) - // add handlers and hooks + // add handlers app.bankKeeper = bank.NewBaseKeeper(app.accountMapper) app.ibcMapper = ibc.NewMapper(app.cdc, app.keyIBC, app.RegisterCodespace(ibc.DefaultCodespace)) app.paramsKeeper = params.NewKeeper(app.cdc, app.keyParams) app.stakeKeeper = stake.NewKeeper(app.cdc, app.keyStake, app.tkeyStake, app.bankKeeper, app.RegisterCodespace(stake.DefaultCodespace)) app.distrKeeper = distr.NewKeeper(app.cdc, app.keyDistr, app.tkeyStake, app.bankKeeper, app.RegisterCodespace(stake.DefaultCodespace)) app.slashingKeeper = slashing.NewKeeper(app.cdc, app.keySlashing, app.stakeKeeper, app.paramsKeeper.Getter(), app.RegisterCodespace(slashing.DefaultCodespace)) - app.stakeKeeper = app.stakeKeeper.WithValidatorHooks(app.slashingKeeper.ValidatorHooks()) app.govKeeper = gov.NewKeeper(app.cdc, app.keyGov, app.paramsKeeper.Setter(), app.bankKeeper, app.stakeKeeper, app.RegisterCodespace(gov.DefaultCodespace)) app.feeCollectionKeeper = auth.NewFeeCollectionKeeper(app.cdc, app.keyFeeCollection) + // register the staking hooks + app.stakeKeeper = app.stakeKeeper.WithValidatorHooks(NewHooks(app.distrKeeper.Hooks(), app.slashingKeeper.Hooks())) + // register message routes app.Router(). AddRoute("bank", bank.NewHandler(app.bankKeeper)). @@ -249,10 +251,14 @@ func (app *GaiaApp) ExportAppStateAndValidators() (appState json.RawMessage, val //______________________________________________________________________________________________ -// Combine Staking Hooks +// Combined Staking Hooks type Hooks struct { dh distr.Hooks - sh slashing.Hooks + sh slashing.ValidatorHooks +} + +func NewHooks(dh distr.Hooks, sh slashing.ValidatorHooks) Hooks { + return Hooks{dh, sh} } var _ sdk.StakingHooks = Hooks{} diff --git a/x/distribution/hooks.go b/x/distribution/hooks.go index 3a4d8fbd2d..97be985f29 100644 --- a/x/distribution/hooks.go +++ b/x/distribution/hooks.go @@ -5,33 +5,6 @@ import ( "github.com/cosmos/cosmos-sdk/x/distribution/types" ) -/* -## Create or modify delegation distribution - - - triggered-by: `stake.TxDelegate`, `stake.TxBeginRedelegate`, `stake.TxBeginUnbonding` - -The pool of a new delegator bond will be 0 for the height at which the bond was -added, or the withdrawal has taken place. This is achieved by setting -`DelegatorDistInfo.WithdrawalHeight` to the height of the triggering transaction. - -## Commission rate change - - - triggered-by: `stake.TxEditValidator` - -If a validator changes its commission rate, all commission on fees must be -simultaneously withdrawn using the transaction `TxWithdrawValidator`. -Additionally the change and associated height must be recorded in a -`ValidatorUpdate` state record. - -## Change in Validator State - - - triggered-by: `stake.Slash`, `stake.UpdateValidator` - -Whenever a validator is slashed or enters/leaves the validator group all of the -validator entitled reward tokens must be simultaneously withdrawn from -`Global.Pool` and added to `ValidatorDistInfo.Pool`. -*/ - // Create a new validator distribution record func (k Keeper) onValidatorCreated(ctx sdk.Context, addr sdk.ValAddress) { @@ -91,8 +64,10 @@ type Hooks struct { k Keeper } +// New Validator Hooks +func (k Keeper) ValidatorHooks() Hooks { return Hooks{k} } + // nolint -func (k Keeper) ValidatorHooks() sdk.ValidatorHooks { return ValidatorHooks{k} } func (h Hooks) OnValidatorCreated(ctx sdk.Context, addr sdk.VlAddress) { v.k.onValidatorCreated(ctx, address) } diff --git a/x/slashing/hooks.go b/x/slashing/hooks.go index f5f3cc48c3..8d519c7945 100644 --- a/x/slashing/hooks.go +++ b/x/slashing/hooks.go @@ -22,25 +22,24 @@ func (k Keeper) onValidatorBeginUnbonding(ctx sdk.Context, address sdk.ConsAddre k.addOrUpdateValidatorSlashingPeriod(ctx, slashingPeriod) } -// Wrapper struct for sdk.ValidatorHooks -type ValidatorHooks struct { +//_________________________________________________________________________________________ + +// Wrapper struct +type Hooks struct { k Keeper } -// Assert implementation -var _ sdk.ValidatorHooks = ValidatorHooks{} - -// Return a sdk.ValidatorHooks interface over the wrapper struct -func (k Keeper) ValidatorHooks() sdk.ValidatorHooks { - return ValidatorHooks{k} +// Return the wrapper struct +func (k Keeper) Hooks() Hooks { + return Hooks{k} } // Implements sdk.ValidatorHooks -func (v ValidatorHooks) OnValidatorBonded(ctx sdk.Context, address sdk.ConsAddress) { - v.k.onValidatorBonded(ctx, address) +func (h Hooks) OnValidatorBonded(ctx sdk.Context, address sdk.ConsAddress) { + h.k.onValidatorBonded(ctx, address) } // Implements sdk.ValidatorHooks -func (v ValidatorHooks) OnValidatorBeginUnbonding(ctx sdk.Context, address sdk.ConsAddress) { - v.k.onValidatorBeginUnbonding(ctx, address) +func (h Hooks) OnValidatorBeginUnbonding(ctx sdk.Context, address sdk.ConsAddress) { + h.k.onValidatorBeginUnbonding(ctx, address) } diff --git a/x/stake/keeper/validator.go b/x/stake/keeper/validator.go index bef723c4da..2e5191b2dd 100644 --- a/x/stake/keeper/validator.go +++ b/x/stake/keeper/validator.go @@ -669,6 +669,11 @@ func (k Keeper) bondValidator(ctx sdk.Context, validator types.Validator) types. // remove the validator record and associated indexes func (k Keeper) RemoveValidator(ctx sdk.Context, address sdk.ValAddress) { + // call the hook if present + if k.validatorHooks != nil { + k.validatorHooks.OnValidatorRemoved(ctx, validator.OperatorAddr) + } + // first retrieve the old validator record validator, found := k.GetValidator(ctx, address) if !found { @@ -728,3 +733,38 @@ func ensureValidatorFound(found bool, ownerAddr []byte) { panic(fmt.Sprintf("validator record not found for address: %X\n", ownerAddr)) } } + +//__________________________________________________________________________ + +// get a single validator +func (k Keeper) UpdateValidatorCommission(ctx sdk.Context, addr sdk.ValAddress, newCommission sdk.Dec) sdk.Error { + store := ctx.KVStore(k.storeKey) + + // call the hook if present + if k.validatorHooks != nil { + k.validatorHooks.OnValidatorCommissionChange(ctx, validator.OperatorAddr) + } + + validator, found := k.GetValidator(addr) + + // check for errors + switch { + case !found: + return types.ErrNoValidatorFound(k.Codespace()) + case newCommission.LT(sdk.ZeroDec()): + return types.ErrCommissionNegative(k.Codespace()) + case newCommission.GT(sdk.OnedDec()): + return types.ErrCommissionHuge(k.Codespace()) + case newCommission.GT(validator.CommissionMax): + return types.ErrCommissionBeyondMax(k.Codespace()) + //case rateChange(Commission) > CommissionMaxChange: // XXX XXX XXX TODO implementation + //return types.ErrCommissionPastRate(k.Codespace()) + } + + // TODO adjust all the commission terms appropriately + + validator.Commission = newCommission + + k.SetValidator(addr, validator) + return nil +} diff --git a/x/stake/types/errors.go b/x/stake/types/errors.go index 366012bbfb..541ad3d7b9 100644 --- a/x/stake/types/errors.go +++ b/x/stake/types/errors.go @@ -65,6 +65,14 @@ func ErrCommissionHuge(codespace sdk.CodespaceType) sdk.Error { return sdk.NewError(codespace, CodeInvalidValidator, "commission cannot be more than 100%") } +func ErrCommissionBeyondMax(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidValidator, "commission cannot be more than preset commission maximum") +} + +func ErrCommissionPastRate(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidValidator, "commission change is greater than the commission rate, please wait before changing your commission more") +} + func ErrNilDelegatorAddr(codespace sdk.CodespaceType) sdk.Error { return sdk.NewError(codespace, CodeInvalidInput, "delegator address is nil") }