refactor(x/distribution)!: migrate to collections (#17115)

Co-authored-by: Julien Robert <julien@rbrt.fr>
This commit is contained in:
Facundo Medica 2023-07-31 10:41:22 +02:00 committed by GitHub
parent dc2b1998c2
commit 60605de779
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 296 additions and 177 deletions

View File

@ -56,6 +56,9 @@ Ref: https://keepachangelog.com/en/1.0.0/
### API Breaking Changes
* (x/distribution) [#17115](https://github.com/cosmos/cosmos-sdk/pull/17115) Use collections for `PreviousProposer` and `ValidatorSlashEvents`:
* remove from `Keeper`: `GetPreviousProposerConsAddr`, `SetPreviousProposerConsAddr`, `GetValidatorHistoricalReferenceCount`, `GetValidatorSlashEvent`, `SetValidatorSlashEvent`.
* (x/slashing) [17063](https://github.com/cosmos/cosmos-sdk/pull/17063) Use collections for `HistoricalInfo`:
* (x/feegrant) [16535](https://github.com/cosmos/cosmos-sdk/pull/16535) Use collections for `FeeAllowance`, `FeeAllowanceQueue`.
* (x/staking) [17063](https://github.com/cosmos/cosmos-sdk/pull/17063) Use collections for `HistoricalInfo`:
* remove `Keeper`: `GetHistoricalInfo`, `SetHistoricalInfo`,
@ -94,6 +97,10 @@ Ref: https://keepachangelog.com/en/1.0.0/
* (server) [#17177](https://github.com/cosmos/cosmos-sdk/pull/17177) Remove `iavl-lazy-loading` configuration.
* (rosetta) [#16276](https://github.com/cosmos/cosmos-sdk/issues/16276) Rosetta migration to standalone repo.
### State Machine Breaking
* (x/distribution) [#17115](https://github.com/cosmos/cosmos-sdk/pull/17115) Migrate `PreviousProposer` to collections.
## [v0.50.0-beta.0](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.50.0-beta.0) - 2023-07-19
### Features

2
go.mod
View File

@ -4,7 +4,7 @@ module github.com/cosmos/cosmos-sdk
require (
cosmossdk.io/api v0.7.0
cosmossdk.io/collections v0.3.0
cosmossdk.io/collections v0.3.1-0.20230727092431-f0f777fa3cb7
cosmossdk.io/core v0.9.0
cosmossdk.io/depinject v1.0.0-alpha.3
cosmossdk.io/errors v1.0.0

4
go.sum
View File

@ -37,8 +37,8 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
cosmossdk.io/api v0.7.0 h1:QsEMIWuv9xWDbF2HZnW4Lpu1/SejCztPu0LQx7t6MN4=
cosmossdk.io/api v0.7.0/go.mod h1:kJFAEMLN57y0viszHDPLMmieF0471o5QAwwApa+270M=
cosmossdk.io/collections v0.3.0 h1:v0eEqLBxebAV+t+Ahwf9tSJOu95HVLINwROXx2TTZ08=
cosmossdk.io/collections v0.3.0/go.mod h1:CHE1+niUElL9ikCpevRZcp0yqQ4TU0TrEEGirN0mvIg=
cosmossdk.io/collections v0.3.1-0.20230727092431-f0f777fa3cb7 h1:4XhcAIVBXPwCFTT9abOzZZaEadbRaVws8A6UTr2KGIE=
cosmossdk.io/collections v0.3.1-0.20230727092431-f0f777fa3cb7/go.mod h1:+KJND4gZHilxE3meopEl/Uet6IAw3PyiSbgeOrFzAZE=
cosmossdk.io/core v0.9.0 h1:30ScAOHDIUOCg1DKAwqkho9wuQJnu7GUrMcg0XLioic=
cosmossdk.io/core v0.9.0/go.mod h1:NFgl5r41Q36+RixTvyrfsS6qQ65agCbZ1FTpnN7/G1Y=
cosmossdk.io/depinject v1.0.0-alpha.3 h1:6evFIgj//Y3w09bqOUOzEpFj5tsxBqdc5CfkO7z+zfw=

View File

@ -105,7 +105,10 @@ func (app *SimApp) prepForZeroHeightGenesis(ctx sdk.Context, jailAllowedAddrs []
}
// clear validator slash events
app.DistrKeeper.DeleteAllValidatorSlashEvents(ctx)
err = app.DistrKeeper.ValidatorSlashEvents.Clear(ctx, nil)
if err != nil {
panic(err)
}
// clear validator historical rewards
err = app.DistrKeeper.ValidatorHistoricalRewards.Clear(ctx, nil)

View File

@ -236,7 +236,12 @@ func TestGRPCValidatorSlashes(t *testing.T) {
}
for i, slash := range slashes {
assert.NilError(t, f.distrKeeper.SetValidatorSlashEvent(f.sdkCtx, f.valAddr, uint64(i+2), 0, slash))
err := f.distrKeeper.ValidatorSlashEvents.Set(
f.sdkCtx,
collections.Join3(f.valAddr, uint64(i+2), uint64(0)),
slash,
)
assert.NilError(t, err)
}
var (

View File

@ -275,8 +275,8 @@ func TestMsgWithdrawDelegatorReward(t *testing.T) {
}
height := f.app.LastBlockHeight()
_, err = f.distrKeeper.GetPreviousProposerConsAddr(f.sdkCtx)
assert.Error(t, err, "previous proposer not set")
_, err = f.distrKeeper.PreviousProposer.Get(f.sdkCtx)
assert.ErrorIs(t, err, collections.ErrNotFound)
for _, tc := range testCases {
tc := tc
@ -315,7 +315,7 @@ func TestMsgWithdrawDelegatorReward(t *testing.T) {
assert.DeepEqual(t, rewards, initOutstandingRewards.Sub(curOutstandingRewards.Rewards))
}
prevProposerConsAddr, err := f.distrKeeper.GetPreviousProposerConsAddr(f.sdkCtx)
prevProposerConsAddr, err := f.distrKeeper.PreviousProposer.Get(f.sdkCtx)
assert.NilError(t, err)
assert.Assert(t, prevProposerConsAddr.Empty() == false)
assert.DeepEqual(t, prevProposerConsAddr, valConsAddr)

View File

@ -19,6 +19,15 @@ func WithCollectionPaginationPairPrefix[K1, K2 any](prefix K1) func(o *Collectio
}
}
// WithCollectionPaginationTriplePrefix applies a prefix to a collection, whose key is a collection.Triple,
// being paginated that needs prefixing.
func WithCollectionPaginationTriplePrefix[K1, K2, K3 any](prefix K1) func(o *CollectionsPaginateOptions[collections.Triple[K1, K2, K3]]) {
return func(o *CollectionsPaginateOptions[collections.Triple[K1, K2, K3]]) {
prefix := collections.TriplePrefix[K1, K2, K3](prefix)
o.Prefix = &prefix
}
}
// CollectionsPaginateOptions provides extra options for pagination in collections.
type CollectionsPaginateOptions[K any] struct {
// Prefix allows to optionally set a prefix for the pagination.

View File

@ -30,5 +30,5 @@ func BeginBlocker(ctx sdk.Context, k keeper.Keeper) error {
// record the proposer for when we payout on the next block
consAddr := sdk.ConsAddress(ctx.BlockHeader().ProposerAddress)
return k.SetPreviousProposerConsAddr(ctx, consAddr)
return k.PreviousProposer.Set(ctx, consAddr)
}

View File

@ -120,7 +120,7 @@ func (k Keeper) CalculateDelegationRewards(ctx context.Context, val stakingtypes
// for them for the stake sanity check below.
endingHeight := uint64(sdkCtx.BlockHeight())
if endingHeight > startingHeight {
k.IterateValidatorSlashEventsBetween(ctx, valAddr, startingHeight, endingHeight,
err = k.IterateValidatorSlashEventsBetween(ctx, valAddr, startingHeight, endingHeight,
func(height uint64, event types.ValidatorSlashEvent) (stop bool) {
endingPeriod := event.ValidatorPeriod
if endingPeriod > startingPeriod {
@ -138,6 +138,9 @@ func (k Keeper) CalculateDelegationRewards(ctx context.Context, val stakingtypes
return false
},
)
if err != nil {
return sdk.DecCoins{}, err
}
}
// A total stake sanity check; Recalculated final stake should be less than or

View File

@ -1,12 +1,14 @@
package keeper_test
import (
"errors"
"testing"
cmtproto "github.com/cometbft/cometbft/proto/tendermint/types"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/require"
"cosmossdk.io/collections"
"cosmossdk.io/math"
storetypes "cosmossdk.io/store/types"
@ -72,13 +74,13 @@ func TestCalculateRewardsBasic(t *testing.T) {
ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1)
// historical count should be 2 (once for validator init, once for delegation init)
require.Equal(t, uint64(2), distrKeeper.GetValidatorHistoricalReferenceCount(ctx))
require.Equal(t, 2, getValHistoricalReferenceCount(distrKeeper, ctx))
// end period
endingPeriod, _ := distrKeeper.IncrementValidatorPeriod(ctx, val)
// historical count should be 2 still
require.Equal(t, uint64(2), distrKeeper.GetValidatorHistoricalReferenceCount(ctx))
require.Equal(t, 2, getValHistoricalReferenceCount(distrKeeper, ctx))
// calculate delegation rewards
rewards, err := distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod)
@ -108,6 +110,22 @@ func TestCalculateRewardsBasic(t *testing.T) {
require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(initial / 2)}}, valCommission.Commission)
}
func getValHistoricalReferenceCount(k keeper.Keeper, ctx sdk.Context) int {
count := 0
err := k.ValidatorHistoricalRewards.Walk(
ctx, nil, func(key collections.Pair[sdk.ValAddress, uint64], rewards disttypes.ValidatorHistoricalRewards) (stop bool, err error) {
count += int(rewards.ReferenceCount)
return false, nil
},
)
if err != nil && !errors.Is(err, collections.ErrInvalidIterator) {
panic(err)
}
return count
}
func TestCalculateRewardsAfterSlash(t *testing.T) {
ctrl := gomock.NewController(t)
key := storetypes.NewKVStoreKey(disttypes.StoreKey)
@ -486,7 +504,7 @@ func TestWithdrawDelegationRewardsBasic(t *testing.T) {
require.NoError(t, distrKeeper.AllocateTokensToValidator(ctx, val, tokens))
// historical count should be 2 (initial + latest for delegation)
require.Equal(t, uint64(2), distrKeeper.GetValidatorHistoricalReferenceCount(ctx))
require.Equal(t, 2, getValHistoricalReferenceCount(distrKeeper, ctx))
// withdraw rewards (the bank keeper should be called with the right amount of tokens to transfer)
expRewards := sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, initial.QuoRaw(2))}
@ -495,7 +513,7 @@ func TestWithdrawDelegationRewardsBasic(t *testing.T) {
require.Nil(t, err)
// historical count should still be 2 (added one record, cleared one)
require.Equal(t, uint64(2), distrKeeper.GetValidatorHistoricalReferenceCount(ctx))
require.Equal(t, 2, getValHistoricalReferenceCount(distrKeeper, ctx))
// withdraw commission (the bank keeper should be called with the right amount of tokens to transfer)
expCommission := sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, initial.QuoRaw(2))}
@ -808,7 +826,7 @@ func TestCalculateRewardsMultiDelegatorMultWithdraw(t *testing.T) {
require.NoError(t, distrKeeper.AllocateTokensToValidator(ctx, val, tokens))
// historical count should be 2 (validator init, delegation init)
require.Equal(t, uint64(2), distrKeeper.GetValidatorHistoricalReferenceCount(ctx))
require.Equal(t, 2, getValHistoricalReferenceCount(distrKeeper, ctx))
// second delegation
_, del2, err := distrtestutil.Delegate(
@ -830,7 +848,7 @@ func TestCalculateRewardsMultiDelegatorMultWithdraw(t *testing.T) {
require.NoError(t, err)
// historical count should be 3 (second delegation init)
require.Equal(t, uint64(3), distrKeeper.GetValidatorHistoricalReferenceCount(ctx))
require.Equal(t, 3, getValHistoricalReferenceCount(distrKeeper, ctx))
// next block
ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1)
@ -851,7 +869,7 @@ func TestCalculateRewardsMultiDelegatorMultWithdraw(t *testing.T) {
require.NoError(t, err)
// historical count should be 3 (validator init + two delegations)
require.Equal(t, uint64(3), distrKeeper.GetValidatorHistoricalReferenceCount(ctx))
require.Equal(t, 3, getValHistoricalReferenceCount(distrKeeper, ctx))
// validator withdraws commission
expCommission := sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, math.NewInt(initial))}

View File

@ -47,7 +47,7 @@ func (k Keeper) InitGenesis(ctx sdk.Context, data types.GenesisState) {
}
}
if err = k.SetPreviousProposerConsAddr(ctx, previousProposer); err != nil {
if err = k.PreviousProposer.Set(ctx, previousProposer); err != nil {
panic(err)
}
@ -112,7 +112,17 @@ func (k Keeper) InitGenesis(ctx sdk.Context, data types.GenesisState) {
if err != nil {
panic(err)
}
err = k.SetValidatorSlashEvent(ctx, valAddr, evt.Height, evt.Period, evt.ValidatorSlashEvent)
err = k.ValidatorSlashEvents.Set(
ctx,
collections.Join3(
sdk.ValAddress(valAddr),
evt.Height,
evt.Period,
),
evt.ValidatorSlashEvent,
)
if err != nil {
panic(err)
}
@ -160,7 +170,7 @@ func (k Keeper) ExportGenesis(ctx sdk.Context) *types.GenesisState {
panic(err)
}
pp, err := k.GetPreviousProposerConsAddr(ctx)
pp, err := k.PreviousProposer.Get(ctx)
if err != nil {
panic(err)
}
@ -234,17 +244,23 @@ func (k Keeper) ExportGenesis(ctx sdk.Context) *types.GenesisState {
}
slashes := make([]types.ValidatorSlashEventRecord, 0)
k.IterateValidatorSlashEvents(ctx,
func(val sdk.ValAddress, height uint64, event types.ValidatorSlashEvent) (stop bool) {
err = k.ValidatorSlashEvents.Walk(
ctx,
nil,
func(k collections.Triple[sdk.ValAddress, uint64, uint64], event types.ValidatorSlashEvent) (stop bool, err error) {
slashes = append(slashes, types.ValidatorSlashEventRecord{
ValidatorAddress: val.String(),
Height: height,
ValidatorAddress: k.K1().String(),
Height: k.K2(),
Period: event.ValidatorPeriod,
ValidatorSlashEvent: event,
})
return false
return false, nil
},
)
if err != nil && !errors.Is(err, collections.ErrInvalidIterator) {
panic(err)
}
return types.NewGenesisState(params, feePool, dwi, pp, outstanding, acc, his, cur, dels, slashes)
}

View File

@ -8,9 +8,7 @@ import (
"cosmossdk.io/collections"
"cosmossdk.io/errors"
"cosmossdk.io/store/prefix"
"github.com/cosmos/cosmos-sdk/runtime"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/query"
"github.com/cosmos/cosmos-sdk/x/distribution/types"
@ -178,28 +176,21 @@ func (k Querier) ValidatorSlashes(ctx context.Context, req *types.QueryValidator
return nil, status.Errorf(codes.InvalidArgument, "invalid validator address")
}
store := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx))
slashesStore := prefix.NewStore(store, types.GetValidatorSlashEventPrefix(valAddr))
events, pageRes, err := query.GenericFilteredPaginate(k.cdc, slashesStore, req.Pagination, func(key []byte, result *types.ValidatorSlashEvent) (*types.ValidatorSlashEvent, error) {
if result.ValidatorPeriod < req.StartingHeight || result.ValidatorPeriod > req.EndingHeight {
return nil, nil
events, pageRes, err := query.CollectionFilteredPaginate(ctx, k.ValidatorSlashEvents, req.Pagination, func(key collections.Triple[sdk.ValAddress, uint64, uint64], ev types.ValidatorSlashEvent) (include bool, err error) {
if ev.ValidatorPeriod < req.StartingHeight || ev.ValidatorPeriod > req.EndingHeight {
return false, nil
}
return result, nil
}, func() *types.ValidatorSlashEvent {
return &types.ValidatorSlashEvent{}
})
return true, nil
}, func(_ collections.Triple[sdk.ValAddress, uint64, uint64], value types.ValidatorSlashEvent) (types.ValidatorSlashEvent, error) {
return value, nil
},
query.WithCollectionPaginationTriplePrefix[sdk.ValAddress, uint64, uint64](valAddr),
)
if err != nil {
return nil, err
}
slashes := []types.ValidatorSlashEvent{}
for _, event := range events {
slashes = append(slashes, *event)
}
return &types.QueryValidatorSlashesResponse{Slashes: slashes, Pagination: pageRes}, nil
return &types.QueryValidatorSlashesResponse{Slashes: events, Pagination: pageRes}, nil
}
// DelegationRewards the total rewards accrued by a delegation

View File

@ -109,7 +109,10 @@ func (h Hooks) AfterValidatorRemoved(ctx context.Context, _ sdk.ConsAddress, val
}
// clear slashes
h.k.DeleteValidatorSlashEvents(ctx, valAddr)
err = h.k.ValidatorSlashEvents.Clear(ctx, collections.NewPrefixedTripleRange[sdk.ValAddress, uint64, uint64](valAddr))
if err != nil {
return err
}
// clear historical rewards
err = h.k.ValidatorHistoricalRewards.Clear(ctx, collections.NewPrefixedPairRange[sdk.ValAddress, uint64](valAddr))

View File

@ -143,16 +143,34 @@ func ReferenceCountInvariant(k Keeper) sdk.Invariant {
}
slashCount := uint64(0)
k.IterateValidatorSlashEvents(ctx,
func(_ sdk.ValAddress, _ uint64, _ types.ValidatorSlashEvent) (stop bool) {
err = k.ValidatorSlashEvents.Walk(
ctx,
nil,
func(k collections.Triple[sdk.ValAddress, uint64, uint64], event types.ValidatorSlashEvent) (stop bool, err error) {
slashCount++
return false
})
return false, nil
},
)
if err != nil && !errors.Is(err, collections.ErrInvalidIterator) {
panic(err)
}
// one record per validator (last tracked period), one record per
// delegation (previous period), one record per slash (previous period)
expected := valCount + uint64(len(dels)) + slashCount
count := k.GetValidatorHistoricalReferenceCount(ctx)
count := uint64(0)
err = k.ValidatorHistoricalRewards.Walk(
ctx, nil, func(key collections.Pair[sdk.ValAddress, uint64], rewards types.ValidatorHistoricalRewards) (stop bool, err error) {
count += uint64(rewards.ReferenceCount)
return false, nil
},
)
if err != nil && !errors.Is(err, collections.ErrInvalidIterator) {
panic(err)
}
broken := count != expected
return sdk.FormatInvariant(types.ModuleName, "reference count",

View File

@ -38,6 +38,8 @@ type Keeper struct {
ValidatorsAccumulatedCommission collections.Map[sdk.ValAddress, types.ValidatorAccumulatedCommission]
ValidatorOutstandingRewards collections.Map[sdk.ValAddress, types.ValidatorOutstandingRewards]
ValidatorHistoricalRewards collections.Map[collections.Pair[sdk.ValAddress, uint64], types.ValidatorHistoricalRewards]
PreviousProposer collections.Item[sdk.ConsAddress]
ValidatorSlashEvents collections.Map[collections.Triple[sdk.ValAddress, uint64, uint64], types.ValidatorSlashEvent] // key is valAddr, height, period
feeCollectorName string // name of the FeeCollector ModuleAccount
}
@ -107,6 +109,14 @@ func NewKeeper(
collections.PairKeyCodec(sdk.LengthPrefixedAddressKey(sdk.ValAddressKey), sdk.LEUint64Key), // nolint: staticcheck // sdk.LengthPrefixedAddressKey is needed to retain state compatibility
codec.CollValue[types.ValidatorHistoricalRewards](cdc),
),
PreviousProposer: collections.NewItem(sb, types.ProposerKey, "previous_proposer", collcodec.KeyToValueCodec(sdk.ConsAddressKey)),
ValidatorSlashEvents: collections.NewMap(
sb,
types.ValidatorSlashEventPrefix,
"validator_slash_events",
collections.TripleKeyCodec(sdk.LengthPrefixedAddressKey(sdk.ValAddressKey), collections.Uint64Key, collections.Uint64Key), // nolint: staticcheck // sdk.LengthPrefixedAddressKey is needed to retain state compatibility
codec.CollValue[types.ValidatorSlashEvent](cdc),
),
}
schema, err := sb.Build()

View File

@ -5,6 +5,7 @@ import (
"github.com/cosmos/cosmos-sdk/x/distribution/exported"
v2 "github.com/cosmos/cosmos-sdk/x/distribution/migrations/v2"
v3 "github.com/cosmos/cosmos-sdk/x/distribution/migrations/v3"
v4 "github.com/cosmos/cosmos-sdk/x/distribution/migrations/v4"
)
// Migrator is a struct for handling in-place store migrations.
@ -30,3 +31,7 @@ func (m Migrator) Migrate1to2(ctx sdk.Context) error {
func (m Migrator) Migrate2to3(ctx sdk.Context) error {
return v3.MigrateStore(ctx, m.keeper.storeService, m.legacySubspace, m.keeper.cdc)
}
func (m Migrator) Migrate3to4(ctx sdk.Context) error {
return v4.MigrateStore(ctx, m.keeper.storeService, m.keeper.cdc)
}

View File

@ -3,13 +3,10 @@ package keeper
import (
"context"
"errors"
gogotypes "github.com/cosmos/gogoproto/types"
"math"
"cosmossdk.io/collections"
storetypes "cosmossdk.io/store/types"
"github.com/cosmos/cosmos-sdk/runtime"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/distribution/types"
)
@ -23,130 +20,25 @@ func (k Keeper) GetDelegatorWithdrawAddr(ctx context.Context, delAddr sdk.AccAdd
return addr, err
}
// GetPreviousProposerConsAddr returns the proposer consensus address for the
// current block.
func (k Keeper) GetPreviousProposerConsAddr(ctx context.Context) (sdk.ConsAddress, error) {
store := k.storeService.OpenKVStore(ctx)
bz, err := store.Get(types.ProposerKey)
if err != nil {
return nil, err
}
if bz == nil {
return nil, errors.New("previous proposer not set")
}
addrValue := gogotypes.BytesValue{}
err = k.cdc.Unmarshal(bz, &addrValue)
if err != nil {
return nil, err
}
return addrValue.GetValue(), nil
}
// set the proposer public key for this block
func (k Keeper) SetPreviousProposerConsAddr(ctx context.Context, consAddr sdk.ConsAddress) error {
store := k.storeService.OpenKVStore(ctx)
bz := k.cdc.MustMarshal(&gogotypes.BytesValue{Value: consAddr})
return store.Set(types.ProposerKey, bz)
}
// historical reference count (used for testcases)
func (k Keeper) GetValidatorHistoricalReferenceCount(ctx context.Context) (count uint64) {
store := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx))
iter := storetypes.KVStorePrefixIterator(store, types.ValidatorHistoricalRewardsPrefix)
defer iter.Close()
for ; iter.Valid(); iter.Next() {
var rewards types.ValidatorHistoricalRewards
k.cdc.MustUnmarshal(iter.Value(), &rewards)
count += uint64(rewards.ReferenceCount)
}
return
}
// get slash event for height
func (k Keeper) GetValidatorSlashEvent(ctx context.Context, val sdk.ValAddress, height, period uint64) (event types.ValidatorSlashEvent, found bool, err error) {
store := k.storeService.OpenKVStore(ctx)
b, err := store.Get(types.GetValidatorSlashEventKey(val, height, period))
if err != nil {
return types.ValidatorSlashEvent{}, false, err
}
if b == nil {
return types.ValidatorSlashEvent{}, false, nil
}
err = k.cdc.Unmarshal(b, &event)
if err != nil {
return types.ValidatorSlashEvent{}, false, err
}
return event, true, nil
}
// set slash event for height
func (k Keeper) SetValidatorSlashEvent(ctx context.Context, val sdk.ValAddress, height, period uint64, event types.ValidatorSlashEvent) error {
store := k.storeService.OpenKVStore(ctx)
b, err := k.cdc.Marshal(&event)
if err != nil {
return err
}
return store.Set(types.GetValidatorSlashEventKey(val, height, period), b)
}
// iterate over slash events between heights, inclusive
func (k Keeper) IterateValidatorSlashEventsBetween(ctx context.Context, val sdk.ValAddress, startingHeight, endingHeight uint64,
handler func(height uint64, event types.ValidatorSlashEvent) (stop bool),
) {
store := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx))
iter := store.Iterator(
types.GetValidatorSlashEventKeyPrefix(val, startingHeight),
types.GetValidatorSlashEventKeyPrefix(val, endingHeight+1),
)
defer iter.Close()
for ; iter.Valid(); iter.Next() {
var event types.ValidatorSlashEvent
k.cdc.MustUnmarshal(iter.Value(), &event)
_, height := types.GetValidatorSlashEventAddressHeight(iter.Key())
if handler(height, event) {
break
) error {
rng := new(collections.Range[collections.Triple[sdk.ValAddress, uint64, uint64]]).
StartInclusive(collections.Join3(val, startingHeight, uint64(0))).
EndExclusive(collections.Join3(val, endingHeight+1, uint64(math.MaxUint64)))
err := k.ValidatorSlashEvents.Walk(ctx, rng, func(k collections.Triple[sdk.ValAddress, uint64, uint64], ev types.ValidatorSlashEvent) (stop bool, err error) {
height := k.K2()
if handler(height, ev) {
return true, nil
}
}
}
return false, nil
})
// iterate over all slash events
func (k Keeper) IterateValidatorSlashEvents(ctx context.Context, handler func(val sdk.ValAddress, height uint64, event types.ValidatorSlashEvent) (stop bool)) {
store := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx))
iter := storetypes.KVStorePrefixIterator(store, types.ValidatorSlashEventPrefix)
defer iter.Close()
for ; iter.Valid(); iter.Next() {
var event types.ValidatorSlashEvent
k.cdc.MustUnmarshal(iter.Value(), &event)
val, height := types.GetValidatorSlashEventAddressHeight(iter.Key())
if handler(val, height, event) {
break
}
if err != nil && !errors.Is(err, collections.ErrInvalidIterator) {
return err
}
}
// delete slash events for a particular validator
func (k Keeper) DeleteValidatorSlashEvents(ctx context.Context, val sdk.ValAddress) {
store := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx))
iter := storetypes.KVStorePrefixIterator(store, types.GetValidatorSlashEventPrefix(val))
defer iter.Close()
for ; iter.Valid(); iter.Next() {
store.Delete(iter.Key())
}
}
// delete all slash events
func (k Keeper) DeleteAllValidatorSlashEvents(ctx context.Context) {
store := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx))
iter := storetypes.KVStorePrefixIterator(store, types.ValidatorSlashEventPrefix)
defer iter.Close()
for ; iter.Valid(); iter.Next() {
store.Delete(iter.Key())
}
return nil
}

View File

@ -167,5 +167,13 @@ func (k Keeper) updateValidatorSlashFraction(ctx context.Context, valAddr sdk.Va
slashEvent := types.NewValidatorSlashEvent(newPeriod, fraction)
height := uint64(sdkCtx.BlockHeight())
return k.SetValidatorSlashEvent(ctx, valAddr, height, newPeriod, slashEvent)
return k.ValidatorSlashEvents.Set(
ctx,
collections.Join3[sdk.ValAddress, uint64, uint64](
valAddr,
height,
newPeriod,
),
slashEvent,
)
}

View File

@ -0,0 +1,77 @@
package v4
import (
"context"
"errors"
gogotypes "github.com/cosmos/gogoproto/types"
"cosmossdk.io/collections"
collcodec "cosmossdk.io/collections/codec"
"cosmossdk.io/core/store"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
)
var (
OldProposerKey = []byte{0x01}
NewProposerKey = collections.NewPrefix(1)
)
func MigrateStore(ctx sdk.Context, storeService store.KVStoreService, cdc codec.BinaryCodec) error {
store := storeService.OpenKVStore(ctx)
bz, err := store.Get(OldProposerKey)
if err != nil {
return err
}
if bz == nil {
// previous proposer not set, nothing to do
return nil
}
addrValue := gogotypes.BytesValue{}
err = cdc.Unmarshal(bz, &addrValue)
if err != nil {
return err
}
sb := collections.NewSchemaBuilder(storeService)
prevProposer := collections.NewItem(sb, NewProposerKey, "previous_proposer", collcodec.KeyToValueCodec(sdk.ConsAddressKey))
_, err = sb.Build()
if err != nil {
return err
}
return prevProposer.Set(ctx, addrValue.GetValue())
}
// GetPreviousProposerConsAddr returns the proposer consensus address for the
// current block.
func GetPreviousProposerConsAddr(ctx context.Context, storeService store.KVStoreService, cdc codec.BinaryCodec) (sdk.ConsAddress, error) {
store := storeService.OpenKVStore(ctx)
bz, err := store.Get(OldProposerKey)
if err != nil {
return nil, err
}
if bz == nil {
return nil, errors.New("previous proposer not set")
}
addrValue := gogotypes.BytesValue{}
err = cdc.Unmarshal(bz, &addrValue)
if err != nil {
return nil, err
}
return addrValue.GetValue(), nil
}
// set the proposer public key for this block
func SetPreviousProposerConsAddr(ctx context.Context, storeService store.KVStoreService, cdc codec.BinaryCodec, consAddr sdk.ConsAddress) error {
store := storeService.OpenKVStore(ctx)
bz := cdc.MustMarshal(&gogotypes.BytesValue{Value: consAddr})
return store.Set(OldProposerKey, bz)
}

View File

@ -0,0 +1,50 @@
package v4_test
import (
"testing"
"github.com/stretchr/testify/require"
"cosmossdk.io/collections"
collcodec "cosmossdk.io/collections/codec"
storetypes "cosmossdk.io/store/types"
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
"github.com/cosmos/cosmos-sdk/runtime"
"github.com/cosmos/cosmos-sdk/testutil"
sdk "github.com/cosmos/cosmos-sdk/types"
moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil"
"github.com/cosmos/cosmos-sdk/x/distribution"
v4 "github.com/cosmos/cosmos-sdk/x/distribution/migrations/v4"
)
func TestMigration(t *testing.T) {
cdc := moduletestutil.MakeTestEncodingConfig(distribution.AppModuleBasic{}).Codec
storeKey := storetypes.NewKVStoreKey("distribution")
storeService := runtime.NewKVStoreService(storeKey)
tKey := storetypes.NewTransientStoreKey("transient_test")
ctx := testutil.DefaultContext(storeKey, tKey)
addr1 := secp256k1.GenPrivKey().PubKey().Address()
consAddr1 := sdk.ConsAddress(addr1)
err := v4.SetPreviousProposerConsAddr(ctx, storeService, cdc, consAddr1)
require.NoError(t, err)
gotAddr, err := v4.GetPreviousProposerConsAddr(ctx, storeService, cdc)
require.NoError(t, err)
require.Equal(t, consAddr1, gotAddr)
err = v4.MigrateStore(ctx, storeService, cdc)
require.NoError(t, err)
sb := collections.NewSchemaBuilder(storeService)
prevProposer := collections.NewItem(sb, v4.NewProposerKey, "previous_proposer", collcodec.KeyToValueCodec(sdk.ConsAddressKey))
_, err = sb.Build()
require.NoError(t, err)
newAddr, err := prevProposer.Get(ctx)
require.NoError(t, err)
require.Equal(t, consAddr1, newAddr)
}

View File

@ -149,6 +149,10 @@ func (am AppModule) RegisterServices(cfg module.Configurator) {
if err := cfg.RegisterMigration(types.ModuleName, 2, m.Migrate2to3); err != nil {
panic(fmt.Sprintf("failed to migrate x/%s from version 2 to 3: %v", types.ModuleName, err))
}
if err := cfg.RegisterMigration(types.ModuleName, 3, m.Migrate3to4); err != nil {
panic(fmt.Sprintf("failed to migrate x/%s from version 3 to 4: %v", types.ModuleName, err))
}
}
// InitGenesis performs genesis initialization for the distribution module. It returns

View File

@ -43,19 +43,19 @@ const (
//
// - 0x07<valAddrLen (1 Byte)><valAddr_Bytes>: ValidatorCurrentCommission
//
// - 0x08<valAddrLen (1 Byte)><valAddr_Bytes><height>: ValidatorSlashEvent
// - 0x08<valAddrLen (1 Byte)><valAddr_Bytes><height><period>: ValidatorSlashEvent
//
// - 0x09: Params
var (
FeePoolKey = collections.NewPrefix(0) // key for global distribution state
ProposerKey = []byte{0x01} // key for the proposer operator address
ProposerKey = collections.NewPrefix(1) // key for the proposer operator address
ValidatorOutstandingRewardsPrefix = collections.NewPrefix(2) // key for outstanding rewards
DelegatorWithdrawAddrPrefix = collections.NewPrefix(3) // key for delegator withdraw address
DelegatorStartingInfoPrefix = collections.NewPrefix(4) // key for delegator starting info
ValidatorHistoricalRewardsPrefix = collections.NewPrefix(5) // key for historical validators rewards / stake
ValidatorCurrentRewardsPrefix = collections.NewPrefix(6) // key for current validator rewards
ValidatorAccumulatedCommissionPrefix = collections.NewPrefix(7) // key for accumulated validator commission
ValidatorSlashEventPrefix = []byte{0x08} // key for validator slash fraction
ValidatorSlashEventPrefix = collections.NewPrefix(8) // key for validator slash fraction
ParamsKey = collections.NewPrefix(9) // key for distribution module params
)