diff --git a/tests/integration/staking/keeper/delegation_test.go b/tests/integration/staking/keeper/delegation_test.go index 5626116be2d7..b857d2da4725 100644 --- a/tests/integration/staking/keeper/delegation_test.go +++ b/tests/integration/staking/keeper/delegation_test.go @@ -103,7 +103,7 @@ func TestUnbondingDelegationsMaxEntries(t *testing.T) { // mature unbonding delegations ctx = ctx.WithBlockTime(completionTime) - _, err = f.stakingKeeper.CompleteUnbonding(ctx, addrDel, addrVal) + _, _, err = f.stakingKeeper.CompleteUnbonding(ctx, addrDel, addrVal) assert.NilError(t, err) newBonded = f.bankKeeper.GetBalance(ctx, f.stakingKeeper.GetBondedPool(ctx).GetAddress(), bondDenom).Amount diff --git a/tests/integration/staking/keeper/unbonding_test.go b/tests/integration/staking/keeper/unbonding_test.go index 6282853536f1..4d44b43f9e26 100644 --- a/tests/integration/staking/keeper/unbonding_test.go +++ b/tests/integration/staking/keeper/unbonding_test.go @@ -403,7 +403,7 @@ func TestUnbondingDelegationOnHold1(t *testing.T) { // PROVIDER CHAIN'S UNBONDING PERIOD ENDS - STOPPED UNBONDING CAN NOW COMPLETE f.sdkCtx = f.sdkCtx.WithBlockTime(completionTime) - _, err = f.stakingKeeper.CompleteUnbonding(f.sdkCtx, addrDels[0], addrVals[0]) + _, _, err = f.stakingKeeper.CompleteUnbonding(f.sdkCtx, addrDels[0], addrVals[0]) assert.NilError(t, err) // Check that the unbonding was finally completed @@ -430,7 +430,7 @@ func TestUnbondingDelegationOnHold2(t *testing.T) { // PROVIDER CHAIN'S UNBONDING PERIOD ENDS - BUT UNBONDING CANNOT COMPLETE f.sdkCtx = f.sdkCtx.WithBlockTime(completionTime) - _, err := f.stakingKeeper.CompleteUnbonding(f.sdkCtx, addrDels[0], addrVals[0]) + _, _, err := f.stakingKeeper.CompleteUnbonding(f.sdkCtx, addrDels[0], addrVals[0]) assert.NilError(t, err) bondedAmt3 := f.bankKeeper.GetBalance(f.sdkCtx, f.stakingKeeper.GetBondedPool(f.sdkCtx).GetAddress(), bondDenom).Amount diff --git a/x/staking/keeper/abci.go b/x/staking/keeper/abci.go index 8131fa6dd16f..dd3bc430a193 100644 --- a/x/staking/keeper/abci.go +++ b/x/staking/keeper/abci.go @@ -18,6 +18,15 @@ func (k *Keeper) BeginBlocker(ctx context.Context) error { // EndBlocker called at every block, update validator set func (k *Keeper) EndBlocker(ctx context.Context) ([]abci.ValidatorUpdate, error) { + valUpdates, _, err := k.EndBlockerWithUnbondedEntries(ctx) + if err != nil { + return nil, err + } + + return valUpdates, nil +} + +func (k Keeper) EndBlockerWithUnbondedEntries(ctx context.Context) ([]abci.ValidatorUpdate, []types.UnbondedEntry, error) { defer telemetry.ModuleMeasureSince(types.ModuleName, telemetry.Now(), telemetry.MetricKeyEndBlocker) return k.BlockValidatorUpdates(ctx) } diff --git a/x/staking/keeper/delegation.go b/x/staking/keeper/delegation.go index bc4a1ab95fc9..0acfba02c8d1 100644 --- a/x/staking/keeper/delegation.go +++ b/x/staking/keeper/delegation.go @@ -1239,15 +1239,15 @@ func (k Keeper) Undelegate( // CompleteUnbonding completes the unbonding of all mature entries in the // retrieved unbonding delegation object and returns the total unbonding balance // or an error upon failure. -func (k Keeper) CompleteUnbonding(ctx context.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) (sdk.Coins, error) { +func (k Keeper) CompleteUnbonding(ctx context.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) (sdk.Coins, *types.UnbondedEntry, error) { ubd, err := k.GetUnbondingDelegation(ctx, delAddr, valAddr) if err != nil { - return nil, err + return nil, nil, err } bondDenom, err := k.BondDenom(ctx) if err != nil { - return nil, err + return nil, nil, err } balances := sdk.NewCoins() @@ -1256,7 +1256,13 @@ func (k Keeper) CompleteUnbonding(ctx context.Context, delAddr sdk.AccAddress, v delegatorAddress, err := k.authKeeper.AddressCodec().StringToBytes(ubd.DelegatorAddress) if err != nil { - return nil, err + return nil, nil, err + } + + unbondedEntry := &types.UnbondedEntry{ + DelegatorAddress: delAddr.String(), + ValidatorAddress: valAddr.String(), + Amount: math.NewInt(0), } // loop through all the entries and complete unbonding mature entries @@ -1266,7 +1272,7 @@ func (k Keeper) CompleteUnbonding(ctx context.Context, delAddr sdk.AccAddress, v ubd.RemoveEntry(int64(i)) i-- if err = k.DeleteUnbondingIndex(ctx, entry.UnbondingId); err != nil { - return nil, err + return nil, unbondedEntry, err } // track undelegation only when remaining or truncated shares are non-zero @@ -1275,10 +1281,11 @@ func (k Keeper) CompleteUnbonding(ctx context.Context, delAddr sdk.AccAddress, v if err := k.bankKeeper.UndelegateCoinsFromModuleToAccount( ctx, types.NotBondedPoolName, delegatorAddress, sdk.NewCoins(amt), ); err != nil { - return nil, err + return nil, unbondedEntry, err } balances = balances.Add(amt) + unbondedEntry.Amount = balances.AmountOf(bondDenom) } } } @@ -1291,10 +1298,10 @@ func (k Keeper) CompleteUnbonding(ctx context.Context, delAddr sdk.AccAddress, v } if err != nil { - return nil, err + return nil, unbondedEntry, err } - return balances, nil + return balances, unbondedEntry, nil } // BeginRedelegation begins unbonding / redelegation and creates a redelegation diff --git a/x/staking/keeper/val_state_change.go b/x/staking/keeper/val_state_change.go index 444633983c36..bc5a6099e604 100644 --- a/x/staking/keeper/val_state_change.go +++ b/x/staking/keeper/val_state_change.go @@ -18,7 +18,7 @@ import ( // BlockValidatorUpdates calculates the ValidatorUpdates for the current block // Called in each EndBlock -func (k Keeper) BlockValidatorUpdates(ctx context.Context) ([]abci.ValidatorUpdate, error) { +func (k Keeper) BlockValidatorUpdates(ctx context.Context) ([]abci.ValidatorUpdate, []types.UnbondedEntry, error) { // Calculate validator set changes. // // NOTE: ApplyAndReturnValidatorSetUpdates has to come before @@ -30,37 +30,46 @@ func (k Keeper) BlockValidatorUpdates(ctx context.Context) ([]abci.ValidatorUpda // UnbondAllMatureValidatorQueue). validatorUpdates, err := k.ApplyAndReturnValidatorSetUpdates(ctx) if err != nil { - return nil, err + return nil, nil, err } // unbond all mature validators from the unbonding queue err = k.UnbondAllMatureValidators(ctx) if err != nil { - return nil, err + return nil, nil, err } sdkCtx := sdk.UnwrapSDKContext(ctx) // Remove all mature unbonding delegations from the ubd queue. matureUnbonds, err := k.DequeueAllMatureUBDQueue(ctx, sdkCtx.BlockHeader().Time) if err != nil { - return nil, err + return nil, nil, err } + var unbondedEntries []types.UnbondedEntry + for _, dvPair := range matureUnbonds { addr, err := k.validatorAddressCodec.StringToBytes(dvPair.ValidatorAddress) if err != nil { - return nil, err + return nil, nil, err } delegatorAddress, err := k.authKeeper.AddressCodec().StringToBytes(dvPair.DelegatorAddress) if err != nil { - return nil, err + return nil, nil, err } - balances, err := k.CompleteUnbonding(ctx, delegatorAddress, addr) + balances, unbondedEntry, err := k.CompleteUnbonding(ctx, delegatorAddress, addr) if err != nil { + k.Logger(ctx).Error( + "failed to complete unbonding", + "error", err, + "val_addr", string(addr), + "del_addr", string(delegatorAddress)) continue } + unbondedEntries = append(unbondedEntries, *unbondedEntry) + sdkCtx.EventManager().EmitEvent( sdk.NewEvent( types.EventTypeCompleteUnbonding, @@ -74,21 +83,21 @@ func (k Keeper) BlockValidatorUpdates(ctx context.Context) ([]abci.ValidatorUpda // Remove all mature redelegations from the red queue. matureRedelegations, err := k.DequeueAllMatureRedelegationQueue(ctx, sdkCtx.BlockHeader().Time) if err != nil { - return nil, err + return nil, nil, err } for _, dvvTriplet := range matureRedelegations { valSrcAddr, err := k.validatorAddressCodec.StringToBytes(dvvTriplet.ValidatorSrcAddress) if err != nil { - return nil, err + return nil, nil, err } valDstAddr, err := k.validatorAddressCodec.StringToBytes(dvvTriplet.ValidatorDstAddress) if err != nil { - return nil, err + return nil, nil, err } delegatorAddress, err := k.authKeeper.AddressCodec().StringToBytes(dvvTriplet.DelegatorAddress) if err != nil { - return nil, err + return nil, nil, err } balances, err := k.CompleteRedelegation( @@ -98,6 +107,12 @@ func (k Keeper) BlockValidatorUpdates(ctx context.Context) ([]abci.ValidatorUpda valDstAddr, ) if err != nil { + k.Logger(ctx).Error( + "failed to complete redelegation", + "error", err, + "val_src_addr", string(valSrcAddr), + "val_dst_addr", string(valDstAddr), + "del_addr", string(delegatorAddress)) continue } @@ -112,7 +127,7 @@ func (k Keeper) BlockValidatorUpdates(ctx context.Context) ([]abci.ValidatorUpda ) } - return validatorUpdates, nil + return validatorUpdates, unbondedEntries, nil } // ApplyAndReturnValidatorSetUpdates applies and return accumulated updates to the bonded validator set. Also, diff --git a/x/staking/types/delegation.go b/x/staking/types/delegation.go index 0dddbce8d47d..02b6edb47f20 100644 --- a/x/staking/types/delegation.go +++ b/x/staking/types/delegation.go @@ -384,3 +384,9 @@ func (r RedelegationResponses) String() (out string) { return strings.TrimSpace(out) } + +type UnbondedEntry struct { + ValidatorAddress string + DelegatorAddress string + Amount math.Int +}