feat: include rotate keys logic in abci (#18236)

This commit is contained in:
atheeshp 2023-12-22 15:44:15 +05:30 committed by GitHub
parent 51b72d297a
commit 19e66a9dc8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 1051 additions and 239 deletions

View File

@ -44,6 +44,7 @@ func SetupUnbondingTests(t *testing.T, f *fixture, hookCalled *bool, ubdeID *uin
mockStackingHooks.EXPECT().BeforeDelegationSharesModified(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
mockStackingHooks.EXPECT().BeforeValidatorModified(gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
mockStackingHooks.EXPECT().BeforeValidatorSlashed(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
mockStackingHooks.EXPECT().AfterConsensusPubKeyUpdate(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
f.stakingKeeper.SetHooks(types.NewMultiStakingHooks(mockStackingHooks))
addrDels = simtestutil.AddTestAddrsIncremental(f.bankKeeper, f.stakingKeeper, f.sdkCtx, 2, math.NewInt(10000))

View File

@ -9,6 +9,7 @@ import (
"cosmossdk.io/x/distribution/types"
stakingtypes "cosmossdk.io/x/staking/types"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
sdk "github.com/cosmos/cosmos-sdk/types"
)
@ -187,3 +188,7 @@ func (h Hooks) BeforeDelegationRemoved(_ context.Context, _ sdk.AccAddress, _ sd
func (h Hooks) AfterUnbondingInitiated(_ context.Context, _ uint64) error {
return nil
}
func (h Hooks) AfterConsensusPubKeyUpdate(_ context.Context, _, _ cryptotypes.PubKey, _ sdk.Coin) error {
return nil
}

View File

@ -40,6 +40,15 @@ func (k Keeper) handleEquivocationEvidence(ctx context.Context, evidence *types.
}
if len(validator.GetOperator()) != 0 {
// Get the consAddr from the validator read from the store and not from the evidence,
// because if the validator has rotated its key, the key in evidence could be outdated.
// (ValidatorByConsAddr can get a validator even if the key has been rotated)
valConsAddr, err := validator.GetConsAddr()
if err != nil {
return err
}
consAddr = valConsAddr
if _, err := k.slashingKeeper.GetPubkey(ctx, consAddr.Bytes()); err != nil {
// Ignore evidence that cannot be handled.
//

View File

@ -9,6 +9,7 @@ import (
sdkmath "cosmossdk.io/math"
"cosmossdk.io/x/slashing/types"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
sdk "github.com/cosmos/cosmos-sdk/types"
)
@ -99,3 +100,16 @@ func (h Hooks) BeforeValidatorSlashed(_ context.Context, _ sdk.ValAddress, _ sdk
func (h Hooks) AfterUnbondingInitiated(_ context.Context, _ uint64) error {
return nil
}
// AfterConsensusPubKeyUpdate triggers the functions to rotate the signing-infos also sets address pubkey relation.
func (h Hooks) AfterConsensusPubKeyUpdate(ctx context.Context, oldPubKey, newPubKey cryptotypes.PubKey, _ sdk.Coin) error {
if err := h.k.performConsensusPubKeyUpdate(ctx, oldPubKey, newPubKey); err != nil {
return err
}
if err := h.k.AddrPubkeyRelation.Remove(ctx, oldPubKey.Address()); err != nil {
return err
}
return nil
}

View File

@ -12,6 +12,7 @@ import (
errorsmod "cosmossdk.io/errors"
"cosmossdk.io/x/slashing/types"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
sdk "github.com/cosmos/cosmos-sdk/types"
)
@ -75,6 +76,21 @@ func (k Keeper) SetMissedBlockBitmapChunk(ctx context.Context, addr sdk.ConsAddr
return k.ValidatorMissedBlockBitmap.Set(ctx, collections.Join(addr.Bytes(), uint64(chunkIndex)), chunk)
}
// getPreviousConsKey checks if the key rotated, returns the old consKey to get the missed blocks
// because missed blocks are still pointing to the old key
func (k Keeper) getPreviousConsKey(ctx context.Context, addr sdk.ConsAddress) (sdk.ConsAddress, error) {
oldPk, err := k.sk.ValidatorIdentifier(ctx, addr)
if err != nil {
return nil, err
}
if oldPk != nil {
return oldPk, nil
}
return addr, nil
}
// GetMissedBlockBitmapValue returns true if a validator missed signing a block
// at the given index and false otherwise. The index provided is assumed to be
// the index in the range [0, SignedBlocksWindow), which represents the bitmap
@ -82,6 +98,13 @@ func (k Keeper) SetMissedBlockBitmapChunk(ctx context.Context, addr sdk.ConsAddr
// IndexOffset modulo SignedBlocksWindow. This index is used to fetch the chunk
// in the bitmap and the relative bit in that chunk.
func (k Keeper) GetMissedBlockBitmapValue(ctx context.Context, addr sdk.ConsAddress, index int64) (bool, error) {
// check the key rotated, if rotated use the returned consKey to get the missed blocks
// because missed blocks are still pointing to the old key
addr, err := k.getPreviousConsKey(ctx, addr)
if err != nil {
return false, err
}
// get the chunk or "word" in the logical bitmap
chunkIndex := index / types.MissedBlockBitmapChunkSize
@ -111,6 +134,13 @@ func (k Keeper) GetMissedBlockBitmapValue(ctx context.Context, addr sdk.ConsAddr
// index is used to fetch the chunk in the bitmap and the relative bit in that
// chunk.
func (k Keeper) SetMissedBlockBitmapValue(ctx context.Context, addr sdk.ConsAddress, index int64, missed bool) error {
// check the key rotated, if rotated use the returned consKey to get the missed blocks
// because missed blocks are still pointing to the old key
addr, err := k.getPreviousConsKey(ctx, addr)
if err != nil {
return err
}
// get the chunk or "word" in the logical bitmap
chunkIndex := index / types.MissedBlockBitmapChunkSize
@ -144,19 +174,21 @@ func (k Keeper) SetMissedBlockBitmapValue(ctx context.Context, addr sdk.ConsAddr
// DeleteMissedBlockBitmap removes a validator's missed block bitmap from state.
func (k Keeper) DeleteMissedBlockBitmap(ctx context.Context, addr sdk.ConsAddress) error {
// check the key rotated, if rotated use the returned consKey to delete the missed blocks
// because missed blocks are still pointing to the old key
addr, err := k.getPreviousConsKey(ctx, addr)
if err != nil {
return err
}
rng := collections.NewPrefixedPairRange[[]byte, uint64](addr.Bytes())
err := k.ValidatorMissedBlockBitmap.Walk(ctx, rng, func(key collections.Pair[[]byte, uint64], value []byte) (bool, error) {
return k.ValidatorMissedBlockBitmap.Walk(ctx, rng, func(key collections.Pair[[]byte, uint64], value []byte) (bool, error) {
err := k.ValidatorMissedBlockBitmap.Remove(ctx, key)
if err != nil {
return true, err
}
return false, nil
})
if err != nil {
return err
}
return nil
}
// IterateMissedBlockBitmap iterates over a validator's signed blocks window
@ -168,7 +200,7 @@ func (k Keeper) DeleteMissedBlockBitmap(ctx context.Context, addr sdk.ConsAddres
func (k Keeper) IterateMissedBlockBitmap(ctx context.Context, addr sdk.ConsAddress, cb func(index int64, missed bool) (stop bool)) error {
var index int64
rng := collections.NewPrefixedPairRange[[]byte, uint64](addr.Bytes())
err := k.ValidatorMissedBlockBitmap.Walk(ctx, rng, func(key collections.Pair[[]byte, uint64], value []byte) (bool, error) {
return k.ValidatorMissedBlockBitmap.Walk(ctx, rng, func(key collections.Pair[[]byte, uint64], value []byte) (bool, error) {
bs := bitset.New(uint(types.MissedBlockBitmapChunkSize))
if err := bs.UnmarshalBinary(value); err != nil {
@ -185,10 +217,6 @@ func (k Keeper) IterateMissedBlockBitmap(ctx context.Context, addr sdk.ConsAddre
}
return false, nil
})
if err != nil {
return err
}
return nil
}
// GetValidatorMissedBlocks returns array of missed blocks for given validator.
@ -209,3 +237,24 @@ func (k Keeper) GetValidatorMissedBlocks(ctx context.Context, addr sdk.ConsAddre
return missedBlocks, err
}
// performConsensusPubKeyUpdate updates cons address to its pub key relation
// Updates signing info, missed blocks (removes old one, and sets new one)
func (k Keeper) performConsensusPubKeyUpdate(ctx context.Context, oldPubKey, newPubKey cryptotypes.PubKey) error {
// Connect new consensus address with PubKey
if err := k.AddrPubkeyRelation.Set(ctx, newPubKey.Address(), newPubKey); err != nil {
return err
}
// Migrate ValidatorSigningInfo from oldPubKey to newPubKey
signingInfo, err := k.ValidatorSigningInfo.Get(ctx, sdk.ConsAddress(oldPubKey.Address()))
if err != nil {
return types.ErrInvalidConsPubKey.Wrap("failed to get signing info for old public key")
}
if err := k.ValidatorSigningInfo.Set(ctx, sdk.ConsAddress(newPubKey.Address()), signingInfo); err != nil {
return err
}
return k.ValidatorSigningInfo.Remove(ctx, sdk.ConsAddress(oldPubKey.Address()))
}

View File

@ -3,6 +3,8 @@ package keeper_test
import (
"time"
"github.com/golang/mock/gomock"
"cosmossdk.io/x/slashing/testutil"
slashingtypes "cosmossdk.io/x/slashing/types"
@ -65,6 +67,8 @@ func (s *KeeperTestSuite) TestValidatorMissedBlockBitmap_SmallWindow() {
params.SignedBlocksWindow = window
require.NoError(keeper.Params.Set(ctx, params))
s.stakingKeeper.EXPECT().ValidatorIdentifier(gomock.Any(), consAddr).Return(consAddr, nil).AnyTimes()
// validator misses all blocks in the window
var valIdxOffset int64
for valIdxOffset < params.SignedBlocksWindow {
@ -97,5 +101,13 @@ func (s *KeeperTestSuite) TestValidatorMissedBlockBitmap_SmallWindow() {
missedBlocks, err = keeper.GetValidatorMissedBlocks(ctx, consAddr)
require.NoError(err)
require.Len(missedBlocks, int(params.SignedBlocksWindow)-1)
// if the validator rotated it's key there will be different consKeys and a mapping will be added in the state.
consAddr1 := sdk.ConsAddress(sdk.AccAddress([]byte("addr1_______________")))
s.stakingKeeper.EXPECT().ValidatorIdentifier(gomock.Any(), consAddr1).Return(consAddr, nil).AnyTimes()
missedBlocks, err = keeper.GetValidatorMissedBlocks(ctx, consAddr1)
require.NoError(err)
require.Len(missedBlocks, int(params.SignedBlocksWindow)-1)
}
}

View File

@ -371,6 +371,21 @@ func (mr *MockStakingKeeperMockRecorder) ValidatorByConsAddr(arg0, arg1 interfac
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidatorByConsAddr", reflect.TypeOf((*MockStakingKeeper)(nil).ValidatorByConsAddr), arg0, arg1)
}
// ValidatorIdentifier mocks base method.
func (m *MockStakingKeeper) ValidatorIdentifier(arg0 context.Context, arg1 types0.ConsAddress) (types0.ConsAddress, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "ValidatorIdentifier", arg0, arg1)
ret0, _ := ret[0].(types0.ConsAddress)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// ValidatorIdentifier indicates an expected call of ValidatorIdentifier.
func (mr *MockStakingKeeperMockRecorder) ValidatorIdentifier(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidatorIdentifier", reflect.TypeOf((*MockStakingKeeper)(nil).ValidatorIdentifier), arg0, arg1)
}
// MockStakingHooks is a mock of StakingHooks interface.
type MockStakingHooks struct {
ctrl *gomock.Controller

View File

@ -13,4 +13,5 @@ var (
ErrNoSigningInfoFound = errors.Register(ModuleName, 8, "no validator signing info found")
ErrValidatorTombstoned = errors.Register(ModuleName, 9, "validator already tombstoned")
ErrInvalidSigner = errors.Register(ModuleName, 10, "expected authority account as only signer for proposal message")
ErrInvalidConsPubKey = errors.Register(ModuleName, 11, "invalid consensus pubkey")
)

View File

@ -53,6 +53,10 @@ type StakingKeeper interface {
// IsValidatorJailed returns if the validator is jailed.
IsValidatorJailed(ctx context.Context, addr sdk.ConsAddress) (bool, error)
// ValidatorIdentifier maps the new cons key to previous cons key (which is the address before the rotation).
// (that is: newConsKey -> oldConsKey)
ValidatorIdentifier(context.Context, sdk.ConsAddress) (sdk.ConsAddress, error)
}
// StakingHooks event hooks for staking validator object (noalias)

View File

@ -3,13 +3,18 @@ package keeper
import (
"bytes"
"context"
"errors"
"time"
"cosmossdk.io/collections"
"cosmossdk.io/collections/indexes"
errorsmod "cosmossdk.io/errors"
"cosmossdk.io/x/staking/types"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
)
// maxRotations is the value of max rotations can be made in unbonding period for a validator.
@ -29,7 +34,7 @@ func (k Keeper) setConsPubKeyRotationHistory(
Height: height,
Fee: fee,
}
err := k.RotationHistory.Set(ctx, valAddr, history)
err := k.RotationHistory.Set(ctx, collections.Join(valAddr.Bytes(), height), history)
if err != nil {
return err
}
@ -39,7 +44,7 @@ func (k Keeper) setConsPubKeyRotationHistory(
return err
}
queueTime := sdkCtx.BlockHeader().Time.Add(ubdTime)
queueTime := sdkCtx.HeaderInfo().Time.Add(ubdTime)
if err := k.ValidatorConsensusKeyRotationRecordIndexKey.Set(ctx, collections.Join(valAddr.Bytes(), queueTime)); err != nil {
return err
}
@ -47,6 +52,85 @@ func (k Keeper) setConsPubKeyRotationHistory(
return k.setConsKeyQueue(ctx, queueTime, valAddr)
}
// updateToNewPubkey gets called from the `ApplyAndReturnValidatorSetUpdates` method during EndBlock.
//
// This method makes the relative state changes to update the keys,
// also maintains a map with old to new conskey rotation which is needed to retrieve the old conskey.
// And also triggers the hook to make changes required in slashing and distribution modules.
func (k Keeper) updateToNewPubkey(ctx context.Context, val types.Validator, oldPubKey, newPubKey *codectypes.Any, fee sdk.Coin) error {
consAddr, err := val.GetConsAddr()
if err != nil {
return err
}
if err := k.ValidatorByConsensusAddress.Remove(ctx, consAddr); err != nil {
return err
}
if err := k.DeleteValidatorByPowerIndex(ctx, val); err != nil {
return err
}
val.ConsensusPubkey = newPubKey
if err := k.SetValidator(ctx, val); err != nil {
return err
}
if err := k.SetValidatorByConsAddr(ctx, val); err != nil {
return err
}
if err := k.SetValidatorByPowerIndex(ctx, val); err != nil {
return err
}
oldPk, ok := oldPubKey.GetCachedValue().(cryptotypes.PubKey)
if !ok {
return errorsmod.Wrapf(sdkerrors.ErrInvalidType, "Expecting cryptotypes.PubKey, got %T", oldPk)
}
newPk, ok := newPubKey.GetCachedValue().(cryptotypes.PubKey)
if !ok {
return errorsmod.Wrapf(sdkerrors.ErrInvalidType, "Expecting cryptotypes.PubKey, got %T", newPk)
}
// sets a map: oldConsKey -> newConsKey
if err := k.OldToNewConsKeyMap.Set(ctx, oldPk.Address(), newPk.Address()); err != nil {
return err
}
// sets a map: newConsKey -> oldConsKey
if err := k.setNewToOldConsKeyMap(ctx, sdk.ConsAddress(oldPk.Address()), sdk.ConsAddress(newPk.Address())); err != nil {
return err
}
return k.Hooks().AfterConsensusPubKeyUpdate(ctx, oldPk, newPk, fee)
}
// setNewToOldConsKeyMap adds an entry in the state with the current consKey to the initial consKey of the validator.
// it tries to find the oldPk if there is a entry already present in the state
func (k Keeper) setNewToOldConsKeyMap(ctx context.Context, oldPk, newPk sdk.ConsAddress) error {
pk, err := k.NewToOldConsKeyMap.Get(ctx, oldPk)
if err != nil && !errors.Is(err, collections.ErrNotFound) {
return err
}
if pk != nil {
oldPk = pk
}
return k.NewToOldConsKeyMap.Set(ctx, newPk, oldPk)
}
// ValidatorIdentifier maps the new cons key to previous cons key (which is the address before the rotation).
// (that is: newConsKey -> oldConsKey)
func (k Keeper) ValidatorIdentifier(ctx context.Context, newPk sdk.ConsAddress) (sdk.ConsAddress, error) {
pk, err := k.NewToOldConsKeyMap.Get(ctx, newPk)
if err != nil && !errors.Is(err, collections.ErrNotFound) {
return nil, err
}
return pk, nil
}
// exceedsMaxRotations returns true if the key rotations exceed the limit, currently we are limiting one rotation for unbonding period.
func (k Keeper) exceedsMaxRotations(ctx context.Context, valAddr sdk.ValAddress) error {
count := 0
@ -70,10 +154,12 @@ func (k Keeper) exceedsMaxRotations(ctx context.Context, valAddr sdk.ValAddress)
// this is to keep track of rotations made within the unbonding period
func (k Keeper) setConsKeyQueue(ctx context.Context, ts time.Time, valAddr sdk.ValAddress) error {
queueRec, err := k.ValidatorConsensusKeyRotationRecordQueue.Get(ctx, ts)
if err != nil {
// we should return if the key found here.
if err != nil && !errors.Is(err, collections.ErrNotFound) {
return err
}
// push the address if it is not present in the array.
if !bytesSliceExists(queueRec.Addresses, valAddr.Bytes()) {
// Address does not exist, so you can append it to the list
queueRec.Addresses = append(queueRec.Addresses, valAddr.Bytes())
@ -82,6 +168,7 @@ func (k Keeper) setConsKeyQueue(ctx context.Context, ts time.Time, valAddr sdk.V
return k.ValidatorConsensusKeyRotationRecordQueue.Set(ctx, ts, queueRec)
}
// bytesSliceExists tries to find the duplicate entry the array.
func bytesSliceExists(sliceList [][]byte, targetBytes []byte) bool {
for _, bytesSlice := range sliceList {
if bytes.Equal(bytesSlice, targetBytes) {
@ -90,3 +177,79 @@ func bytesSliceExists(sliceList [][]byte, targetBytes []byte) bool {
}
return false
}
// PurgeAllMaturedConsKeyRotatedKeys deletes all the matured key rotations.
func (k Keeper) PurgeAllMaturedConsKeyRotatedKeys(ctx sdk.Context, maturedTime time.Time) error {
maturedRotatedValAddrs, err := k.getAndRemoveAllMaturedRotatedKeys(ctx, maturedTime)
if err != nil {
return err
}
for _, valAddr := range maturedRotatedValAddrs {
err := k.deleteConsKeyIndexKey(ctx, valAddr, maturedTime)
if err != nil {
return err
}
}
return nil
}
// deleteConsKeyIndexKey deletes the keys which forms a with given validator address and time lesser than the given time.
// eventually there should be only one occurrence since we allow only one rotation for bonding period.
func (k Keeper) deleteConsKeyIndexKey(ctx sdk.Context, valAddr sdk.ValAddress, ts time.Time) error {
rng := new(collections.Range[collections.Pair[[]byte, time.Time]]).
StartInclusive(collections.Join(valAddr.Bytes(), time.Time{})).
EndInclusive(collections.Join(valAddr.Bytes(), ts))
return k.ValidatorConsensusKeyRotationRecordIndexKey.Walk(ctx, rng, func(key collections.Pair[[]byte, time.Time]) (stop bool, err error) {
return false, k.ValidatorConsensusKeyRotationRecordIndexKey.Remove(ctx, key)
})
}
// getAndRemoveAllMaturedRotatedKeys returns all matured valaddresses.
func (k Keeper) getAndRemoveAllMaturedRotatedKeys(ctx sdk.Context, matureTime time.Time) ([][]byte, error) {
valAddrs := [][]byte{}
// get an iterator for all timeslices from time 0 until the current HeaderInfo time
rng := new(collections.Range[time.Time]).EndInclusive(matureTime)
err := k.ValidatorConsensusKeyRotationRecordQueue.Walk(ctx, rng, func(key time.Time, value types.ValAddrsOfRotatedConsKeys) (stop bool, err error) {
valAddrs = append(valAddrs, value.Addresses...)
return false, k.ValidatorConsensusKeyRotationRecordQueue.Remove(ctx, key)
})
if err != nil {
return nil, err
}
return valAddrs, nil
}
// GetBlockConsPubKeyRotationHistory returns the rotation history for the current height.
func (k Keeper) GetBlockConsPubKeyRotationHistory(ctx context.Context) ([]types.ConsPubKeyRotationHistory, error) {
sdkCtx := sdk.UnwrapSDKContext(ctx)
iterator, err := k.RotationHistory.Indexes.Block.MatchExact(ctx, uint64(sdkCtx.BlockHeight()))
if err != nil {
return nil, err
}
defer iterator.Close()
return indexes.CollectValues(ctx, k.RotationHistory, iterator)
}
// GetValidatorConsPubKeyRotationHistory iterates over all the rotated history objects in the state with the given valAddr and returns.
func (k Keeper) GetValidatorConsPubKeyRotationHistory(ctx sdk.Context, operatorAddress sdk.ValAddress) ([]types.ConsPubKeyRotationHistory, error) {
var historyObjects []types.ConsPubKeyRotationHistory
rng := collections.NewPrefixedPairRange[[]byte, uint64](operatorAddress.Bytes())
err := k.RotationHistory.Walk(ctx, rng, func(key collections.Pair[[]byte, uint64], history types.ConsPubKeyRotationHistory) (stop bool, err error) {
historyObjects = append(historyObjects, history)
return false, nil
})
if err != nil {
return nil, err
}
return historyObjects, nil
}

View File

@ -0,0 +1,180 @@
package keeper_test
import (
"time"
"github.com/golang/mock/gomock"
"cosmossdk.io/collections"
"cosmossdk.io/core/header"
authtypes "cosmossdk.io/x/auth/types"
stakingkeeper "cosmossdk.io/x/staking/keeper"
"cosmossdk.io/x/staking/testutil"
"cosmossdk.io/x/staking/types"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
sdk "github.com/cosmos/cosmos-sdk/types"
)
func (s *KeeperTestSuite) TestConsPubKeyRotationHistory() {
stakingKeeper, ctx := s.stakingKeeper, s.ctx
_, addrVals := createValAddrs(2)
// create a validator with a self-delegation
val := testutil.NewValidator(s.T(), addrVals[0], PKs[0])
valTokens := stakingKeeper.TokensFromConsensusPower(ctx, 10)
val, issuedShares := val.AddTokensFromDel(valTokens)
s.Require().Equal(valTokens, issuedShares.RoundInt())
s.bankKeeper.EXPECT().SendCoinsFromModuleToModule(gomock.Any(), types.NotBondedPoolName, types.BondedPoolName, gomock.Any())
_ = stakingkeeper.TestingUpdateValidator(stakingKeeper, ctx, val, true)
val0AccAddr := sdk.AccAddress(addrVals[0].Bytes())
selfDelegation := types.NewDelegation(val0AccAddr.String(), addrVals[0].String(), issuedShares)
err := stakingKeeper.SetDelegation(ctx, selfDelegation)
s.Require().NoError(err)
validators, err := stakingKeeper.GetAllValidators(ctx)
s.Require().NoError(err)
s.Require().Len(validators, 1)
validator := validators[0]
valAddr, err := sdk.ValAddressFromBech32(validator.OperatorAddress)
s.Require().NoError(err)
historyObjects, err := stakingKeeper.GetValidatorConsPubKeyRotationHistory(ctx, valAddr)
s.Require().NoError(err)
s.Require().Len(historyObjects, 0)
newConsPub, err := codectypes.NewAnyWithValue(PKs[1])
s.Require().NoError(err)
newConsPub2, err := codectypes.NewAnyWithValue(PKs[2])
s.Require().NoError(err)
params, err := stakingKeeper.Params.Get(ctx)
s.Require().NoError(err)
height := uint64(ctx.BlockHeight())
err = stakingKeeper.RotationHistory.Set(ctx, collections.Join(valAddr.Bytes(), height), types.ConsPubKeyRotationHistory{
OperatorAddress: valAddr,
OldConsPubkey: validator.ConsensusPubkey,
NewConsPubkey: newConsPub,
Height: height,
Fee: params.KeyRotationFee,
})
s.Require().NoError(err)
historyObjects, err = stakingKeeper.GetValidatorConsPubKeyRotationHistory(ctx, valAddr)
s.Require().NoError(err)
s.Require().Len(historyObjects, 1)
historyObjects, err = stakingKeeper.GetBlockConsPubKeyRotationHistory(ctx)
s.Require().NoError(err)
s.Require().Len(historyObjects, 1)
err = stakingKeeper.RotationHistory.Set(ctx, collections.Join(valAddr.Bytes(), height+1), types.ConsPubKeyRotationHistory{
OperatorAddress: valAddr,
OldConsPubkey: newConsPub,
NewConsPubkey: newConsPub2,
Height: height + 1,
Fee: params.KeyRotationFee,
})
s.Require().NoError(err)
historyObjects1, err := stakingKeeper.GetValidatorConsPubKeyRotationHistory(ctx, valAddr)
s.Require().NoError(err)
s.Require().Len(historyObjects1, 2)
historyObjects, err = stakingKeeper.GetBlockConsPubKeyRotationHistory(ctx)
s.Require().NoError(err)
s.Require().Len(historyObjects, 1)
}
func (s *KeeperTestSuite) TestValidatorIdentifier() {
stakingKeeper, ctx, accountKeeper, bankKeeper := s.stakingKeeper, s.ctx, s.accountKeeper, s.bankKeeper
msgServer := stakingkeeper.NewMsgServerImpl(stakingKeeper)
s.setValidators(6)
validators, err := stakingKeeper.GetAllValidators(ctx)
s.Require().NoError(err)
s.Require().Len(validators, 6)
initialConsAddr, err := validators[3].GetConsAddr()
s.Require().NoError(err)
oldPk, err := stakingKeeper.ValidatorIdentifier(ctx, initialConsAddr)
s.Require().NoError(err)
s.Require().Nil(oldPk)
bondedPool := authtypes.NewEmptyModuleAccount(types.BondedPoolName)
accountKeeper.EXPECT().GetModuleAccount(gomock.Any(), types.BondedPoolName).Return(bondedPool).AnyTimes()
bankKeeper.EXPECT().GetBalance(gomock.Any(), bondedPool.GetAddress(), sdk.DefaultBondDenom).Return(sdk.NewInt64Coin(sdk.DefaultBondDenom, 1000000)).AnyTimes()
val, err := stakingKeeper.ValidatorAddressCodec().StringToBytes(validators[3].GetOperator())
s.Require().NoError(err)
bankKeeper.EXPECT().SendCoinsFromAccountToModule(gomock.Any(), sdk.AccAddress(val), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
req, err := types.NewMsgRotateConsPubKey(validators[3].GetOperator(), PKs[495])
s.Require().NoError(err)
_, err = msgServer.RotateConsPubKey(ctx, req)
s.Require().NoError(err)
_, err = stakingKeeper.BlockValidatorUpdates(ctx)
s.Require().NoError(err)
params, err := stakingKeeper.Params.Get(ctx)
s.Require().NoError(err)
oldPk1, err := stakingKeeper.ValidatorIdentifier(ctx, sdk.ConsAddress(PKs[495].Address()))
s.Require().NoError(err)
s.Require().Equal(oldPk1.Bytes(), initialConsAddr)
ctx = ctx.WithHeaderInfo(header.Info{Time: ctx.BlockTime().Add(params.UnbondingTime).Add(time.Hour)})
_, err = stakingKeeper.BlockValidatorUpdates(ctx)
s.Require().NoError(err)
req, err = types.NewMsgRotateConsPubKey(validators[3].GetOperator(), PKs[494])
s.Require().NoError(err)
_, err = msgServer.RotateConsPubKey(ctx, req)
s.Require().NoError(err)
_, err = stakingKeeper.BlockValidatorUpdates(ctx)
s.Require().NoError(err)
ctx = ctx.WithHeaderInfo(header.Info{Time: ctx.BlockTime().Add(params.UnbondingTime)})
oldPk2, err := stakingKeeper.ValidatorIdentifier(ctx, sdk.ConsAddress(PKs[494].Address()))
s.Require().NoError(err)
_, err = stakingKeeper.BlockValidatorUpdates(ctx)
s.Require().NoError(err)
s.Require().Equal(oldPk2.Bytes(), initialConsAddr)
}
func (s *KeeperTestSuite) setValidators(n int) {
stakingKeeper, ctx := s.stakingKeeper, s.ctx
_, addrVals := createValAddrs(n)
for i := 0; i < n; i++ {
val := testutil.NewValidator(s.T(), addrVals[i], PKs[i])
valTokens := stakingKeeper.TokensFromConsensusPower(ctx, 10)
val, issuedShares := val.AddTokensFromDel(valTokens)
s.Require().Equal(valTokens, issuedShares.RoundInt())
s.bankKeeper.EXPECT().SendCoinsFromModuleToModule(gomock.Any(), types.NotBondedPoolName, types.BondedPoolName, gomock.Any())
_ = stakingkeeper.TestingUpdateValidator(stakingKeeper, ctx, val, true)
val0AccAddr := sdk.AccAddress(addrVals[i].Bytes())
selfDelegation := types.NewDelegation(val0AccAddr.String(), addrVals[i].String(), issuedShares)
err := stakingKeeper.SetDelegation(ctx, selfDelegation)
s.Require().NoError(err)
err = stakingKeeper.SetValidatorByConsAddr(ctx, val)
s.Require().NoError(err)
}
validators, err := stakingKeeper.GetAllValidators(ctx)
s.Require().NoError(err)
s.Require().Len(validators, n)
}

View File

@ -0,0 +1,15 @@
package keeper_test
import (
sdk "github.com/cosmos/cosmos-sdk/types"
)
func (s *KeeperTestSuite) TestHookAfterConsensusPubKeyUpdate() {
stKeeper := s.stakingKeeper
ctx := s.ctx
require := s.Require()
rotationFee := sdk.NewInt64Coin("stake", 1000000)
err := stKeeper.Hooks().AfterConsensusPubKeyUpdate(ctx, PKs[0], PKs[1], rotationFee)
require.NoError(err)
}

View File

@ -43,11 +43,11 @@ func HistoricalInfoCodec(cdc codec.BinaryCodec) collcodec.ValueCodec[types.Histo
}
type rotationHistoryIndexes struct {
Block *indexes.Multi[uint64, []byte, types.ConsPubKeyRotationHistory]
Block *indexes.Multi[uint64, collections.Pair[[]byte, uint64], types.ConsPubKeyRotationHistory]
}
func (a rotationHistoryIndexes) IndexesList() []collections.Index[[]byte, types.ConsPubKeyRotationHistory] {
return []collections.Index[[]byte, types.ConsPubKeyRotationHistory]{
func (a rotationHistoryIndexes) IndexesList() []collections.Index[collections.Pair[[]byte, uint64], types.ConsPubKeyRotationHistory] {
return []collections.Index[collections.Pair[[]byte, uint64], types.ConsPubKeyRotationHistory]{
a.Block,
}
}
@ -55,8 +55,12 @@ func (a rotationHistoryIndexes) IndexesList() []collections.Index[[]byte, types.
func NewRotationHistoryIndexes(sb *collections.SchemaBuilder) rotationHistoryIndexes {
return rotationHistoryIndexes{
Block: indexes.NewMulti(
sb, types.BlockConsPubKeyRotationHistoryKey, "cons_pubkey_history_by_block", collections.Uint64Key, collections.BytesKey,
func(_ []byte, v types.ConsPubKeyRotationHistory) (uint64, error) {
sb,
types.BlockConsPubKeyRotationHistoryKey,
"cons_pubkey_history_by_block",
collections.Uint64Key,
collections.PairKeyCodec(collections.BytesKey, collections.Uint64Key),
func(key collections.Pair[[]byte, uint64], v types.ConsPubKeyRotationHistory) (uint64, error) {
return v.Height, nil
},
),
@ -119,11 +123,13 @@ type Keeper struct {
ValidatorConsensusKeyRotationRecordIndexKey collections.KeySet[collections.Pair[[]byte, time.Time]]
// ValidatorConsensusKeyRotationRecordQueue: this key is used to set the unbonding period time on each rotation
ValidatorConsensusKeyRotationRecordQueue collections.Map[time.Time, types.ValAddrsOfRotatedConsKeys]
// RotatedConsKeyMapIndex: prefix for rotated cons address to new cons address
RotatedConsKeyMapIndex collections.Map[[]byte, []byte]
// NewToOldConsKeyMap: prefix for rotated old cons address to new cons address
NewToOldConsKeyMap collections.Map[[]byte, []byte]
// OldToNewConsKeyMap: prefix for rotated new cons address to old cons address
OldToNewConsKeyMap collections.Map[[]byte, []byte]
// ValidatorConsPubKeyRotationHistory: consPubkey rotation history by validator
// A index is being added with key `BlockConsPubKeyRotationHistory`: consPubkey rotation history by height
RotationHistory *collections.IndexedMap[[]byte, types.ConsPubKeyRotationHistory, rotationHistoryIndexes]
RotationHistory *collections.IndexedMap[collections.Pair[[]byte, uint64], types.ConsPubKeyRotationHistory, rotationHistoryIndexes]
}
// NewKeeper creates a new staking Keeper instance
@ -273,10 +279,18 @@ func NewKeeper(
codec.CollValue[types.ValAddrsOfRotatedConsKeys](cdc),
),
// key format is: 105 | valAddr
RotatedConsKeyMapIndex: collections.NewMap(
sb, types.RotatedConsKeyMapIndex,
"cons_pubkey_map",
// key format is: 105 | consAddr
NewToOldConsKeyMap: collections.NewMap(
sb, types.NewToOldConsKeyMap,
"new_to_old_cons_key_map",
collections.BytesKey,
collections.BytesValue,
),
// key format is: 106 | consAddr
OldToNewConsKeyMap: collections.NewMap(
sb, types.OldToNewConsKeyMap,
"old_to_new_cons_key_map",
collections.BytesKey,
collections.BytesValue,
),
@ -287,7 +301,7 @@ func NewKeeper(
sb,
types.ValidatorConsPubKeyRotationHistoryKey,
"cons_pub_rotation_history",
collections.BytesKey,
collections.PairKeyCodec(collections.BytesKey, collections.Uint64Key),
codec.CollValue[types.ConsPubKeyRotationHistory](cdc),
NewRotationHistoryIndexes(sb),
),

View File

@ -617,7 +617,7 @@ func (k msgServer) RotateConsPubKey(ctx context.Context, msg *types.MsgRotateCon
}
// check cons key is already present in the key rotation history.
rotatedTo, err := k.RotatedConsKeyMapIndex.Get(ctx, pk.Address())
rotatedTo, err := k.NewToOldConsKeyMap.Get(ctx, pk.Address())
if err != nil && !errors.Is(err, collections.ErrNotFound) {
return nil, err
}
@ -630,8 +630,8 @@ func (k msgServer) RotateConsPubKey(ctx context.Context, msg *types.MsgRotateCon
newConsAddr := sdk.ConsAddress(pk.Address())
// checks if NewPubKey is not duplicated on ValidatorsByConsAddr
validator1, _ := k.Keeper.ValidatorByConsAddr(ctx, newConsAddr)
if validator1 != nil {
_, err = k.Keeper.ValidatorByConsAddr(ctx, newConsAddr)
if err == nil {
return nil, types.ErrConsensusPubKeyAlreadyUsedForValidator
}
@ -642,6 +642,10 @@ func (k msgServer) RotateConsPubKey(ctx context.Context, msg *types.MsgRotateCon
validator2, err := k.Keeper.GetValidator(ctx, valAddr)
if err != nil {
return nil, err
}
if validator2.GetOperator() == "" {
return nil, types.ErrNoValidatorFound
}
@ -663,7 +667,7 @@ func (k msgServer) RotateConsPubKey(ctx context.Context, msg *types.MsgRotateCon
return nil, err
}
err = k.Keeper.bankKeeper.SendCoinsFromAccountToModule(ctx, sdk.AccAddress(valAddr), types.DistributionModuleName, sdk.NewCoins(params.KeyRotationFee))
err = k.Keeper.bankKeeper.SendCoinsFromAccountToModule(ctx, sdk.AccAddress(valAddr), types.PoolModuleName, sdk.NewCoins(params.KeyRotationFee))
if err != nil {
return nil, err
}

File diff suppressed because it is too large Load Diff

View File

@ -10,10 +10,14 @@ import (
gogotypes "github.com/cosmos/gogoproto/types"
"cosmossdk.io/core/address"
errorsmod "cosmossdk.io/errors"
"cosmossdk.io/math"
"cosmossdk.io/x/staking/types"
cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
)
// BlockValidatorUpdates calculates the ValidatorUpdates for the current block
@ -112,6 +116,11 @@ func (k Keeper) BlockValidatorUpdates(ctx context.Context) ([]abci.ValidatorUpda
)
}
err = k.PurgeAllMaturedConsKeyRotatedKeys(sdkCtx, sdkCtx.HeaderInfo().Time)
if err != nil {
return nil, err
}
return validatorUpdates, nil
}
@ -241,6 +250,63 @@ func (k Keeper) ApplyAndReturnValidatorSetUpdates(ctx context.Context) (updates
updates = append(updates, validator.ABCIValidatorUpdateZero())
}
// ApplyAndReturnValidatorSetUpdates checks if there is ConsPubKeyRotationHistory
// with ConsPubKeyRotationHistory.RotatedHeight == ctx.BlockHeight() and if so, generates 2 ValidatorUpdate,
// one for a remove validator and one for create new validator
historyObjects, err := k.GetBlockConsPubKeyRotationHistory(ctx)
if err != nil {
return nil, err
}
for _, history := range historyObjects {
valAddr := history.OperatorAddress
if err != nil {
return nil, err
}
validator, err := k.GetValidator(ctx, valAddr)
if err != nil {
return nil, err
}
oldPk, ok := history.OldConsPubkey.GetCachedValue().(cryptotypes.PubKey)
if !ok {
return nil, errorsmod.Wrapf(sdkerrors.ErrInvalidType, "Expecting cryptotypes.PubKey, got %T", oldPk)
}
oldCmtPk, err := cryptocodec.ToCmtProtoPublicKey(oldPk)
if err != nil {
return nil, err
}
newPk, ok := history.NewConsPubkey.GetCachedValue().(cryptotypes.PubKey)
if !ok {
return nil, errorsmod.Wrapf(sdkerrors.ErrInvalidType, "Expecting cryptotypes.PubKey, got %T", oldPk)
}
newCmtPk, err := cryptocodec.ToCmtProtoPublicKey(newPk)
if err != nil {
return nil, err
}
// a validator cannot rotate keys if it's not bonded or if it's jailed
// - a validator can be unbonding state but jailed status false
// - a validator can be jailed and status can be unbonding
if !(validator.Jailed || validator.Status != types.Bonded) {
updates = append(updates, abci.ValidatorUpdate{
PubKey: oldCmtPk,
Power: 0,
})
updates = append(updates, abci.ValidatorUpdate{
PubKey: newCmtPk,
Power: validator.ConsensusPower(powerReduction),
})
if err := k.updateToNewPubkey(ctx, validator, history.OldConsPubkey, history.NewConsPubkey, history.Fee); err != nil {
return nil, err
}
}
}
// Update the pools based on the recent updates in the validator set:
// - The tokens from the non-bonded candidates that enter the new validator set need to be transferred
// to the Bonded pool.

View File

@ -38,11 +38,21 @@ func (k Keeper) GetValidator(ctx context.Context, addr sdk.ValAddress) (validato
func (k Keeper) GetValidatorByConsAddr(ctx context.Context, consAddr sdk.ConsAddress) (validator types.Validator, err error) {
opAddr, err := k.ValidatorByConsensusAddress.Get(ctx, consAddr)
if err != nil && !errors.Is(err, collections.ErrNotFound) {
return validator, err
// if the validator not found try to find it in the map of `OldToNewConsKeyMap`` because validator may've rotated it's key.
if !errors.Is(err, collections.ErrNotFound) {
return types.Validator{}, err
}
newConsAddr, err := k.OldToNewConsKeyMap.Get(ctx, consAddr)
if err != nil {
return types.Validator{}, err
}
opAddr = newConsAddr
}
if opAddr == nil {
return validator, types.ErrNoValidatorFound
return types.Validator{}, types.ErrNoValidatorFound
}
return k.GetValidator(ctx, opAddr)

View File

@ -13,7 +13,8 @@ import (
math "cosmossdk.io/math"
types "cosmossdk.io/x/staking/types"
crypto "github.com/cometbft/cometbft/proto/tendermint/crypto"
types0 "github.com/cosmos/cosmos-sdk/types"
types0 "github.com/cosmos/cosmos-sdk/crypto/types"
types1 "github.com/cosmos/cosmos-sdk/types"
gomock "github.com/golang/mock/gomock"
)
@ -55,10 +56,10 @@ func (mr *MockAccountKeeperMockRecorder) AddressCodec() *gomock.Call {
}
// GetAccount mocks base method.
func (m *MockAccountKeeper) GetAccount(ctx context.Context, addr types0.AccAddress) types0.AccountI {
func (m *MockAccountKeeper) GetAccount(ctx context.Context, addr types1.AccAddress) types1.AccountI {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetAccount", ctx, addr)
ret0, _ := ret[0].(types0.AccountI)
ret0, _ := ret[0].(types1.AccountI)
return ret0
}
@ -69,10 +70,10 @@ func (mr *MockAccountKeeperMockRecorder) GetAccount(ctx, addr interface{}) *gomo
}
// GetModuleAccount mocks base method.
func (m *MockAccountKeeper) GetModuleAccount(ctx context.Context, moduleName string) types0.ModuleAccountI {
func (m *MockAccountKeeper) GetModuleAccount(ctx context.Context, moduleName string) types1.ModuleAccountI {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetModuleAccount", ctx, moduleName)
ret0, _ := ret[0].(types0.ModuleAccountI)
ret0, _ := ret[0].(types1.ModuleAccountI)
return ret0
}
@ -83,10 +84,10 @@ func (mr *MockAccountKeeperMockRecorder) GetModuleAccount(ctx, moduleName interf
}
// GetModuleAddress mocks base method.
func (m *MockAccountKeeper) GetModuleAddress(name string) types0.AccAddress {
func (m *MockAccountKeeper) GetModuleAddress(name string) types1.AccAddress {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetModuleAddress", name)
ret0, _ := ret[0].(types0.AccAddress)
ret0, _ := ret[0].(types1.AccAddress)
return ret0
}
@ -97,7 +98,7 @@ func (mr *MockAccountKeeperMockRecorder) GetModuleAddress(name interface{}) *gom
}
// IterateAccounts mocks base method.
func (m *MockAccountKeeper) IterateAccounts(ctx context.Context, process func(types0.AccountI) bool) {
func (m *MockAccountKeeper) IterateAccounts(ctx context.Context, process func(types1.AccountI) bool) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "IterateAccounts", ctx, process)
}
@ -109,7 +110,7 @@ func (mr *MockAccountKeeperMockRecorder) IterateAccounts(ctx, process interface{
}
// SetModuleAccount mocks base method.
func (m *MockAccountKeeper) SetModuleAccount(arg0 context.Context, arg1 types0.ModuleAccountI) {
func (m *MockAccountKeeper) SetModuleAccount(arg0 context.Context, arg1 types1.ModuleAccountI) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "SetModuleAccount", arg0, arg1)
}
@ -144,7 +145,7 @@ func (m *MockBankKeeper) EXPECT() *MockBankKeeperMockRecorder {
}
// BurnCoins mocks base method.
func (m *MockBankKeeper) BurnCoins(arg0 context.Context, arg1 []byte, arg2 types0.Coins) error {
func (m *MockBankKeeper) BurnCoins(arg0 context.Context, arg1 []byte, arg2 types1.Coins) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "BurnCoins", arg0, arg1, arg2)
ret0, _ := ret[0].(error)
@ -158,7 +159,7 @@ func (mr *MockBankKeeperMockRecorder) BurnCoins(arg0, arg1, arg2 interface{}) *g
}
// DelegateCoinsFromAccountToModule mocks base method.
func (m *MockBankKeeper) DelegateCoinsFromAccountToModule(ctx context.Context, senderAddr types0.AccAddress, recipientModule string, amt types0.Coins) error {
func (m *MockBankKeeper) DelegateCoinsFromAccountToModule(ctx context.Context, senderAddr types1.AccAddress, recipientModule string, amt types1.Coins) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "DelegateCoinsFromAccountToModule", ctx, senderAddr, recipientModule, amt)
ret0, _ := ret[0].(error)
@ -172,10 +173,10 @@ func (mr *MockBankKeeperMockRecorder) DelegateCoinsFromAccountToModule(ctx, send
}
// GetAllBalances mocks base method.
func (m *MockBankKeeper) GetAllBalances(ctx context.Context, addr types0.AccAddress) types0.Coins {
func (m *MockBankKeeper) GetAllBalances(ctx context.Context, addr types1.AccAddress) types1.Coins {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetAllBalances", ctx, addr)
ret0, _ := ret[0].(types0.Coins)
ret0, _ := ret[0].(types1.Coins)
return ret0
}
@ -186,10 +187,10 @@ func (mr *MockBankKeeperMockRecorder) GetAllBalances(ctx, addr interface{}) *gom
}
// GetBalance mocks base method.
func (m *MockBankKeeper) GetBalance(ctx context.Context, addr types0.AccAddress, denom string) types0.Coin {
func (m *MockBankKeeper) GetBalance(ctx context.Context, addr types1.AccAddress, denom string) types1.Coin {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetBalance", ctx, addr, denom)
ret0, _ := ret[0].(types0.Coin)
ret0, _ := ret[0].(types1.Coin)
return ret0
}
@ -200,10 +201,10 @@ func (mr *MockBankKeeperMockRecorder) GetBalance(ctx, addr, denom interface{}) *
}
// GetSupply mocks base method.
func (m *MockBankKeeper) GetSupply(ctx context.Context, denom string) types0.Coin {
func (m *MockBankKeeper) GetSupply(ctx context.Context, denom string) types1.Coin {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetSupply", ctx, denom)
ret0, _ := ret[0].(types0.Coin)
ret0, _ := ret[0].(types1.Coin)
return ret0
}
@ -214,10 +215,10 @@ func (mr *MockBankKeeperMockRecorder) GetSupply(ctx, denom interface{}) *gomock.
}
// LockedCoins mocks base method.
func (m *MockBankKeeper) LockedCoins(ctx context.Context, addr types0.AccAddress) types0.Coins {
func (m *MockBankKeeper) LockedCoins(ctx context.Context, addr types1.AccAddress) types1.Coins {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "LockedCoins", ctx, addr)
ret0, _ := ret[0].(types0.Coins)
ret0, _ := ret[0].(types1.Coins)
return ret0
}
@ -228,7 +229,7 @@ func (mr *MockBankKeeperMockRecorder) LockedCoins(ctx, addr interface{}) *gomock
}
// SendCoinsFromAccountToModule mocks base method.
func (m *MockBankKeeper) SendCoinsFromAccountToModule(ctx context.Context, senderAddr types0.AccAddress, recipientModule string, amt types0.Coins) error {
func (m *MockBankKeeper) SendCoinsFromAccountToModule(ctx context.Context, senderAddr types1.AccAddress, recipientModule string, amt types1.Coins) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "SendCoinsFromAccountToModule", ctx, senderAddr, recipientModule, amt)
ret0, _ := ret[0].(error)
@ -242,7 +243,7 @@ func (mr *MockBankKeeperMockRecorder) SendCoinsFromAccountToModule(ctx, senderAd
}
// SendCoinsFromModuleToModule mocks base method.
func (m *MockBankKeeper) SendCoinsFromModuleToModule(ctx context.Context, senderPool, recipientPool string, amt types0.Coins) error {
func (m *MockBankKeeper) SendCoinsFromModuleToModule(ctx context.Context, senderPool, recipientPool string, amt types1.Coins) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "SendCoinsFromModuleToModule", ctx, senderPool, recipientPool, amt)
ret0, _ := ret[0].(error)
@ -256,10 +257,10 @@ func (mr *MockBankKeeperMockRecorder) SendCoinsFromModuleToModule(ctx, senderPoo
}
// SpendableCoins mocks base method.
func (m *MockBankKeeper) SpendableCoins(ctx context.Context, addr types0.AccAddress) types0.Coins {
func (m *MockBankKeeper) SpendableCoins(ctx context.Context, addr types1.AccAddress) types1.Coins {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "SpendableCoins", ctx, addr)
ret0, _ := ret[0].(types0.Coins)
ret0, _ := ret[0].(types1.Coins)
return ret0
}
@ -270,7 +271,7 @@ func (mr *MockBankKeeperMockRecorder) SpendableCoins(ctx, addr interface{}) *gom
}
// UndelegateCoinsFromModuleToAccount mocks base method.
func (m *MockBankKeeper) UndelegateCoinsFromModuleToAccount(ctx context.Context, senderModule string, recipientAddr types0.AccAddress, amt types0.Coins) error {
func (m *MockBankKeeper) UndelegateCoinsFromModuleToAccount(ctx context.Context, senderModule string, recipientAddr types1.AccAddress, amt types1.Coins) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "UndelegateCoinsFromModuleToAccount", ctx, senderModule, recipientAddr, amt)
ret0, _ := ret[0].(error)
@ -307,10 +308,10 @@ func (m *MockValidatorSet) EXPECT() *MockValidatorSetMockRecorder {
}
// Delegation mocks base method.
func (m *MockValidatorSet) Delegation(arg0 context.Context, arg1 types0.AccAddress, arg2 types0.ValAddress) (types0.DelegationI, error) {
func (m *MockValidatorSet) Delegation(arg0 context.Context, arg1 types1.AccAddress, arg2 types1.ValAddress) (types1.DelegationI, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Delegation", arg0, arg1, arg2)
ret0, _ := ret[0].(types0.DelegationI)
ret0, _ := ret[0].(types1.DelegationI)
ret1, _ := ret[1].(error)
return ret0, ret1
}
@ -322,7 +323,7 @@ func (mr *MockValidatorSetMockRecorder) Delegation(arg0, arg1, arg2 interface{})
}
// GetPubKeyByConsAddr mocks base method.
func (m *MockValidatorSet) GetPubKeyByConsAddr(arg0 context.Context, arg1 types0.ConsAddress) (crypto.PublicKey, error) {
func (m *MockValidatorSet) GetPubKeyByConsAddr(arg0 context.Context, arg1 types1.ConsAddress) (crypto.PublicKey, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetPubKeyByConsAddr", arg0, arg1)
ret0, _ := ret[0].(crypto.PublicKey)
@ -337,7 +338,7 @@ func (mr *MockValidatorSetMockRecorder) GetPubKeyByConsAddr(arg0, arg1 interface
}
// IterateBondedValidatorsByPower mocks base method.
func (m *MockValidatorSet) IterateBondedValidatorsByPower(arg0 context.Context, arg1 func(int64, types0.ValidatorI) bool) error {
func (m *MockValidatorSet) IterateBondedValidatorsByPower(arg0 context.Context, arg1 func(int64, types1.ValidatorI) bool) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "IterateBondedValidatorsByPower", arg0, arg1)
ret0, _ := ret[0].(error)
@ -351,7 +352,7 @@ func (mr *MockValidatorSetMockRecorder) IterateBondedValidatorsByPower(arg0, arg
}
// IterateValidators mocks base method.
func (m *MockValidatorSet) IterateValidators(arg0 context.Context, arg1 func(int64, types0.ValidatorI) bool) error {
func (m *MockValidatorSet) IterateValidators(arg0 context.Context, arg1 func(int64, types1.ValidatorI) bool) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "IterateValidators", arg0, arg1)
ret0, _ := ret[0].(error)
@ -365,7 +366,7 @@ func (mr *MockValidatorSetMockRecorder) IterateValidators(arg0, arg1 interface{}
}
// Jail mocks base method.
func (m *MockValidatorSet) Jail(arg0 context.Context, arg1 types0.ConsAddress) error {
func (m *MockValidatorSet) Jail(arg0 context.Context, arg1 types1.ConsAddress) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Jail", arg0, arg1)
ret0, _ := ret[0].(error)
@ -394,7 +395,7 @@ func (mr *MockValidatorSetMockRecorder) MaxValidators(arg0 interface{}) *gomock.
}
// Slash mocks base method.
func (m *MockValidatorSet) Slash(arg0 context.Context, arg1 types0.ConsAddress, arg2, arg3 int64, arg4 math.LegacyDec) (math.Int, error) {
func (m *MockValidatorSet) Slash(arg0 context.Context, arg1 types1.ConsAddress, arg2, arg3 int64, arg4 math.LegacyDec) (math.Int, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Slash", arg0, arg1, arg2, arg3, arg4)
ret0, _ := ret[0].(math.Int)
@ -409,7 +410,7 @@ func (mr *MockValidatorSetMockRecorder) Slash(arg0, arg1, arg2, arg3, arg4 inter
}
// SlashWithInfractionReason mocks base method.
func (m *MockValidatorSet) SlashWithInfractionReason(arg0 context.Context, arg1 types0.ConsAddress, arg2, arg3 int64, arg4 math.LegacyDec, arg5 stakingv1beta1.Infraction) (math.Int, error) {
func (m *MockValidatorSet) SlashWithInfractionReason(arg0 context.Context, arg1 types1.ConsAddress, arg2, arg3 int64, arg4 math.LegacyDec, arg5 stakingv1beta1.Infraction) (math.Int, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "SlashWithInfractionReason", arg0, arg1, arg2, arg3, arg4, arg5)
ret0, _ := ret[0].(math.Int)
@ -454,7 +455,7 @@ func (mr *MockValidatorSetMockRecorder) TotalBondedTokens(arg0 interface{}) *gom
}
// Unjail mocks base method.
func (m *MockValidatorSet) Unjail(arg0 context.Context, arg1 types0.ConsAddress) error {
func (m *MockValidatorSet) Unjail(arg0 context.Context, arg1 types1.ConsAddress) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Unjail", arg0, arg1)
ret0, _ := ret[0].(error)
@ -468,10 +469,10 @@ func (mr *MockValidatorSetMockRecorder) Unjail(arg0, arg1 interface{}) *gomock.C
}
// Validator mocks base method.
func (m *MockValidatorSet) Validator(arg0 context.Context, arg1 types0.ValAddress) (types0.ValidatorI, error) {
func (m *MockValidatorSet) Validator(arg0 context.Context, arg1 types1.ValAddress) (types1.ValidatorI, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Validator", arg0, arg1)
ret0, _ := ret[0].(types0.ValidatorI)
ret0, _ := ret[0].(types1.ValidatorI)
ret1, _ := ret[1].(error)
return ret0, ret1
}
@ -483,10 +484,10 @@ func (mr *MockValidatorSetMockRecorder) Validator(arg0, arg1 interface{}) *gomoc
}
// ValidatorByConsAddr mocks base method.
func (m *MockValidatorSet) ValidatorByConsAddr(arg0 context.Context, arg1 types0.ConsAddress) (types0.ValidatorI, error) {
func (m *MockValidatorSet) ValidatorByConsAddr(arg0 context.Context, arg1 types1.ConsAddress) (types1.ValidatorI, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "ValidatorByConsAddr", arg0, arg1)
ret0, _ := ret[0].(types0.ValidatorI)
ret0, _ := ret[0].(types1.ValidatorI)
ret1, _ := ret[1].(error)
return ret0, ret1
}
@ -535,7 +536,7 @@ func (mr *MockDelegationSetMockRecorder) GetValidatorSet() *gomock.Call {
}
// IterateDelegations mocks base method.
func (m *MockDelegationSet) IterateDelegations(ctx context.Context, delegator types0.AccAddress, fn func(int64, types0.DelegationI) bool) error {
func (m *MockDelegationSet) IterateDelegations(ctx context.Context, delegator types1.AccAddress, fn func(int64, types1.DelegationI) bool) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "IterateDelegations", ctx, delegator, fn)
ret0, _ := ret[0].(error)
@ -571,8 +572,22 @@ func (m *MockStakingHooks) EXPECT() *MockStakingHooksMockRecorder {
return m.recorder
}
// AfterConsensusPubKeyUpdate mocks base method.
func (m *MockStakingHooks) AfterConsensusPubKeyUpdate(ctx context.Context, oldPubKey, newPubKey types0.PubKey, rotationFee types1.Coin) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "AfterConsensusPubKeyUpdate", ctx, oldPubKey, newPubKey, rotationFee)
ret0, _ := ret[0].(error)
return ret0
}
// AfterConsensusPubKeyUpdate indicates an expected call of AfterConsensusPubKeyUpdate.
func (mr *MockStakingHooksMockRecorder) AfterConsensusPubKeyUpdate(ctx, oldPubKey, newPubKey, rotationFee interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AfterConsensusPubKeyUpdate", reflect.TypeOf((*MockStakingHooks)(nil).AfterConsensusPubKeyUpdate), ctx, oldPubKey, newPubKey, rotationFee)
}
// AfterDelegationModified mocks base method.
func (m *MockStakingHooks) AfterDelegationModified(ctx context.Context, delAddr types0.AccAddress, valAddr types0.ValAddress) error {
func (m *MockStakingHooks) AfterDelegationModified(ctx context.Context, delAddr types1.AccAddress, valAddr types1.ValAddress) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "AfterDelegationModified", ctx, delAddr, valAddr)
ret0, _ := ret[0].(error)
@ -600,7 +615,7 @@ func (mr *MockStakingHooksMockRecorder) AfterUnbondingInitiated(ctx, id interfac
}
// AfterValidatorBeginUnbonding mocks base method.
func (m *MockStakingHooks) AfterValidatorBeginUnbonding(ctx context.Context, consAddr types0.ConsAddress, valAddr types0.ValAddress) error {
func (m *MockStakingHooks) AfterValidatorBeginUnbonding(ctx context.Context, consAddr types1.ConsAddress, valAddr types1.ValAddress) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "AfterValidatorBeginUnbonding", ctx, consAddr, valAddr)
ret0, _ := ret[0].(error)
@ -614,7 +629,7 @@ func (mr *MockStakingHooksMockRecorder) AfterValidatorBeginUnbonding(ctx, consAd
}
// AfterValidatorBonded mocks base method.
func (m *MockStakingHooks) AfterValidatorBonded(ctx context.Context, consAddr types0.ConsAddress, valAddr types0.ValAddress) error {
func (m *MockStakingHooks) AfterValidatorBonded(ctx context.Context, consAddr types1.ConsAddress, valAddr types1.ValAddress) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "AfterValidatorBonded", ctx, consAddr, valAddr)
ret0, _ := ret[0].(error)
@ -628,7 +643,7 @@ func (mr *MockStakingHooksMockRecorder) AfterValidatorBonded(ctx, consAddr, valA
}
// AfterValidatorCreated mocks base method.
func (m *MockStakingHooks) AfterValidatorCreated(ctx context.Context, valAddr types0.ValAddress) error {
func (m *MockStakingHooks) AfterValidatorCreated(ctx context.Context, valAddr types1.ValAddress) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "AfterValidatorCreated", ctx, valAddr)
ret0, _ := ret[0].(error)
@ -642,7 +657,7 @@ func (mr *MockStakingHooksMockRecorder) AfterValidatorCreated(ctx, valAddr inter
}
// AfterValidatorRemoved mocks base method.
func (m *MockStakingHooks) AfterValidatorRemoved(ctx context.Context, consAddr types0.ConsAddress, valAddr types0.ValAddress) error {
func (m *MockStakingHooks) AfterValidatorRemoved(ctx context.Context, consAddr types1.ConsAddress, valAddr types1.ValAddress) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "AfterValidatorRemoved", ctx, consAddr, valAddr)
ret0, _ := ret[0].(error)
@ -656,7 +671,7 @@ func (mr *MockStakingHooksMockRecorder) AfterValidatorRemoved(ctx, consAddr, val
}
// BeforeDelegationCreated mocks base method.
func (m *MockStakingHooks) BeforeDelegationCreated(ctx context.Context, delAddr types0.AccAddress, valAddr types0.ValAddress) error {
func (m *MockStakingHooks) BeforeDelegationCreated(ctx context.Context, delAddr types1.AccAddress, valAddr types1.ValAddress) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "BeforeDelegationCreated", ctx, delAddr, valAddr)
ret0, _ := ret[0].(error)
@ -670,7 +685,7 @@ func (mr *MockStakingHooksMockRecorder) BeforeDelegationCreated(ctx, delAddr, va
}
// BeforeDelegationRemoved mocks base method.
func (m *MockStakingHooks) BeforeDelegationRemoved(ctx context.Context, delAddr types0.AccAddress, valAddr types0.ValAddress) error {
func (m *MockStakingHooks) BeforeDelegationRemoved(ctx context.Context, delAddr types1.AccAddress, valAddr types1.ValAddress) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "BeforeDelegationRemoved", ctx, delAddr, valAddr)
ret0, _ := ret[0].(error)
@ -684,7 +699,7 @@ func (mr *MockStakingHooksMockRecorder) BeforeDelegationRemoved(ctx, delAddr, va
}
// BeforeDelegationSharesModified mocks base method.
func (m *MockStakingHooks) BeforeDelegationSharesModified(ctx context.Context, delAddr types0.AccAddress, valAddr types0.ValAddress) error {
func (m *MockStakingHooks) BeforeDelegationSharesModified(ctx context.Context, delAddr types1.AccAddress, valAddr types1.ValAddress) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "BeforeDelegationSharesModified", ctx, delAddr, valAddr)
ret0, _ := ret[0].(error)
@ -698,7 +713,7 @@ func (mr *MockStakingHooksMockRecorder) BeforeDelegationSharesModified(ctx, delA
}
// BeforeValidatorModified mocks base method.
func (m *MockStakingHooks) BeforeValidatorModified(ctx context.Context, valAddr types0.ValAddress) error {
func (m *MockStakingHooks) BeforeValidatorModified(ctx context.Context, valAddr types1.ValAddress) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "BeforeValidatorModified", ctx, valAddr)
ret0, _ := ret[0].(error)
@ -712,7 +727,7 @@ func (mr *MockStakingHooksMockRecorder) BeforeValidatorModified(ctx, valAddr int
}
// BeforeValidatorSlashed mocks base method.
func (m *MockStakingHooks) BeforeValidatorSlashed(ctx context.Context, valAddr types0.ValAddress, fraction math.LegacyDec) error {
func (m *MockStakingHooks) BeforeValidatorSlashed(ctx context.Context, valAddr types1.ValAddress, fraction math.LegacyDec) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "BeforeValidatorSlashed", ctx, valAddr, fraction)
ret0, _ := ret[0].(error)

View File

@ -18,6 +18,7 @@ func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) {
legacy.RegisterAminoMsg(cdc, &MsgBeginRedelegate{}, "cosmos-sdk/MsgBeginRedelegate")
legacy.RegisterAminoMsg(cdc, &MsgCancelUnbondingDelegation{}, "cosmos-sdk/MsgCancelUnbondingDelegation")
legacy.RegisterAminoMsg(cdc, &MsgUpdateParams{}, "cosmos-sdk/x/staking/MsgUpdateParams")
legacy.RegisterAminoMsg(cdc, &MsgRotateConsPubKey{}, "cosmos-sdk/MsgRotateConsPubKey")
cdc.RegisterInterface((*isStakeAuthorization_Validators)(nil), nil)
cdc.RegisterConcrete(&StakeAuthorization_AllowList{}, "cosmos-sdk/StakeAuthorization/AllowList", nil)

View File

@ -9,6 +9,7 @@ import (
"cosmossdk.io/core/address"
"cosmossdk.io/math"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
sdk "github.com/cosmos/cosmos-sdk/types"
)
@ -107,6 +108,7 @@ type StakingHooks interface {
AfterDelegationModified(ctx context.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) error
BeforeValidatorSlashed(ctx context.Context, valAddr sdk.ValAddress, fraction math.LegacyDec) error
AfterUnbondingInitiated(ctx context.Context, id uint64) error
AfterConsensusPubKeyUpdate(ctx context.Context, oldPubKey, newPubKey cryptotypes.PubKey, rotationFee sdk.Coin) error
}
// StakingHooksWrapper is a wrapper for modules to inject StakingHooks using depinject.

View File

@ -5,6 +5,7 @@ import (
sdkmath "cosmossdk.io/math"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
sdk "github.com/cosmos/cosmos-sdk/types"
)
@ -116,3 +117,12 @@ func (h MultiStakingHooks) AfterUnbondingInitiated(ctx context.Context, id uint6
}
return nil
}
func (h MultiStakingHooks) AfterConsensusPubKeyUpdate(ctx context.Context, oldPubKey, newPubKey cryptotypes.PubKey, rotationFee sdk.Coin) error {
for i := range h {
if err := h[i].AfterConsensusPubKeyUpdate(ctx, oldPubKey, newPubKey, rotationFee); err != nil {
return err
}
}
return nil
}

View File

@ -25,10 +25,10 @@ const (
// GovModuleName is the name of the gov module
GovModuleName = "gov"
// distributionModuleName duplicates the distribution module's name to avoid a cyclic dependency with x/distribution.
// PoolModuleName duplicates the Protocolpool module's name to avoid a cyclic dependency with x/protocolpool.
// It should be synced with the distribution module's name if it is ever changed.
// See: https://github.com/cosmos/cosmos-sdk/blob/912390d5fc4a32113ea1aacc98b77b2649aea4c2/x/distribution/types/keys.go#L15
DistributionModuleName = "distribution"
PoolModuleName = "protocolpool"
)
var (
@ -68,7 +68,8 @@ var (
BlockConsPubKeyRotationHistoryKey = collections.NewPrefix(102) // prefix for consPubkey rotation history by height
ValidatorConsensusKeyRotationRecordQueueKey = collections.NewPrefix(103) // this key is used to set the unbonding period time on each rotation
ValidatorConsensusKeyRotationRecordIndexKey = collections.NewPrefix(104) // this key is used to restrict the validator next rotation within waiting (unbonding) period
RotatedConsKeyMapIndex = collections.NewPrefix(105) // prefix for rotated cons address to new cons address
NewToOldConsKeyMap = collections.NewPrefix(105) // prefix for rotated cons address to new cons address
OldToNewConsKeyMap = collections.NewPrefix(106) // prefix for rotated cons address to new cons address
)
// UnbondingType defines the type of unbonding operation

View File

@ -142,3 +142,35 @@ func NewMsgCancelUnbondingDelegation(delAddr, valAddr string, creationHeight int
CreationHeight: creationHeight,
}
}
// NewMsgRotateConsPubKey creates a new MsgRotateConsPubKey instance.
func NewMsgRotateConsPubKey(valAddr string, pubKey cryptotypes.PubKey) (*MsgRotateConsPubKey, error) {
var pkAny *codectypes.Any
if pubKey != nil {
var err error
if pkAny, err = codectypes.NewAnyWithValue(pubKey); err != nil {
return nil, err
}
}
return &MsgRotateConsPubKey{
ValidatorAddress: valAddr,
NewPubkey: pkAny,
}, nil
}
// UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces
func (msg MsgRotateConsPubKey) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error {
var pubKey cryptotypes.PubKey
return unpacker.UnpackAny(msg.NewPubkey, &pubKey)
}
// UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces
func (hi ConsPubKeyRotationHistory) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error {
var oldPubKey cryptotypes.PubKey
err := unpacker.UnpackAny(hi.OldConsPubkey, &oldPubKey)
if err != nil {
return err
}
var newPubKey cryptotypes.PubKey
return unpacker.UnpackAny(hi.NewConsPubkey, &newPubKey)
}