x{stake,slash,gov,distrib} In-place Store Migrations (#8504)
* Add 1st version of migrate * Put migration logic into Configurator * add test to bank store migration * add test for configurator * Error if no migration found * Remove RunMigrations from Configurator interface * Update spec * Rename folders * copy-paste from keys.go * Fix nil map * rename function * Update simapp/app.go Co-authored-by: Robert Zaremba <robert@zaremba.ch> * Update simapp/app_test.go Co-authored-by: Robert Zaremba <robert@zaremba.ch> * Adderss reviews * Fix tests * Update testutil/context.go Co-authored-by: Robert Zaremba <robert@zaremba.ch> * Update docs for ConsensusVersion * Rename to forVersion * Fix tests * Check error early * Return 1 for intiial version * Use MigrationKeeper * Fix test * Revert adding marshaler to Configurator * Godoc updates * Update docs * Add distrib legacy folder * Add tests for distrib migration * Add gov migrations * Copy paste whole keys file * Add gov migrations * Add staking * Fix staking tests * Update spec and module.go * Update to latest changes * Update migration scripts * capability to 1 * Fix tests * Add package godoc * Remove whitespace * Remove global * Use Migrator * Remove 042 keys files * Fix build * Unlambda * Rename to Migrate1to2 Co-authored-by: Robert Zaremba <robert@zaremba.ch>
This commit is contained in:
parent
eeb3eabdc6
commit
ba74a7c737
|
@ -11,8 +11,27 @@ import (
|
||||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||||
dbm "github.com/tendermint/tm-db"
|
dbm "github.com/tendermint/tm-db"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
"github.com/cosmos/cosmos-sdk/types/module"
|
"github.com/cosmos/cosmos-sdk/types/module"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/auth/vesting"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/authz"
|
||||||
|
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/capability"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/crisis"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/distribution"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/evidence"
|
||||||
|
feegrant "github.com/cosmos/cosmos-sdk/x/feegrant"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/genutil"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/gov"
|
||||||
|
transfer "github.com/cosmos/cosmos-sdk/x/ibc/applications/transfer"
|
||||||
|
ibc "github.com/cosmos/cosmos-sdk/x/ibc/core"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/mint"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/params"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/slashing"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/staking"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/upgrade"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSimAppExportAndBlockedAddrs(t *testing.T) {
|
func TestSimAppExportAndBlockedAddrs(t *testing.T) {
|
||||||
|
@ -52,11 +71,33 @@ func TestGetMaccPerms(t *testing.T) {
|
||||||
func TestRunMigrations(t *testing.T) {
|
func TestRunMigrations(t *testing.T) {
|
||||||
db := dbm.NewMemDB()
|
db := dbm.NewMemDB()
|
||||||
encCfg := MakeTestEncodingConfig()
|
encCfg := MakeTestEncodingConfig()
|
||||||
app := NewSimApp(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, nil, true, map[int64]bool{}, DefaultNodeHome, 0, encCfg, EmptyAppOptions{})
|
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout))
|
||||||
|
app := NewSimApp(logger, db, nil, true, map[int64]bool{}, DefaultNodeHome, 0, encCfg, EmptyAppOptions{})
|
||||||
|
|
||||||
// Create a new configurator for the purpose of this test.
|
// Create a new baseapp and configurator for the purpose of this test.
|
||||||
|
bApp := baseapp.NewBaseApp(appName, logger, db, encCfg.TxConfig.TxDecoder())
|
||||||
|
bApp.SetCommitMultiStoreTracer(nil)
|
||||||
|
bApp.SetInterfaceRegistry(encCfg.InterfaceRegistry)
|
||||||
|
app.BaseApp = bApp
|
||||||
app.configurator = module.NewConfigurator(app.MsgServiceRouter(), app.GRPCQueryRouter())
|
app.configurator = module.NewConfigurator(app.MsgServiceRouter(), app.GRPCQueryRouter())
|
||||||
|
|
||||||
|
// We register all modules on the Configurator, except x/bank. x/bank will
|
||||||
|
// serve as the test subject on which we run the migration tests.
|
||||||
|
//
|
||||||
|
// The loop below is the same as calling `RegisterServices` on
|
||||||
|
// ModuleManager, except that we skip x/bank.
|
||||||
|
for _, module := range app.mm.Modules {
|
||||||
|
if module.Name() == banktypes.ModuleName {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
module.RegisterServices(app.configurator)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the chain
|
||||||
|
app.InitChain(abci.RequestInitChain{})
|
||||||
|
app.Commit()
|
||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
moduleName string
|
moduleName string
|
||||||
|
@ -115,12 +156,30 @@ func TestRunMigrations(t *testing.T) {
|
||||||
}
|
}
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Run migrations only for bank. That's why we put the initial
|
||||||
|
// version for bank as 1, and for all other modules, we put as
|
||||||
|
// their latest ConsensusVersion.
|
||||||
err = app.RunMigrations(
|
err = app.RunMigrations(
|
||||||
app.NewContext(true, tmproto.Header{Height: app.LastBlockHeight()}),
|
app.NewContext(true, tmproto.Header{Height: app.LastBlockHeight()}),
|
||||||
module.MigrationMap{
|
module.MigrationMap{
|
||||||
"auth": 1, "authz": 1, "bank": 1, "staking": 1, "mint": 1, "distribution": 1,
|
"bank": 1,
|
||||||
"slashing": 1, "gov": 1, "params": 1, "ibc": 1, "upgrade": 1, "vesting": 1,
|
"auth": auth.AppModule{}.ConsensusVersion(),
|
||||||
"feegrant": 1, "transfer": 1, "evidence": 1, "crisis": 1, "genutil": 1, "capability": 1,
|
"authz": authz.AppModule{}.ConsensusVersion(),
|
||||||
|
"staking": staking.AppModule{}.ConsensusVersion(),
|
||||||
|
"mint": mint.AppModule{}.ConsensusVersion(),
|
||||||
|
"distribution": distribution.AppModule{}.ConsensusVersion(),
|
||||||
|
"slashing": slashing.AppModule{}.ConsensusVersion(),
|
||||||
|
"gov": gov.AppModule{}.ConsensusVersion(),
|
||||||
|
"params": params.AppModule{}.ConsensusVersion(),
|
||||||
|
"ibc": ibc.AppModule{}.ConsensusVersion(),
|
||||||
|
"upgrade": upgrade.AppModule{}.ConsensusVersion(),
|
||||||
|
"vesting": vesting.AppModule{}.ConsensusVersion(),
|
||||||
|
"feegrant": feegrant.AppModule{}.ConsensusVersion(),
|
||||||
|
"transfer": transfer.AppModule{}.ConsensusVersion(),
|
||||||
|
"evidence": evidence.AppModule{}.ConsensusVersion(),
|
||||||
|
"crisis": crisis.AppModule{}.ConsensusVersion(),
|
||||||
|
"genutil": genutil.AppModule{}.ConsensusVersion(),
|
||||||
|
"capability": capability.AppModule{}.ConsensusVersion(),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
if tc.expRunErr {
|
if tc.expRunErr {
|
||||||
|
|
|
@ -331,7 +331,7 @@ func (m *Manager) ExportGenesis(ctx sdk.Context, cdc codec.JSONMarshaler) map[st
|
||||||
}
|
}
|
||||||
|
|
||||||
// MigrationHandler is the migration function that each module registers.
|
// MigrationHandler is the migration function that each module registers.
|
||||||
type MigrationHandler func(store sdk.Context) error
|
type MigrationHandler func(sdk.Context) error
|
||||||
|
|
||||||
// MigrationMap is a map of moduleName -> version, where version denotes the
|
// MigrationMap is a map of moduleName -> version, where version denotes the
|
||||||
// version from which we should perform the migration for each module.
|
// version from which we should perform the migration for each module.
|
||||||
|
|
|
@ -5,16 +5,17 @@ import (
|
||||||
v042 "github.com/cosmos/cosmos-sdk/x/bank/legacy/v042"
|
v042 "github.com/cosmos/cosmos-sdk/x/bank/legacy/v042"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MigrationKeeper is an interface that the keeper implements for handling
|
// Migrator is a struct for handling in-place store migrations.
|
||||||
// in-place store migrations.
|
type Migrator struct {
|
||||||
type MigrationKeeper interface {
|
keeper BaseKeeper
|
||||||
// Migrate1 migrates the store from version 1 to 2.
|
|
||||||
Migrate1(ctx sdk.Context) error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ MigrationKeeper = (*BaseKeeper)(nil)
|
// NewMigrator returns a new Migrator.
|
||||||
|
func NewMigrator(keeper BaseKeeper) Migrator {
|
||||||
// Migrate1 implements MigrationKeeper.Migrate1 method.
|
return Migrator{keeper: keeper}
|
||||||
func (keeper BaseKeeper) Migrate1(ctx sdk.Context) error {
|
}
|
||||||
return v042.MigrateStore(ctx, keeper.storeKey)
|
|
||||||
|
// Migrate1to2 migrates from version 1 to 2.
|
||||||
|
func (m Migrator) Migrate1to2(ctx sdk.Context) error {
|
||||||
|
return v042.MigrateStore(ctx, m.keeper.storeKey)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// Package v040 is copy-pasted from:
|
||||||
|
// https://github.com/cosmos/cosmos-sdk/blob/v0.41.0/x/bank/types/key.go
|
||||||
package v040
|
package v040
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
@ -1,50 +0,0 @@
|
||||||
package v042
|
|
||||||
|
|
||||||
import (
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
||||||
"github.com/cosmos/cosmos-sdk/types/address"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// ModuleName defines the module name
|
|
||||||
ModuleName = "bank"
|
|
||||||
|
|
||||||
// StoreKey defines the primary module store key
|
|
||||||
StoreKey = ModuleName
|
|
||||||
|
|
||||||
// RouterKey defines the module's message routing key
|
|
||||||
RouterKey = ModuleName
|
|
||||||
|
|
||||||
// QuerierRoute defines the module's query routing key
|
|
||||||
QuerierRoute = ModuleName
|
|
||||||
)
|
|
||||||
|
|
||||||
// KVStore keys
|
|
||||||
var (
|
|
||||||
// BalancesPrefix is the for the account balances store. We use a byte
|
|
||||||
// (instead of say `[]]byte("balances")` to save some disk space).
|
|
||||||
BalancesPrefix = []byte{0x02}
|
|
||||||
SupplyKey = []byte{0x00}
|
|
||||||
DenomMetadataPrefix = []byte{0x1}
|
|
||||||
)
|
|
||||||
|
|
||||||
// DenomMetadataKey returns the denomination metadata key.
|
|
||||||
func DenomMetadataKey(denom string) []byte {
|
|
||||||
d := []byte(denom)
|
|
||||||
return append(DenomMetadataPrefix, d...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddressFromBalancesStore returns an account address from a balances prefix
|
|
||||||
// store. The key must not contain the perfix BalancesPrefix as the prefix store
|
|
||||||
// iterator discards the actual prefix.
|
|
||||||
func AddressFromBalancesStore(key []byte) sdk.AccAddress {
|
|
||||||
addrLen := key[0]
|
|
||||||
addr := key[1 : addrLen+1]
|
|
||||||
|
|
||||||
return sdk.AccAddress(addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateAccountBalancesPrefix creates the prefix for an account's balances.
|
|
||||||
func CreateAccountBalancesPrefix(addr []byte) []byte {
|
|
||||||
return append(BalancesPrefix, address.MustLengthPrefix(addr)...)
|
|
||||||
}
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
v040auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v040"
|
v040auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v040"
|
||||||
v040bank "github.com/cosmos/cosmos-sdk/x/bank/legacy/v040"
|
v040bank "github.com/cosmos/cosmos-sdk/x/bank/legacy/v040"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/bank/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MigrateStore performs in-place store migrations from v0.40 to v0.42. The
|
// MigrateStore performs in-place store migrations from v0.40 to v0.42. The
|
||||||
|
@ -27,7 +28,7 @@ func MigrateStore(ctx sdk.Context, storeKey sdk.StoreKey) error {
|
||||||
for ; oldStoreIter.Valid(); oldStoreIter.Next() {
|
for ; oldStoreIter.Valid(); oldStoreIter.Next() {
|
||||||
addr := v040bank.AddressFromBalancesStore(oldStoreIter.Key())
|
addr := v040bank.AddressFromBalancesStore(oldStoreIter.Key())
|
||||||
denom := oldStoreIter.Key()[v040auth.AddrLen:]
|
denom := oldStoreIter.Key()[v040auth.AddrLen:]
|
||||||
newStoreKey := append(CreateAccountBalancesPrefix(addr), denom...)
|
newStoreKey := append(types.CreateAccountBalancesPrefix(addr), denom...)
|
||||||
|
|
||||||
// Set new key on store. Values don't change.
|
// Set new key on store. Values don't change.
|
||||||
store.Set(newStoreKey, oldStoreIter.Value())
|
store.Set(newStoreKey, oldStoreIter.Value())
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
v040bank "github.com/cosmos/cosmos-sdk/x/bank/legacy/v040"
|
v040bank "github.com/cosmos/cosmos-sdk/x/bank/legacy/v040"
|
||||||
v042bank "github.com/cosmos/cosmos-sdk/x/bank/legacy/v042"
|
v042bank "github.com/cosmos/cosmos-sdk/x/bank/legacy/v042"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/bank/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestStoreMigration(t *testing.T) {
|
func TestStoreMigration(t *testing.T) {
|
||||||
|
@ -27,7 +28,7 @@ func TestStoreMigration(t *testing.T) {
|
||||||
err := v042bank.MigrateStore(ctx, bankKey)
|
err := v042bank.MigrateStore(ctx, bankKey)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
newKey := append(v042bank.CreateAccountBalancesPrefix(addr), denom...)
|
newKey := append(types.CreateAccountBalancesPrefix(addr), denom...)
|
||||||
// -7 because we replaced "balances" with 0x02,
|
// -7 because we replaced "balances" with 0x02,
|
||||||
// +1 because we added length-prefix to address.
|
// +1 because we added length-prefix to address.
|
||||||
require.Equal(t, len(oldKey)-7+1, len(newKey))
|
require.Equal(t, len(oldKey)-7+1, len(newKey))
|
||||||
|
|
|
@ -98,9 +98,9 @@ type AppModule struct {
|
||||||
func (am AppModule) RegisterServices(cfg module.Configurator) {
|
func (am AppModule) RegisterServices(cfg module.Configurator) {
|
||||||
types.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServerImpl(am.keeper))
|
types.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServerImpl(am.keeper))
|
||||||
types.RegisterQueryServer(cfg.QueryServer(), am.keeper)
|
types.RegisterQueryServer(cfg.QueryServer(), am.keeper)
|
||||||
cfg.RegisterMigration(types.ModuleName, 0, func(ctx sdk.Context) error {
|
|
||||||
return am.keeper.(keeper.MigrationKeeper).Migrate1(ctx)
|
m := keeper.NewMigrator(am.keeper.(keeper.BaseKeeper))
|
||||||
})
|
cfg.RegisterMigration(types.ModuleName, 1, m.Migrate1to2)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewAppModule creates a new AppModule object
|
// NewAppModule creates a new AppModule object
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
package keeper
|
||||||
|
|
||||||
|
import (
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
v042 "github.com/cosmos/cosmos-sdk/x/distribution/legacy/v042"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Migrator is a struct for handling in-place store migrations.
|
||||||
|
type Migrator struct {
|
||||||
|
keeper Keeper
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMigrator returns a new Migrator.
|
||||||
|
func NewMigrator(keeper Keeper) Migrator {
|
||||||
|
return Migrator{keeper: keeper}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Migrate1to2 migrates from version 1 to 2.
|
||||||
|
func (m Migrator) Migrate1to2(ctx sdk.Context) error {
|
||||||
|
return v042.MigrateStore(ctx, m.keeper.storeKey)
|
||||||
|
}
|
|
@ -0,0 +1,196 @@
|
||||||
|
// Package v040 is copy-pasted from:
|
||||||
|
// https://github.com/cosmos/cosmos-sdk/blob/v0.41.0/x/distribution/types/keys.go
|
||||||
|
package v040
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
v040auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v040"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// ModuleName is the module name constant used in many places
|
||||||
|
ModuleName = "distribution"
|
||||||
|
|
||||||
|
// StoreKey is the store key string for distribution
|
||||||
|
StoreKey = ModuleName
|
||||||
|
|
||||||
|
// RouterKey is the message route for distribution
|
||||||
|
RouterKey = ModuleName
|
||||||
|
|
||||||
|
// QuerierRoute is the querier route for distribution
|
||||||
|
QuerierRoute = ModuleName
|
||||||
|
)
|
||||||
|
|
||||||
|
// Keys for distribution store
|
||||||
|
// Items are stored with the following key: values
|
||||||
|
//
|
||||||
|
// - 0x00<proposalID_Bytes>: FeePol
|
||||||
|
//
|
||||||
|
// - 0x01: sdk.ConsAddress
|
||||||
|
//
|
||||||
|
// - 0x02<valAddr_Bytes>: ValidatorOutstandingRewards
|
||||||
|
//
|
||||||
|
// - 0x03<accAddr_Bytes>: sdk.AccAddress
|
||||||
|
//
|
||||||
|
// - 0x04<valAddr_Bytes><accAddr_Bytes>: DelegatorStartingInfo
|
||||||
|
//
|
||||||
|
// - 0x05<valAddr_Bytes><period_Bytes>: ValidatorHistoricalRewards
|
||||||
|
//
|
||||||
|
// - 0x06<valAddr_Bytes>: ValidatorCurrentRewards
|
||||||
|
//
|
||||||
|
// - 0x07<valAddr_Bytes>: ValidatorCurrentRewards
|
||||||
|
//
|
||||||
|
// - 0x08<valAddr_Bytes><height>: ValidatorSlashEvent
|
||||||
|
var (
|
||||||
|
FeePoolKey = []byte{0x00} // key for global distribution state
|
||||||
|
ProposerKey = []byte{0x01} // key for the proposer operator address
|
||||||
|
ValidatorOutstandingRewardsPrefix = []byte{0x02} // key for outstanding rewards
|
||||||
|
|
||||||
|
DelegatorWithdrawAddrPrefix = []byte{0x03} // key for delegator withdraw address
|
||||||
|
DelegatorStartingInfoPrefix = []byte{0x04} // key for delegator starting info
|
||||||
|
ValidatorHistoricalRewardsPrefix = []byte{0x05} // key for historical validators rewards / stake
|
||||||
|
ValidatorCurrentRewardsPrefix = []byte{0x06} // key for current validator rewards
|
||||||
|
ValidatorAccumulatedCommissionPrefix = []byte{0x07} // key for accumulated validator commission
|
||||||
|
ValidatorSlashEventPrefix = []byte{0x08} // key for validator slash fraction
|
||||||
|
)
|
||||||
|
|
||||||
|
// gets an address from a validator's outstanding rewards key
|
||||||
|
func GetValidatorOutstandingRewardsAddress(key []byte) (valAddr sdk.ValAddress) {
|
||||||
|
addr := key[1:]
|
||||||
|
if len(addr) != v040auth.AddrLen {
|
||||||
|
panic("unexpected key length")
|
||||||
|
}
|
||||||
|
return sdk.ValAddress(addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// gets an address from a delegator's withdraw info key
|
||||||
|
func GetDelegatorWithdrawInfoAddress(key []byte) (delAddr sdk.AccAddress) {
|
||||||
|
addr := key[1:]
|
||||||
|
if len(addr) != v040auth.AddrLen {
|
||||||
|
panic("unexpected key length")
|
||||||
|
}
|
||||||
|
return sdk.AccAddress(addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// gets the addresses from a delegator starting info key
|
||||||
|
func GetDelegatorStartingInfoAddresses(key []byte) (valAddr sdk.ValAddress, delAddr sdk.AccAddress) {
|
||||||
|
addr := key[1 : 1+v040auth.AddrLen]
|
||||||
|
if len(addr) != v040auth.AddrLen {
|
||||||
|
panic("unexpected key length")
|
||||||
|
}
|
||||||
|
valAddr = sdk.ValAddress(addr)
|
||||||
|
addr = key[1+v040auth.AddrLen:]
|
||||||
|
if len(addr) != v040auth.AddrLen {
|
||||||
|
panic("unexpected key length")
|
||||||
|
}
|
||||||
|
delAddr = sdk.AccAddress(addr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// gets the address & period from a validator's historical rewards key
|
||||||
|
func GetValidatorHistoricalRewardsAddressPeriod(key []byte) (valAddr sdk.ValAddress, period uint64) {
|
||||||
|
addr := key[1 : 1+v040auth.AddrLen]
|
||||||
|
if len(addr) != v040auth.AddrLen {
|
||||||
|
panic("unexpected key length")
|
||||||
|
}
|
||||||
|
valAddr = sdk.ValAddress(addr)
|
||||||
|
b := key[1+v040auth.AddrLen:]
|
||||||
|
if len(b) != 8 {
|
||||||
|
panic("unexpected key length")
|
||||||
|
}
|
||||||
|
period = binary.LittleEndian.Uint64(b)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// gets the address from a validator's current rewards key
|
||||||
|
func GetValidatorCurrentRewardsAddress(key []byte) (valAddr sdk.ValAddress) {
|
||||||
|
addr := key[1:]
|
||||||
|
if len(addr) != v040auth.AddrLen {
|
||||||
|
panic("unexpected key length")
|
||||||
|
}
|
||||||
|
return sdk.ValAddress(addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// gets the address from a validator's accumulated commission key
|
||||||
|
func GetValidatorAccumulatedCommissionAddress(key []byte) (valAddr sdk.ValAddress) {
|
||||||
|
addr := key[1:]
|
||||||
|
if len(addr) != v040auth.AddrLen {
|
||||||
|
panic("unexpected key length")
|
||||||
|
}
|
||||||
|
return sdk.ValAddress(addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// gets the height from a validator's slash event key
|
||||||
|
func GetValidatorSlashEventAddressHeight(key []byte) (valAddr sdk.ValAddress, height uint64) {
|
||||||
|
addr := key[1 : 1+v040auth.AddrLen]
|
||||||
|
if len(addr) != v040auth.AddrLen {
|
||||||
|
panic("unexpected key length")
|
||||||
|
}
|
||||||
|
valAddr = sdk.ValAddress(addr)
|
||||||
|
startB := 1 + v040auth.AddrLen
|
||||||
|
b := key[startB : startB+8] // the next 8 bytes represent the height
|
||||||
|
height = binary.BigEndian.Uint64(b)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// gets the outstanding rewards key for a validator
|
||||||
|
func GetValidatorOutstandingRewardsKey(valAddr sdk.ValAddress) []byte {
|
||||||
|
return append(ValidatorOutstandingRewardsPrefix, valAddr.Bytes()...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// gets the key for a delegator's withdraw addr
|
||||||
|
func GetDelegatorWithdrawAddrKey(delAddr sdk.AccAddress) []byte {
|
||||||
|
return append(DelegatorWithdrawAddrPrefix, delAddr.Bytes()...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// gets the key for a delegator's starting info
|
||||||
|
func GetDelegatorStartingInfoKey(v sdk.ValAddress, d sdk.AccAddress) []byte {
|
||||||
|
return append(append(DelegatorStartingInfoPrefix, v.Bytes()...), d.Bytes()...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// gets the prefix key for a validator's historical rewards
|
||||||
|
func GetValidatorHistoricalRewardsPrefix(v sdk.ValAddress) []byte {
|
||||||
|
return append(ValidatorHistoricalRewardsPrefix, v.Bytes()...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// gets the key for a validator's historical rewards
|
||||||
|
func GetValidatorHistoricalRewardsKey(v sdk.ValAddress, k uint64) []byte {
|
||||||
|
b := make([]byte, 8)
|
||||||
|
binary.LittleEndian.PutUint64(b, k)
|
||||||
|
return append(append(ValidatorHistoricalRewardsPrefix, v.Bytes()...), b...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// gets the key for a validator's current rewards
|
||||||
|
func GetValidatorCurrentRewardsKey(v sdk.ValAddress) []byte {
|
||||||
|
return append(ValidatorCurrentRewardsPrefix, v.Bytes()...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// gets the key for a validator's current commission
|
||||||
|
func GetValidatorAccumulatedCommissionKey(v sdk.ValAddress) []byte {
|
||||||
|
return append(ValidatorAccumulatedCommissionPrefix, v.Bytes()...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// gets the prefix key for a validator's slash fractions
|
||||||
|
func GetValidatorSlashEventPrefix(v sdk.ValAddress) []byte {
|
||||||
|
return append(ValidatorSlashEventPrefix, v.Bytes()...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// gets the prefix key for a validator's slash fraction (ValidatorSlashEventPrefix + height)
|
||||||
|
func GetValidatorSlashEventKeyPrefix(v sdk.ValAddress, height uint64) []byte {
|
||||||
|
heightBz := make([]byte, 8)
|
||||||
|
binary.BigEndian.PutUint64(heightBz, height)
|
||||||
|
return append(
|
||||||
|
ValidatorSlashEventPrefix,
|
||||||
|
append(v.Bytes(), heightBz...)...,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// gets the key for a validator's slash fraction
|
||||||
|
func GetValidatorSlashEventKey(v sdk.ValAddress, height, period uint64) []byte {
|
||||||
|
periodBz := make([]byte, 8)
|
||||||
|
binary.BigEndian.PutUint64(periodBz, period)
|
||||||
|
prefix := GetValidatorSlashEventKeyPrefix(v, height)
|
||||||
|
return append(prefix, periodBz...)
|
||||||
|
}
|
|
@ -1,6 +0,0 @@
|
||||||
package v040
|
|
||||||
|
|
||||||
// Default parameter values
|
|
||||||
const (
|
|
||||||
ModuleName = "distribution"
|
|
||||||
)
|
|
|
@ -0,0 +1,70 @@
|
||||||
|
package v042
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/cosmos/cosmos-sdk/store/prefix"
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
"github.com/cosmos/cosmos-sdk/types/address"
|
||||||
|
v040auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v040"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MigratePrefixAddress is a helper function that migrates all keys of format:
|
||||||
|
// prefix_bytes | address_bytes
|
||||||
|
// into format:
|
||||||
|
// prefix_bytes | address_len (1 byte) | address_bytes
|
||||||
|
func MigratePrefixAddress(store sdk.KVStore, prefixBz []byte) {
|
||||||
|
oldStore := prefix.NewStore(store, prefixBz)
|
||||||
|
|
||||||
|
oldStoreIter := oldStore.Iterator(nil, nil)
|
||||||
|
defer oldStoreIter.Close()
|
||||||
|
|
||||||
|
for ; oldStoreIter.Valid(); oldStoreIter.Next() {
|
||||||
|
addr := oldStoreIter.Key()
|
||||||
|
newStoreKey := append(prefixBz, address.MustLengthPrefix(addr)...)
|
||||||
|
|
||||||
|
// Set new key on store. Values don't change.
|
||||||
|
store.Set(newStoreKey, oldStoreIter.Value())
|
||||||
|
oldStore.Delete(oldStoreIter.Key())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MigratePrefixAddressBytes is a helper function that migrates all keys of format:
|
||||||
|
// prefix_bytes | address_bytes | arbitrary_bytes
|
||||||
|
// into format:
|
||||||
|
// prefix_bytes | address_len (1 byte) | address_bytes | arbitrary_bytes
|
||||||
|
func MigratePrefixAddressBytes(store sdk.KVStore, prefixBz []byte) {
|
||||||
|
oldStore := prefix.NewStore(store, prefixBz)
|
||||||
|
|
||||||
|
oldStoreIter := oldStore.Iterator(nil, nil)
|
||||||
|
defer oldStoreIter.Close()
|
||||||
|
|
||||||
|
for ; oldStoreIter.Valid(); oldStoreIter.Next() {
|
||||||
|
addr := oldStoreIter.Key()[:v040auth.AddrLen]
|
||||||
|
endBz := oldStoreIter.Key()[v040auth.AddrLen:]
|
||||||
|
newStoreKey := append(append(prefixBz, address.MustLengthPrefix(addr)...), endBz...)
|
||||||
|
|
||||||
|
// Set new key on store. Values don't change.
|
||||||
|
store.Set(newStoreKey, oldStoreIter.Value())
|
||||||
|
oldStore.Delete(oldStoreIter.Key())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MigratePrefixAddressAddress is a helper function that migrates all keys of format:
|
||||||
|
// prefix_bytes | address_1_bytes | address_2_bytes
|
||||||
|
// into format:
|
||||||
|
// prefix_bytes | address_1_len (1 byte) | address_1_bytes | address_2_len (1 byte) | address_2_bytes
|
||||||
|
func MigratePrefixAddressAddress(store sdk.KVStore, prefixBz []byte) {
|
||||||
|
oldStore := prefix.NewStore(store, prefixBz)
|
||||||
|
|
||||||
|
oldStoreIter := oldStore.Iterator(nil, nil)
|
||||||
|
defer oldStoreIter.Close()
|
||||||
|
|
||||||
|
for ; oldStoreIter.Valid(); oldStoreIter.Next() {
|
||||||
|
addr1 := oldStoreIter.Key()[:v040auth.AddrLen]
|
||||||
|
addr2 := oldStoreIter.Key()[v040auth.AddrLen:]
|
||||||
|
newStoreKey := append(append(prefixBz, address.MustLengthPrefix(addr1)...), address.MustLengthPrefix(addr2)...)
|
||||||
|
|
||||||
|
// Set new key on store. Values don't change.
|
||||||
|
store.Set(newStoreKey, oldStoreIter.Value())
|
||||||
|
oldStore.Delete(oldStoreIter.Key())
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
package v042
|
||||||
|
|
||||||
|
import (
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
v040distribution "github.com/cosmos/cosmos-sdk/x/distribution/legacy/v040"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MigrateStore performs in-place store migrations from v0.40 to v0.42. The
|
||||||
|
// migration includes:
|
||||||
|
//
|
||||||
|
// - Change addresses to be length-prefixed.
|
||||||
|
func MigrateStore(ctx sdk.Context, storeKey sdk.StoreKey) error {
|
||||||
|
store := ctx.KVStore(storeKey)
|
||||||
|
MigratePrefixAddress(store, v040distribution.ValidatorOutstandingRewardsPrefix)
|
||||||
|
MigratePrefixAddress(store, v040distribution.DelegatorWithdrawAddrPrefix)
|
||||||
|
MigratePrefixAddressAddress(store, v040distribution.DelegatorStartingInfoPrefix)
|
||||||
|
MigratePrefixAddressBytes(store, v040distribution.ValidatorHistoricalRewardsPrefix)
|
||||||
|
MigratePrefixAddress(store, v040distribution.ValidatorCurrentRewardsPrefix)
|
||||||
|
MigratePrefixAddress(store, v040distribution.ValidatorAccumulatedCommissionPrefix)
|
||||||
|
MigratePrefixAddressBytes(store, v040distribution.ValidatorSlashEventPrefix)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,99 @@
|
||||||
|
package v042_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/testutil"
|
||||||
|
"github.com/cosmos/cosmos-sdk/testutil/testdata"
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
v040distribution "github.com/cosmos/cosmos-sdk/x/distribution/legacy/v040"
|
||||||
|
v042distribution "github.com/cosmos/cosmos-sdk/x/distribution/legacy/v042"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/distribution/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestStoreMigration(t *testing.T) {
|
||||||
|
distributionKey := sdk.NewKVStoreKey("distribution")
|
||||||
|
ctx := testutil.DefaultContext(distributionKey, sdk.NewTransientStoreKey("transient_test"))
|
||||||
|
store := ctx.KVStore(distributionKey)
|
||||||
|
|
||||||
|
_, _, addr1 := testdata.KeyTestPubAddr()
|
||||||
|
valAddr := sdk.ValAddress(addr1)
|
||||||
|
_, _, addr2 := testdata.KeyTestPubAddr()
|
||||||
|
// Use dummy value for all keys.
|
||||||
|
value := []byte("foo")
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
oldKey []byte
|
||||||
|
newKey []byte
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"FeePoolKey",
|
||||||
|
v040distribution.FeePoolKey,
|
||||||
|
types.FeePoolKey,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ProposerKey",
|
||||||
|
v040distribution.ProposerKey,
|
||||||
|
types.ProposerKey,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ValidatorOutstandingRewards",
|
||||||
|
v040distribution.GetValidatorOutstandingRewardsKey(valAddr),
|
||||||
|
types.GetValidatorOutstandingRewardsKey(valAddr),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"DelegatorWithdrawAddr",
|
||||||
|
v040distribution.GetDelegatorWithdrawAddrKey(addr2),
|
||||||
|
types.GetDelegatorWithdrawAddrKey(addr2),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"DelegatorStartingInfo",
|
||||||
|
v040distribution.GetDelegatorStartingInfoKey(valAddr, addr2),
|
||||||
|
types.GetDelegatorStartingInfoKey(valAddr, addr2),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ValidatorHistoricalRewards",
|
||||||
|
v040distribution.GetValidatorHistoricalRewardsKey(valAddr, 6),
|
||||||
|
types.GetValidatorHistoricalRewardsKey(valAddr, 6),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ValidatorCurrentRewards",
|
||||||
|
v040distribution.GetValidatorCurrentRewardsKey(valAddr),
|
||||||
|
types.GetValidatorCurrentRewardsKey(valAddr),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ValidatorAccumulatedCommission",
|
||||||
|
v040distribution.GetValidatorAccumulatedCommissionKey(valAddr),
|
||||||
|
types.GetValidatorAccumulatedCommissionKey(valAddr),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ValidatorSlashEvent",
|
||||||
|
v040distribution.GetValidatorSlashEventKey(valAddr, 6, 8),
|
||||||
|
types.GetValidatorSlashEventKey(valAddr, 6, 8),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set all the old keys to the store
|
||||||
|
for _, tc := range testCases {
|
||||||
|
store.Set(tc.oldKey, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run migrations.
|
||||||
|
err := v042distribution.MigrateStore(ctx, distributionKey)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Make sure the new keys are set and old keys are deleted.
|
||||||
|
for _, tc := range testCases {
|
||||||
|
tc := tc
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
if bytes.Compare(tc.oldKey, tc.newKey) != 0 {
|
||||||
|
require.Nil(t, store.Get(tc.oldKey))
|
||||||
|
}
|
||||||
|
require.Equal(t, value, store.Get(tc.newKey))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -141,6 +141,9 @@ func (am AppModule) LegacyQuerierHandler(legacyQuerierCdc *codec.LegacyAmino) sd
|
||||||
func (am AppModule) RegisterServices(cfg module.Configurator) {
|
func (am AppModule) RegisterServices(cfg module.Configurator) {
|
||||||
types.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServerImpl(am.keeper))
|
types.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServerImpl(am.keeper))
|
||||||
types.RegisterQueryServer(cfg.QueryServer(), am.keeper)
|
types.RegisterQueryServer(cfg.QueryServer(), am.keeper)
|
||||||
|
|
||||||
|
m := keeper.NewMigrator(am.keeper)
|
||||||
|
cfg.RegisterMigration(types.ModuleName, 1, m.Migrate1to2)
|
||||||
}
|
}
|
||||||
|
|
||||||
// InitGenesis performs genesis initialization for the distribution module. It returns
|
// InitGenesis performs genesis initialization for the distribution module. It returns
|
||||||
|
@ -160,7 +163,7 @@ func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONMarshaler) json
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConsensusVersion implements AppModule/ConsensusVersion.
|
// ConsensusVersion implements AppModule/ConsensusVersion.
|
||||||
func (AppModule) ConsensusVersion() uint64 { return 1 }
|
func (AppModule) ConsensusVersion() uint64 { return 2 }
|
||||||
|
|
||||||
// BeginBlock returns the begin blocker for the distribution module.
|
// BeginBlock returns the begin blocker for the distribution module.
|
||||||
func (am AppModule) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) {
|
func (am AppModule) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) {
|
||||||
|
|
|
@ -15,7 +15,7 @@ for fractions of coins to be received from operations like inflation.
|
||||||
When coins are distributed from the pool they are truncated back to
|
When coins are distributed from the pool they are truncated back to
|
||||||
`sdk.Coins` which are non-decimal.
|
`sdk.Coins` which are non-decimal.
|
||||||
|
|
||||||
- FeePool: `0x00 -> ProtocolBuffer(FeePool)`
|
- FeePool: `0x00 -> ProtocolBuffer(FeePool)`
|
||||||
|
|
||||||
```go
|
```go
|
||||||
// coins with decimal
|
// coins with decimal
|
||||||
|
@ -33,12 +33,12 @@ type DecCoin struct {
|
||||||
|
|
||||||
Validator distribution information for the relevant validator is updated each time:
|
Validator distribution information for the relevant validator is updated each time:
|
||||||
|
|
||||||
1. delegation amount to a validator is updated,
|
1. delegation amount to a validator is updated,
|
||||||
2. a validator successfully proposes a block and receives a reward,
|
2. a validator successfully proposes a block and receives a reward,
|
||||||
3. any delegator withdraws from a validator, or
|
3. any delegator withdraws from a validator, or
|
||||||
4. the validator withdraws it's commission.
|
4. the validator withdraws it's commission.
|
||||||
|
|
||||||
- ValidatorDistInfo: `0x02 | ValOperatorAddr -> ProtocolBuffer(validatorDistribution)`
|
- ValidatorDistInfo: `0x02 | ValOperatorAddrLen (1 byte) | ValOperatorAddr -> ProtocolBuffer(validatorDistribution)`
|
||||||
|
|
||||||
```go
|
```go
|
||||||
type ValidatorDistInfo struct {
|
type ValidatorDistInfo struct {
|
||||||
|
@ -56,7 +56,7 @@ properties change (aka bonded tokens etc.) its properties will remain constant
|
||||||
and the delegator's _accumulation_ factor can be calculated passively knowing
|
and the delegator's _accumulation_ factor can be calculated passively knowing
|
||||||
only the height of the last withdrawal and its current properties.
|
only the height of the last withdrawal and its current properties.
|
||||||
|
|
||||||
- DelegationDistInfo: `0x02 | DelegatorAddr | ValOperatorAddr -> ProtocolBuffer(delegatorDist)`
|
- DelegationDistInfo: `0x02 | DelegatorAddrLen (1 byte) | DelegatorAddr | ValOperatorAddrLen (1 byte) | ValOperatorAddr -> ProtocolBuffer(delegatorDist)`
|
||||||
|
|
||||||
```go
|
```go
|
||||||
type DelegationDistInfo struct {
|
type DelegationDistInfo struct {
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
package keeper
|
||||||
|
|
||||||
|
import (
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
v042 "github.com/cosmos/cosmos-sdk/x/gov/legacy/v042"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Migrator is a struct for handling in-place store migrations.
|
||||||
|
type Migrator struct {
|
||||||
|
keeper Keeper
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMigrator returns a new Migrator.
|
||||||
|
func NewMigrator(keeper Keeper) Migrator {
|
||||||
|
return Migrator{keeper: keeper}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Migrate1to2 migrates from version 1 to 2.
|
||||||
|
func (m Migrator) Migrate1to2(ctx sdk.Context) error {
|
||||||
|
return v042.MigrateStore(ctx, m.keeper.storeKey)
|
||||||
|
}
|
|
@ -0,0 +1,167 @@
|
||||||
|
// Package v040 is copy-pasted from:
|
||||||
|
// https://github.com/cosmos/cosmos-sdk/blob/v0.41.0/x/gov/types/keys.go
|
||||||
|
package v040
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
v040auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v040"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// ModuleName is the name of the module
|
||||||
|
ModuleName = "gov"
|
||||||
|
|
||||||
|
// StoreKey is the store key string for gov
|
||||||
|
StoreKey = ModuleName
|
||||||
|
|
||||||
|
// RouterKey is the message route for gov
|
||||||
|
RouterKey = ModuleName
|
||||||
|
|
||||||
|
// QuerierRoute is the querier route for gov
|
||||||
|
QuerierRoute = ModuleName
|
||||||
|
)
|
||||||
|
|
||||||
|
// Keys for governance store
|
||||||
|
// Items are stored with the following key: values
|
||||||
|
//
|
||||||
|
// - 0x00<proposalID_Bytes>: Proposal
|
||||||
|
//
|
||||||
|
// - 0x01<endTime_Bytes><proposalID_Bytes>: activeProposalID
|
||||||
|
//
|
||||||
|
// - 0x02<endTime_Bytes><proposalID_Bytes>: inactiveProposalID
|
||||||
|
//
|
||||||
|
// - 0x03: nextProposalID
|
||||||
|
//
|
||||||
|
// - 0x10<proposalID_Bytes><depositorAddr_Bytes>: Deposit
|
||||||
|
//
|
||||||
|
// - 0x20<proposalID_Bytes><voterAddr_Bytes>: Voter
|
||||||
|
var (
|
||||||
|
ProposalsKeyPrefix = []byte{0x00}
|
||||||
|
ActiveProposalQueuePrefix = []byte{0x01}
|
||||||
|
InactiveProposalQueuePrefix = []byte{0x02}
|
||||||
|
ProposalIDKey = []byte{0x03}
|
||||||
|
|
||||||
|
DepositsKeyPrefix = []byte{0x10}
|
||||||
|
|
||||||
|
VotesKeyPrefix = []byte{0x20}
|
||||||
|
)
|
||||||
|
|
||||||
|
var lenTime = len(sdk.FormatTimeBytes(time.Now()))
|
||||||
|
|
||||||
|
// GetProposalIDBytes returns the byte representation of the proposalID
|
||||||
|
func GetProposalIDBytes(proposalID uint64) (proposalIDBz []byte) {
|
||||||
|
proposalIDBz = make([]byte, 8)
|
||||||
|
binary.BigEndian.PutUint64(proposalIDBz, proposalID)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetProposalIDFromBytes returns proposalID in uint64 format from a byte array
|
||||||
|
func GetProposalIDFromBytes(bz []byte) (proposalID uint64) {
|
||||||
|
return binary.BigEndian.Uint64(bz)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProposalKey gets a specific proposal from the store
|
||||||
|
func ProposalKey(proposalID uint64) []byte {
|
||||||
|
return append(ProposalsKeyPrefix, GetProposalIDBytes(proposalID)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ActiveProposalByTimeKey gets the active proposal queue key by endTime
|
||||||
|
func ActiveProposalByTimeKey(endTime time.Time) []byte {
|
||||||
|
return append(ActiveProposalQueuePrefix, sdk.FormatTimeBytes(endTime)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ActiveProposalQueueKey returns the key for a proposalID in the activeProposalQueue
|
||||||
|
func ActiveProposalQueueKey(proposalID uint64, endTime time.Time) []byte {
|
||||||
|
return append(ActiveProposalByTimeKey(endTime), GetProposalIDBytes(proposalID)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// InactiveProposalByTimeKey gets the inactive proposal queue key by endTime
|
||||||
|
func InactiveProposalByTimeKey(endTime time.Time) []byte {
|
||||||
|
return append(InactiveProposalQueuePrefix, sdk.FormatTimeBytes(endTime)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// InactiveProposalQueueKey returns the key for a proposalID in the inactiveProposalQueue
|
||||||
|
func InactiveProposalQueueKey(proposalID uint64, endTime time.Time) []byte {
|
||||||
|
return append(InactiveProposalByTimeKey(endTime), GetProposalIDBytes(proposalID)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DepositsKey gets the first part of the deposits key based on the proposalID
|
||||||
|
func DepositsKey(proposalID uint64) []byte {
|
||||||
|
return append(DepositsKeyPrefix, GetProposalIDBytes(proposalID)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DepositKey key of a specific deposit from the store
|
||||||
|
func DepositKey(proposalID uint64, depositorAddr sdk.AccAddress) []byte {
|
||||||
|
return append(DepositsKey(proposalID), depositorAddr.Bytes()...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// VotesKey gets the first part of the votes key based on the proposalID
|
||||||
|
func VotesKey(proposalID uint64) []byte {
|
||||||
|
return append(VotesKeyPrefix, GetProposalIDBytes(proposalID)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// VoteKey key of a specific vote from the store
|
||||||
|
func VoteKey(proposalID uint64, voterAddr sdk.AccAddress) []byte {
|
||||||
|
return append(VotesKey(proposalID), voterAddr.Bytes()...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Split keys function; used for iterators
|
||||||
|
|
||||||
|
// SplitProposalKey split the proposal key and returns the proposal id
|
||||||
|
func SplitProposalKey(key []byte) (proposalID uint64) {
|
||||||
|
if len(key[1:]) != 8 {
|
||||||
|
panic(fmt.Sprintf("unexpected key length (%d ≠ 8)", len(key[1:])))
|
||||||
|
}
|
||||||
|
|
||||||
|
return GetProposalIDFromBytes(key[1:])
|
||||||
|
}
|
||||||
|
|
||||||
|
// SplitActiveProposalQueueKey split the active proposal key and returns the proposal id and endTime
|
||||||
|
func SplitActiveProposalQueueKey(key []byte) (proposalID uint64, endTime time.Time) {
|
||||||
|
return splitKeyWithTime(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SplitInactiveProposalQueueKey split the inactive proposal key and returns the proposal id and endTime
|
||||||
|
func SplitInactiveProposalQueueKey(key []byte) (proposalID uint64, endTime time.Time) {
|
||||||
|
return splitKeyWithTime(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SplitKeyDeposit split the deposits key and returns the proposal id and depositor address
|
||||||
|
func SplitKeyDeposit(key []byte) (proposalID uint64, depositorAddr sdk.AccAddress) {
|
||||||
|
return splitKeyWithAddress(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SplitKeyVote split the votes key and returns the proposal id and voter address
|
||||||
|
func SplitKeyVote(key []byte) (proposalID uint64, voterAddr sdk.AccAddress) {
|
||||||
|
return splitKeyWithAddress(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// private functions
|
||||||
|
|
||||||
|
func splitKeyWithTime(key []byte) (proposalID uint64, endTime time.Time) {
|
||||||
|
if len(key[1:]) != 8+lenTime {
|
||||||
|
panic(fmt.Sprintf("unexpected key length (%d ≠ %d)", len(key[1:]), lenTime+8))
|
||||||
|
}
|
||||||
|
|
||||||
|
endTime, err := sdk.ParseTimeBytes(key[1 : 1+lenTime])
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
proposalID = GetProposalIDFromBytes(key[1+lenTime:])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func splitKeyWithAddress(key []byte) (proposalID uint64, addr sdk.AccAddress) {
|
||||||
|
if len(key[1:]) != 8+v040auth.AddrLen {
|
||||||
|
panic(fmt.Sprintf("unexpected key length (%d ≠ %d)", len(key), 8+v040auth.AddrLen))
|
||||||
|
}
|
||||||
|
|
||||||
|
proposalID = GetProposalIDFromBytes(key[1:9])
|
||||||
|
addr = sdk.AccAddress(key[9:])
|
||||||
|
return
|
||||||
|
}
|
|
@ -1,6 +0,0 @@
|
||||||
package v040
|
|
||||||
|
|
||||||
// Default parameter values
|
|
||||||
const (
|
|
||||||
ModuleName = "gov"
|
|
||||||
)
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
package v042
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/cosmos/cosmos-sdk/store/prefix"
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
"github.com/cosmos/cosmos-sdk/types/address"
|
||||||
|
v040gov "github.com/cosmos/cosmos-sdk/x/gov/legacy/v040"
|
||||||
|
)
|
||||||
|
|
||||||
|
const proposalIDLen = 8
|
||||||
|
|
||||||
|
// migratePrefixProposalAddress is a helper function that migrates all keys of format:
|
||||||
|
// <prefix_bytes><proposal_id (8 bytes)><address_bytes>
|
||||||
|
// into format:
|
||||||
|
// <prefix_bytes><proposal_id (8 bytes)><address_len (1 byte)><address_bytes>
|
||||||
|
func migratePrefixProposalAddress(store sdk.KVStore, prefixBz []byte) {
|
||||||
|
oldStore := prefix.NewStore(store, prefixBz)
|
||||||
|
|
||||||
|
oldStoreIter := oldStore.Iterator(nil, nil)
|
||||||
|
defer oldStoreIter.Close()
|
||||||
|
|
||||||
|
for ; oldStoreIter.Valid(); oldStoreIter.Next() {
|
||||||
|
proposalID := oldStoreIter.Key()[:proposalIDLen]
|
||||||
|
addr := oldStoreIter.Key()[proposalIDLen:]
|
||||||
|
newStoreKey := append(append(prefixBz, proposalID...), address.MustLengthPrefix(addr)...)
|
||||||
|
|
||||||
|
// Set new key on store. Values don't change.
|
||||||
|
store.Set(newStoreKey, oldStoreIter.Value())
|
||||||
|
oldStore.Delete(oldStoreIter.Key())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MigrateStore performs in-place store migrations from v0.40 to v0.42. The
|
||||||
|
// migration includes:
|
||||||
|
//
|
||||||
|
// - Change addresses to be length-prefixed.
|
||||||
|
func MigrateStore(ctx sdk.Context, storeKey sdk.StoreKey) error {
|
||||||
|
store := ctx.KVStore(storeKey)
|
||||||
|
migratePrefixProposalAddress(store, v040gov.DepositsKeyPrefix)
|
||||||
|
migratePrefixProposalAddress(store, v040gov.VotesKeyPrefix)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,85 @@
|
||||||
|
package v042_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/testutil"
|
||||||
|
"github.com/cosmos/cosmos-sdk/testutil/testdata"
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
v040gov "github.com/cosmos/cosmos-sdk/x/gov/legacy/v040"
|
||||||
|
v042gov "github.com/cosmos/cosmos-sdk/x/gov/legacy/v042"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/gov/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestStoreMigration(t *testing.T) {
|
||||||
|
govKey := sdk.NewKVStoreKey("gov")
|
||||||
|
ctx := testutil.DefaultContext(govKey, sdk.NewTransientStoreKey("transient_test"))
|
||||||
|
store := ctx.KVStore(govKey)
|
||||||
|
|
||||||
|
_, _, addr1 := testdata.KeyTestPubAddr()
|
||||||
|
proposalID := uint64(6)
|
||||||
|
now := time.Now()
|
||||||
|
// Use dummy value for all keys.
|
||||||
|
value := []byte("foo")
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
oldKey []byte
|
||||||
|
newKey []byte
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"ProposalKey",
|
||||||
|
v040gov.ProposalKey(proposalID),
|
||||||
|
types.ProposalKey(proposalID),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ActiveProposalQueue",
|
||||||
|
v040gov.ActiveProposalQueueKey(proposalID, now),
|
||||||
|
types.ActiveProposalQueueKey(proposalID, now),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"InactiveProposalQueue",
|
||||||
|
v040gov.InactiveProposalQueueKey(proposalID, now),
|
||||||
|
types.InactiveProposalQueueKey(proposalID, now),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ProposalIDKey",
|
||||||
|
v040gov.ProposalIDKey,
|
||||||
|
types.ProposalIDKey,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"DepositKey",
|
||||||
|
v040gov.DepositKey(proposalID, addr1),
|
||||||
|
types.DepositKey(proposalID, addr1),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"VotesKeyPrefix",
|
||||||
|
v040gov.VoteKey(proposalID, addr1),
|
||||||
|
types.VoteKey(proposalID, addr1),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set all the old keys to the store
|
||||||
|
for _, tc := range testCases {
|
||||||
|
store.Set(tc.oldKey, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run migrations.
|
||||||
|
err := v042gov.MigrateStore(ctx, govKey)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Make sure the new keys are set and old keys are deleted.
|
||||||
|
for _, tc := range testCases {
|
||||||
|
tc := tc
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
if bytes.Compare(tc.oldKey, tc.newKey) != 0 {
|
||||||
|
require.Nil(t, store.Get(tc.oldKey))
|
||||||
|
}
|
||||||
|
require.Equal(t, value, store.Get(tc.newKey))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -157,6 +157,9 @@ func (am AppModule) LegacyQuerierHandler(legacyQuerierCdc *codec.LegacyAmino) sd
|
||||||
func (am AppModule) RegisterServices(cfg module.Configurator) {
|
func (am AppModule) RegisterServices(cfg module.Configurator) {
|
||||||
types.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServerImpl(am.keeper))
|
types.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServerImpl(am.keeper))
|
||||||
types.RegisterQueryServer(cfg.QueryServer(), am.keeper)
|
types.RegisterQueryServer(cfg.QueryServer(), am.keeper)
|
||||||
|
|
||||||
|
m := keeper.NewMigrator(am.keeper)
|
||||||
|
cfg.RegisterMigration(types.ModuleName, 1, m.Migrate1to2)
|
||||||
}
|
}
|
||||||
|
|
||||||
// InitGenesis performs genesis initialization for the gov module. It returns
|
// InitGenesis performs genesis initialization for the gov module. It returns
|
||||||
|
@ -176,7 +179,7 @@ func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONMarshaler) json
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConsensusVersion implements AppModule/ConsensusVersion.
|
// ConsensusVersion implements AppModule/ConsensusVersion.
|
||||||
func (AppModule) ConsensusVersion() uint64 { return 1 }
|
func (AppModule) ConsensusVersion() uint64 { return 2 }
|
||||||
|
|
||||||
// BeginBlock performs a no-op.
|
// BeginBlock performs a no-op.
|
||||||
func (AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock) {}
|
func (AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock) {}
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
package keeper
|
||||||
|
|
||||||
|
import (
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
v042 "github.com/cosmos/cosmos-sdk/x/slashing/legacy/v042"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Migrator is a struct for handling in-place store migrations.
|
||||||
|
type Migrator struct {
|
||||||
|
keeper Keeper
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMigrator returns a new Migrator.
|
||||||
|
func NewMigrator(keeper Keeper) Migrator {
|
||||||
|
return Migrator{keeper: keeper}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Migrate1to2 migrates from version 1 to 2.
|
||||||
|
func (m Migrator) Migrate1to2(ctx sdk.Context) error {
|
||||||
|
return v042.MigrateStore(ctx, m.keeper.storeKey)
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
// Package v040 is copy-pasted from:
|
||||||
|
// https://github.com/cosmos/cosmos-sdk/blob/v0.41.0/x/slashing/types/keys.go
|
||||||
|
package v040
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
v040auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v040"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// ModuleName is the name of the module
|
||||||
|
ModuleName = "slashing"
|
||||||
|
|
||||||
|
// StoreKey is the store key string for slashing
|
||||||
|
StoreKey = ModuleName
|
||||||
|
|
||||||
|
// RouterKey is the message route for slashing
|
||||||
|
RouterKey = ModuleName
|
||||||
|
|
||||||
|
// QuerierRoute is the querier route for slashing
|
||||||
|
QuerierRoute = ModuleName
|
||||||
|
)
|
||||||
|
|
||||||
|
// Keys for slashing store
|
||||||
|
// Items are stored with the following key: values
|
||||||
|
//
|
||||||
|
// - 0x01<consAddress_Bytes>: ValidatorSigningInfo
|
||||||
|
//
|
||||||
|
// - 0x02<consAddress_Bytes><period_Bytes>: bool
|
||||||
|
//
|
||||||
|
// - 0x03<accAddr_Bytes>: crypto.PubKey
|
||||||
|
var (
|
||||||
|
ValidatorSigningInfoKeyPrefix = []byte{0x01} // Prefix for signing info
|
||||||
|
ValidatorMissedBlockBitArrayKeyPrefix = []byte{0x02} // Prefix for missed block bit array
|
||||||
|
AddrPubkeyRelationKeyPrefix = []byte{0x03} // Prefix for address-pubkey relation
|
||||||
|
)
|
||||||
|
|
||||||
|
// ValidatorSigningInfoKey - stored by *Consensus* address (not operator address)
|
||||||
|
func ValidatorSigningInfoKey(v sdk.ConsAddress) []byte {
|
||||||
|
return append(ValidatorSigningInfoKeyPrefix, v.Bytes()...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidatorSigningInfoAddress - extract the address from a validator signing info key
|
||||||
|
func ValidatorSigningInfoAddress(key []byte) (v sdk.ConsAddress) {
|
||||||
|
addr := key[1:]
|
||||||
|
if len(addr) != v040auth.AddrLen {
|
||||||
|
panic("unexpected key length")
|
||||||
|
}
|
||||||
|
return sdk.ConsAddress(addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidatorMissedBlockBitArrayPrefixKey - stored by *Consensus* address (not operator address)
|
||||||
|
func ValidatorMissedBlockBitArrayPrefixKey(v sdk.ConsAddress) []byte {
|
||||||
|
return append(ValidatorMissedBlockBitArrayKeyPrefix, v.Bytes()...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidatorMissedBlockBitArrayKey - stored by *Consensus* address (not operator address)
|
||||||
|
func ValidatorMissedBlockBitArrayKey(v sdk.ConsAddress, i int64) []byte {
|
||||||
|
b := make([]byte, 8)
|
||||||
|
binary.LittleEndian.PutUint64(b, uint64(i))
|
||||||
|
return append(ValidatorMissedBlockBitArrayPrefixKey(v), b...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddrPubkeyRelationKey gets pubkey relation key used to get the pubkey from the address
|
||||||
|
func AddrPubkeyRelationKey(address []byte) []byte {
|
||||||
|
return append(AddrPubkeyRelationKeyPrefix, address...)
|
||||||
|
}
|
|
@ -1,5 +0,0 @@
|
||||||
package v040
|
|
||||||
|
|
||||||
const (
|
|
||||||
ModuleName = "slashing"
|
|
||||||
)
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
package v042
|
||||||
|
|
||||||
|
import (
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
v042distribution "github.com/cosmos/cosmos-sdk/x/distribution/legacy/v042"
|
||||||
|
v040slashing "github.com/cosmos/cosmos-sdk/x/slashing/legacy/v040"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MigrateStore performs in-place store migrations from v0.40 to v0.42. The
|
||||||
|
// migration includes:
|
||||||
|
//
|
||||||
|
// - Change addresses to be length-prefixed.
|
||||||
|
func MigrateStore(ctx sdk.Context, storeKey sdk.StoreKey) error {
|
||||||
|
store := ctx.KVStore(storeKey)
|
||||||
|
v042distribution.MigratePrefixAddress(store, v040slashing.ValidatorSigningInfoKeyPrefix)
|
||||||
|
v042distribution.MigratePrefixAddressBytes(store, v040slashing.ValidatorMissedBlockBitArrayKeyPrefix)
|
||||||
|
v042distribution.MigratePrefixAddress(store, v040slashing.AddrPubkeyRelationKeyPrefix)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
package v042_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/testutil"
|
||||||
|
"github.com/cosmos/cosmos-sdk/testutil/testdata"
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
v040slashing "github.com/cosmos/cosmos-sdk/x/slashing/legacy/v040"
|
||||||
|
v042slashing "github.com/cosmos/cosmos-sdk/x/slashing/legacy/v042"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/slashing/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestStoreMigration(t *testing.T) {
|
||||||
|
slashingKey := sdk.NewKVStoreKey("slashing")
|
||||||
|
ctx := testutil.DefaultContext(slashingKey, sdk.NewTransientStoreKey("transient_test"))
|
||||||
|
store := ctx.KVStore(slashingKey)
|
||||||
|
|
||||||
|
_, _, addr1 := testdata.KeyTestPubAddr()
|
||||||
|
consAddr := sdk.ConsAddress(addr1)
|
||||||
|
// Use dummy value for all keys.
|
||||||
|
value := []byte("foo")
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
oldKey []byte
|
||||||
|
newKey []byte
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"ValidatorSigningInfoKey",
|
||||||
|
v040slashing.ValidatorSigningInfoKey(consAddr),
|
||||||
|
types.ValidatorSigningInfoKey(consAddr),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ValidatorMissedBlockBitArrayKey",
|
||||||
|
v040slashing.ValidatorMissedBlockBitArrayKey(consAddr, 2),
|
||||||
|
types.ValidatorMissedBlockBitArrayKey(consAddr, 2),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"AddrPubkeyRelationKey",
|
||||||
|
v040slashing.AddrPubkeyRelationKey(consAddr),
|
||||||
|
types.AddrPubkeyRelationKey(consAddr),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set all the old keys to the store
|
||||||
|
for _, tc := range testCases {
|
||||||
|
store.Set(tc.oldKey, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run migrations.
|
||||||
|
err := v042slashing.MigrateStore(ctx, slashingKey)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Make sure the new keys are set and old keys are deleted.
|
||||||
|
for _, tc := range testCases {
|
||||||
|
tc := tc
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
if bytes.Compare(tc.oldKey, tc.newKey) != 0 {
|
||||||
|
require.Nil(t, store.Get(tc.oldKey))
|
||||||
|
}
|
||||||
|
require.Equal(t, value, store.Get(tc.newKey))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -139,6 +139,9 @@ func (am AppModule) LegacyQuerierHandler(legacyQuerierCdc *codec.LegacyAmino) sd
|
||||||
func (am AppModule) RegisterServices(cfg module.Configurator) {
|
func (am AppModule) RegisterServices(cfg module.Configurator) {
|
||||||
types.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServerImpl(am.keeper))
|
types.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServerImpl(am.keeper))
|
||||||
types.RegisterQueryServer(cfg.QueryServer(), am.keeper)
|
types.RegisterQueryServer(cfg.QueryServer(), am.keeper)
|
||||||
|
|
||||||
|
m := keeper.NewMigrator(am.keeper)
|
||||||
|
cfg.RegisterMigration(types.ModuleName, 1, m.Migrate1to2)
|
||||||
}
|
}
|
||||||
|
|
||||||
// InitGenesis performs genesis initialization for the slashing module. It returns
|
// InitGenesis performs genesis initialization for the slashing module. It returns
|
||||||
|
|
|
@ -27,8 +27,8 @@ number of blocks by being automatically jailed, potentially slashed, and unbonde
|
||||||
Information about validator's liveness activity is tracked through `ValidatorSigningInfo`.
|
Information about validator's liveness activity is tracked through `ValidatorSigningInfo`.
|
||||||
It is indexed in the store as follows:
|
It is indexed in the store as follows:
|
||||||
|
|
||||||
- ValidatorSigningInfo: ` 0x01 | ConsAddress -> ProtocolBuffer(ValSigningInfo)`
|
- ValidatorSigningInfo: ` 0x01 | ConsAddrLen (1 byte) | ConsAddress -> ProtocolBuffer(ValSigningInfo)`
|
||||||
- MissedBlocksBitArray: ` 0x02 | ConsAddress | LittleEndianUint64(signArrayIndex) -> VarInt(didMiss)` (varint is a number encoding format)
|
- MissedBlocksBitArray: ` 0x02 | ConsAddrLen (1 byte) | ConsAddress | LittleEndianUint64(signArrayIndex) -> VarInt(didMiss)` (varint is a number encoding format)
|
||||||
|
|
||||||
The first mapping allows us to easily lookup the recent signing info for a
|
The first mapping allows us to easily lookup the recent signing info for a
|
||||||
validator based on the validator's consensus address.
|
validator based on the validator's consensus address.
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
package keeper
|
||||||
|
|
||||||
|
import (
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
v042 "github.com/cosmos/cosmos-sdk/x/staking/legacy/v042"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Migrator is a struct for handling in-place store migrations.
|
||||||
|
type Migrator struct {
|
||||||
|
keeper Keeper
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMigrator returns a new Migrator.
|
||||||
|
func NewMigrator(keeper Keeper) Migrator {
|
||||||
|
return Migrator{keeper: keeper}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Migrate1to2 migrates from version 1 to 2.
|
||||||
|
func (m Migrator) Migrate1to2(ctx sdk.Context) error {
|
||||||
|
return v042.MigrateStore(ctx, m.keeper.storeKey)
|
||||||
|
}
|
|
@ -0,0 +1,330 @@
|
||||||
|
// Package v040 is copy-pasted from:
|
||||||
|
// https://github.com/cosmos/cosmos-sdk/blob/v0.41.0/x/staking/types/keys.go
|
||||||
|
package v040
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
v040auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v040"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/staking/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// ModuleName is the name of the staking module
|
||||||
|
ModuleName = "staking"
|
||||||
|
|
||||||
|
// StoreKey is the string store representation
|
||||||
|
StoreKey = ModuleName
|
||||||
|
|
||||||
|
// QuerierRoute is the querier route for the staking module
|
||||||
|
QuerierRoute = ModuleName
|
||||||
|
|
||||||
|
// RouterKey is the msg router key for the staking module
|
||||||
|
RouterKey = ModuleName
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Keys for store prefixes
|
||||||
|
// Last* values are constant during a block.
|
||||||
|
LastValidatorPowerKey = []byte{0x11} // prefix for each key to a validator index, for bonded validators
|
||||||
|
LastTotalPowerKey = []byte{0x12} // prefix for the total power
|
||||||
|
|
||||||
|
ValidatorsKey = []byte{0x21} // prefix for each key to a validator
|
||||||
|
ValidatorsByConsAddrKey = []byte{0x22} // prefix for each key to a validator index, by pubkey
|
||||||
|
ValidatorsByPowerIndexKey = []byte{0x23} // prefix for each key to a validator index, sorted by power
|
||||||
|
|
||||||
|
DelegationKey = []byte{0x31} // key for a delegation
|
||||||
|
UnbondingDelegationKey = []byte{0x32} // key for an unbonding-delegation
|
||||||
|
UnbondingDelegationByValIndexKey = []byte{0x33} // prefix for each key for an unbonding-delegation, by validator operator
|
||||||
|
RedelegationKey = []byte{0x34} // key for a redelegation
|
||||||
|
RedelegationByValSrcIndexKey = []byte{0x35} // prefix for each key for an redelegation, by source validator operator
|
||||||
|
RedelegationByValDstIndexKey = []byte{0x36} // prefix for each key for an redelegation, by destination validator operator
|
||||||
|
|
||||||
|
UnbondingQueueKey = []byte{0x41} // prefix for the timestamps in unbonding queue
|
||||||
|
RedelegationQueueKey = []byte{0x42} // prefix for the timestamps in redelegations queue
|
||||||
|
ValidatorQueueKey = []byte{0x43} // prefix for the timestamps in validator queue
|
||||||
|
|
||||||
|
HistoricalInfoKey = []byte{0x50} // prefix for the historical info
|
||||||
|
)
|
||||||
|
|
||||||
|
// gets the key for the validator with address
|
||||||
|
// VALUE: staking/Validator
|
||||||
|
func GetValidatorKey(operatorAddr sdk.ValAddress) []byte {
|
||||||
|
return append(ValidatorsKey, operatorAddr.Bytes()...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// gets the key for the validator with pubkey
|
||||||
|
// VALUE: validator operator address ([]byte)
|
||||||
|
func GetValidatorByConsAddrKey(addr sdk.ConsAddress) []byte {
|
||||||
|
return append(ValidatorsByConsAddrKey, addr.Bytes()...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the validator operator address from LastValidatorPowerKey
|
||||||
|
func AddressFromLastValidatorPowerKey(key []byte) []byte {
|
||||||
|
return key[1:] // remove prefix bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the validator by power index.
|
||||||
|
// Power index is the key used in the power-store, and represents the relative
|
||||||
|
// power ranking of the validator.
|
||||||
|
// VALUE: validator operator address ([]byte)
|
||||||
|
func GetValidatorsByPowerIndexKey(validator types.Validator) []byte {
|
||||||
|
// NOTE the address doesn't need to be stored because counter bytes must always be different
|
||||||
|
// NOTE the larger values are of higher value
|
||||||
|
|
||||||
|
consensusPower := sdk.TokensToConsensusPower(validator.Tokens)
|
||||||
|
consensusPowerBytes := make([]byte, 8)
|
||||||
|
binary.BigEndian.PutUint64(consensusPowerBytes, uint64(consensusPower))
|
||||||
|
|
||||||
|
powerBytes := consensusPowerBytes
|
||||||
|
powerBytesLen := len(powerBytes) // 8
|
||||||
|
|
||||||
|
// key is of format prefix || powerbytes || addrBytes
|
||||||
|
key := make([]byte, 1+powerBytesLen+v040auth.AddrLen)
|
||||||
|
|
||||||
|
key[0] = ValidatorsByPowerIndexKey[0]
|
||||||
|
copy(key[1:powerBytesLen+1], powerBytes)
|
||||||
|
addr, err := sdk.ValAddressFromBech32(validator.OperatorAddress)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
operAddrInvr := sdk.CopyBytes(addr)
|
||||||
|
|
||||||
|
for i, b := range operAddrInvr {
|
||||||
|
operAddrInvr[i] = ^b
|
||||||
|
}
|
||||||
|
|
||||||
|
copy(key[powerBytesLen+1:], operAddrInvr)
|
||||||
|
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the bonded validator index key for an operator address
|
||||||
|
func GetLastValidatorPowerKey(operator sdk.ValAddress) []byte {
|
||||||
|
return append(LastValidatorPowerKey, operator...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse the validators operator address from power rank key
|
||||||
|
func ParseValidatorPowerRankKey(key []byte) (operAddr []byte) {
|
||||||
|
powerBytesLen := 8
|
||||||
|
if len(key) != 1+powerBytesLen+v040auth.AddrLen {
|
||||||
|
panic("Invalid validator power rank key length")
|
||||||
|
}
|
||||||
|
|
||||||
|
operAddr = sdk.CopyBytes(key[powerBytesLen+1:])
|
||||||
|
|
||||||
|
for i, b := range operAddr {
|
||||||
|
operAddr[i] = ^b
|
||||||
|
}
|
||||||
|
|
||||||
|
return operAddr
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetValidatorQueueKey returns the prefix key used for getting a set of unbonding
|
||||||
|
// validators whose unbonding completion occurs at the given time and height.
|
||||||
|
func GetValidatorQueueKey(timestamp time.Time, height int64) []byte {
|
||||||
|
heightBz := sdk.Uint64ToBigEndian(uint64(height))
|
||||||
|
timeBz := sdk.FormatTimeBytes(timestamp)
|
||||||
|
timeBzL := len(timeBz)
|
||||||
|
prefixL := len(ValidatorQueueKey)
|
||||||
|
|
||||||
|
bz := make([]byte, prefixL+8+timeBzL+8)
|
||||||
|
|
||||||
|
// copy the prefix
|
||||||
|
copy(bz[:prefixL], ValidatorQueueKey)
|
||||||
|
|
||||||
|
// copy the encoded time bytes length
|
||||||
|
copy(bz[prefixL:prefixL+8], sdk.Uint64ToBigEndian(uint64(timeBzL)))
|
||||||
|
|
||||||
|
// copy the encoded time bytes
|
||||||
|
copy(bz[prefixL+8:prefixL+8+timeBzL], timeBz)
|
||||||
|
|
||||||
|
// copy the encoded height
|
||||||
|
copy(bz[prefixL+8+timeBzL:], heightBz)
|
||||||
|
|
||||||
|
return bz
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseValidatorQueueKey returns the encoded time and height from a key created
|
||||||
|
// from GetValidatorQueueKey.
|
||||||
|
func ParseValidatorQueueKey(bz []byte) (time.Time, int64, error) {
|
||||||
|
prefixL := len(ValidatorQueueKey)
|
||||||
|
if prefix := bz[:prefixL]; !bytes.Equal(prefix, ValidatorQueueKey) {
|
||||||
|
return time.Time{}, 0, fmt.Errorf("invalid prefix; expected: %X, got: %X", ValidatorQueueKey, prefix)
|
||||||
|
}
|
||||||
|
|
||||||
|
timeBzL := sdk.BigEndianToUint64(bz[prefixL : prefixL+8])
|
||||||
|
ts, err := sdk.ParseTimeBytes(bz[prefixL+8 : prefixL+8+int(timeBzL)])
|
||||||
|
if err != nil {
|
||||||
|
return time.Time{}, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
height := sdk.BigEndianToUint64(bz[prefixL+8+int(timeBzL):])
|
||||||
|
|
||||||
|
return ts, int64(height), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// gets the key for delegator bond with validator
|
||||||
|
// VALUE: staking/Delegation
|
||||||
|
func GetDelegationKey(delAddr sdk.AccAddress, valAddr sdk.ValAddress) []byte {
|
||||||
|
return append(GetDelegationsKey(delAddr), valAddr.Bytes()...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// gets the prefix for a delegator for all validators
|
||||||
|
func GetDelegationsKey(delAddr sdk.AccAddress) []byte {
|
||||||
|
return append(DelegationKey, delAddr.Bytes()...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// gets the key for an unbonding delegation by delegator and validator addr
|
||||||
|
// VALUE: staking/UnbondingDelegation
|
||||||
|
func GetUBDKey(delAddr sdk.AccAddress, valAddr sdk.ValAddress) []byte {
|
||||||
|
return append(
|
||||||
|
GetUBDsKey(delAddr.Bytes()),
|
||||||
|
valAddr.Bytes()...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// gets the index-key for an unbonding delegation, stored by validator-index
|
||||||
|
// VALUE: none (key rearrangement used)
|
||||||
|
func GetUBDByValIndexKey(delAddr sdk.AccAddress, valAddr sdk.ValAddress) []byte {
|
||||||
|
return append(GetUBDsByValIndexKey(valAddr), delAddr.Bytes()...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// rearranges the ValIndexKey to get the UBDKey
|
||||||
|
func GetUBDKeyFromValIndexKey(indexKey []byte) []byte {
|
||||||
|
addrs := indexKey[1:] // remove prefix bytes
|
||||||
|
if len(addrs) != 2*v040auth.AddrLen {
|
||||||
|
panic("unexpected key length")
|
||||||
|
}
|
||||||
|
|
||||||
|
valAddr := addrs[:v040auth.AddrLen]
|
||||||
|
delAddr := addrs[v040auth.AddrLen:]
|
||||||
|
|
||||||
|
return GetUBDKey(delAddr, valAddr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// gets the prefix for all unbonding delegations from a delegator
|
||||||
|
func GetUBDsKey(delAddr sdk.AccAddress) []byte {
|
||||||
|
return append(UnbondingDelegationKey, delAddr.Bytes()...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// gets the prefix keyspace for the indexes of unbonding delegations for a validator
|
||||||
|
func GetUBDsByValIndexKey(valAddr sdk.ValAddress) []byte {
|
||||||
|
return append(UnbondingDelegationByValIndexKey, valAddr.Bytes()...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// gets the prefix for all unbonding delegations from a delegator
|
||||||
|
func GetUnbondingDelegationTimeKey(timestamp time.Time) []byte {
|
||||||
|
bz := sdk.FormatTimeBytes(timestamp)
|
||||||
|
return append(UnbondingQueueKey, bz...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetREDKey returns a key prefix for indexing a redelegation from a delegator
|
||||||
|
// and source validator to a destination validator.
|
||||||
|
func GetREDKey(delAddr sdk.AccAddress, valSrcAddr, valDstAddr sdk.ValAddress) []byte {
|
||||||
|
key := make([]byte, 1+v040auth.AddrLen*3)
|
||||||
|
|
||||||
|
copy(key[0:v040auth.AddrLen+1], GetREDsKey(delAddr.Bytes()))
|
||||||
|
copy(key[v040auth.AddrLen+1:2*v040auth.AddrLen+1], valSrcAddr.Bytes())
|
||||||
|
copy(key[2*v040auth.AddrLen+1:3*v040auth.AddrLen+1], valDstAddr.Bytes())
|
||||||
|
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
|
||||||
|
// gets the index-key for a redelegation, stored by source-validator-index
|
||||||
|
// VALUE: none (key rearrangement used)
|
||||||
|
func GetREDByValSrcIndexKey(delAddr sdk.AccAddress, valSrcAddr, valDstAddr sdk.ValAddress) []byte {
|
||||||
|
REDSFromValsSrcKey := GetREDsFromValSrcIndexKey(valSrcAddr)
|
||||||
|
offset := len(REDSFromValsSrcKey)
|
||||||
|
|
||||||
|
// key is of the form REDSFromValsSrcKey || delAddr || valDstAddr
|
||||||
|
key := make([]byte, len(REDSFromValsSrcKey)+2*v040auth.AddrLen)
|
||||||
|
copy(key[0:offset], REDSFromValsSrcKey)
|
||||||
|
copy(key[offset:offset+v040auth.AddrLen], delAddr.Bytes())
|
||||||
|
copy(key[offset+v040auth.AddrLen:offset+2*v040auth.AddrLen], valDstAddr.Bytes())
|
||||||
|
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
|
||||||
|
// gets the index-key for a redelegation, stored by destination-validator-index
|
||||||
|
// VALUE: none (key rearrangement used)
|
||||||
|
func GetREDByValDstIndexKey(delAddr sdk.AccAddress, valSrcAddr, valDstAddr sdk.ValAddress) []byte {
|
||||||
|
REDSToValsDstKey := GetREDsToValDstIndexKey(valDstAddr)
|
||||||
|
offset := len(REDSToValsDstKey)
|
||||||
|
|
||||||
|
// key is of the form REDSToValsDstKey || delAddr || valSrcAddr
|
||||||
|
key := make([]byte, len(REDSToValsDstKey)+2*v040auth.AddrLen)
|
||||||
|
copy(key[0:offset], REDSToValsDstKey)
|
||||||
|
copy(key[offset:offset+v040auth.AddrLen], delAddr.Bytes())
|
||||||
|
copy(key[offset+v040auth.AddrLen:offset+2*v040auth.AddrLen], valSrcAddr.Bytes())
|
||||||
|
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetREDKeyFromValSrcIndexKey rearranges the ValSrcIndexKey to get the REDKey
|
||||||
|
func GetREDKeyFromValSrcIndexKey(indexKey []byte) []byte {
|
||||||
|
// note that first byte is prefix byte
|
||||||
|
if len(indexKey) != 3*v040auth.AddrLen+1 {
|
||||||
|
panic("unexpected key length")
|
||||||
|
}
|
||||||
|
|
||||||
|
valSrcAddr := indexKey[1 : v040auth.AddrLen+1]
|
||||||
|
delAddr := indexKey[v040auth.AddrLen+1 : 2*v040auth.AddrLen+1]
|
||||||
|
valDstAddr := indexKey[2*v040auth.AddrLen+1 : 3*v040auth.AddrLen+1]
|
||||||
|
|
||||||
|
return GetREDKey(delAddr, valSrcAddr, valDstAddr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetREDKeyFromValDstIndexKey rearranges the ValDstIndexKey to get the REDKey
|
||||||
|
func GetREDKeyFromValDstIndexKey(indexKey []byte) []byte {
|
||||||
|
// note that first byte is prefix byte
|
||||||
|
if len(indexKey) != 3*v040auth.AddrLen+1 {
|
||||||
|
panic("unexpected key length")
|
||||||
|
}
|
||||||
|
|
||||||
|
valDstAddr := indexKey[1 : v040auth.AddrLen+1]
|
||||||
|
delAddr := indexKey[v040auth.AddrLen+1 : 2*v040auth.AddrLen+1]
|
||||||
|
valSrcAddr := indexKey[2*v040auth.AddrLen+1 : 3*v040auth.AddrLen+1]
|
||||||
|
|
||||||
|
return GetREDKey(delAddr, valSrcAddr, valDstAddr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRedelegationTimeKey returns a key prefix for indexing an unbonding
|
||||||
|
// redelegation based on a completion time.
|
||||||
|
func GetRedelegationTimeKey(timestamp time.Time) []byte {
|
||||||
|
bz := sdk.FormatTimeBytes(timestamp)
|
||||||
|
return append(RedelegationQueueKey, bz...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetREDsKey returns a key prefix for indexing a redelegation from a delegator
|
||||||
|
// address.
|
||||||
|
func GetREDsKey(delAddr sdk.AccAddress) []byte {
|
||||||
|
return append(RedelegationKey, delAddr.Bytes()...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetREDsFromValSrcIndexKey returns a key prefix for indexing a redelegation to
|
||||||
|
// a source validator.
|
||||||
|
func GetREDsFromValSrcIndexKey(valSrcAddr sdk.ValAddress) []byte {
|
||||||
|
return append(RedelegationByValSrcIndexKey, valSrcAddr.Bytes()...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetREDsToValDstIndexKey returns a key prefix for indexing a redelegation to a
|
||||||
|
// destination (target) validator.
|
||||||
|
func GetREDsToValDstIndexKey(valDstAddr sdk.ValAddress) []byte {
|
||||||
|
return append(RedelegationByValDstIndexKey, valDstAddr.Bytes()...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetREDsByDelToValDstIndexKey returns a key prefix for indexing a redelegation
|
||||||
|
// from an address to a source validator.
|
||||||
|
func GetREDsByDelToValDstIndexKey(delAddr sdk.AccAddress, valDstAddr sdk.ValAddress) []byte {
|
||||||
|
return append(GetREDsToValDstIndexKey(valDstAddr), delAddr.Bytes()...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetHistoricalInfoKey returns a key prefix for indexing HistoricalInfo objects.
|
||||||
|
func GetHistoricalInfoKey(height int64) []byte {
|
||||||
|
return append(HistoricalInfoKey, []byte(strconv.FormatInt(height, 10))...)
|
||||||
|
}
|
|
@ -1,5 +0,0 @@
|
||||||
package v040
|
|
||||||
|
|
||||||
const (
|
|
||||||
ModuleName = "staking"
|
|
||||||
)
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
package v042
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/cosmos/cosmos-sdk/store/prefix"
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
"github.com/cosmos/cosmos-sdk/types/address"
|
||||||
|
v040auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v040"
|
||||||
|
v042distribution "github.com/cosmos/cosmos-sdk/x/distribution/legacy/v042"
|
||||||
|
v040staking "github.com/cosmos/cosmos-sdk/x/staking/legacy/v040"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/staking/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// migratePrefixAddressAddressAddress is a helper function that migrates all keys of format:
|
||||||
|
// prefix_bytes | address_1_bytes | address_2_bytes | address_3_bytes
|
||||||
|
// into format:
|
||||||
|
// prefix_bytes | address_1_len (1 byte) | address_1_bytes | address_2_len (1 byte) | address_2_bytes | address_3_len (1 byte) | address_3_bytes
|
||||||
|
func migratePrefixAddressAddressAddress(store sdk.KVStore, prefixBz []byte) {
|
||||||
|
oldStore := prefix.NewStore(store, prefixBz)
|
||||||
|
|
||||||
|
oldStoreIter := oldStore.Iterator(nil, nil)
|
||||||
|
defer oldStoreIter.Close()
|
||||||
|
|
||||||
|
for ; oldStoreIter.Valid(); oldStoreIter.Next() {
|
||||||
|
addr1 := oldStoreIter.Key()[:v040auth.AddrLen]
|
||||||
|
addr2 := oldStoreIter.Key()[v040auth.AddrLen : 2*v040auth.AddrLen]
|
||||||
|
addr3 := oldStoreIter.Key()[2*v040auth.AddrLen:]
|
||||||
|
newStoreKey := append(append(append(
|
||||||
|
prefixBz,
|
||||||
|
address.MustLengthPrefix(addr1)...), address.MustLengthPrefix(addr2)...), address.MustLengthPrefix(addr3)...,
|
||||||
|
)
|
||||||
|
|
||||||
|
// Set new key on store. Values don't change.
|
||||||
|
store.Set(newStoreKey, oldStoreIter.Value())
|
||||||
|
oldStore.Delete(oldStoreIter.Key())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const powerBytesLen = 8
|
||||||
|
|
||||||
|
func migrateValidatorsByPowerIndexKey(store sdk.KVStore) {
|
||||||
|
oldStore := prefix.NewStore(store, v040staking.ValidatorsByPowerIndexKey)
|
||||||
|
|
||||||
|
oldStoreIter := oldStore.Iterator(nil, nil)
|
||||||
|
defer oldStoreIter.Close()
|
||||||
|
|
||||||
|
for ; oldStoreIter.Valid(); oldStoreIter.Next() {
|
||||||
|
powerBytes := oldStoreIter.Key()[:powerBytesLen]
|
||||||
|
valAddr := oldStoreIter.Key()[powerBytesLen:]
|
||||||
|
newStoreKey := append(append(types.ValidatorsByPowerIndexKey, powerBytes...), address.MustLengthPrefix(valAddr)...)
|
||||||
|
|
||||||
|
// Set new key on store. Values don't change.
|
||||||
|
store.Set(newStoreKey, oldStoreIter.Value())
|
||||||
|
oldStore.Delete(oldStoreIter.Key())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MigrateStore performs in-place store migrations from v0.40 to v0.42. The
|
||||||
|
// migration includes:
|
||||||
|
//
|
||||||
|
// - Change addresses to be length-prefixed.
|
||||||
|
func MigrateStore(ctx sdk.Context, storeKey sdk.StoreKey) error {
|
||||||
|
store := ctx.KVStore(storeKey)
|
||||||
|
|
||||||
|
v042distribution.MigratePrefixAddress(store, v040staking.LastValidatorPowerKey)
|
||||||
|
|
||||||
|
v042distribution.MigratePrefixAddress(store, v040staking.ValidatorsKey)
|
||||||
|
v042distribution.MigratePrefixAddress(store, v040staking.ValidatorsByConsAddrKey)
|
||||||
|
migrateValidatorsByPowerIndexKey(store)
|
||||||
|
|
||||||
|
v042distribution.MigratePrefixAddressAddress(store, v040staking.DelegationKey)
|
||||||
|
v042distribution.MigratePrefixAddressAddress(store, v040staking.UnbondingDelegationKey)
|
||||||
|
v042distribution.MigratePrefixAddressAddress(store, v040staking.UnbondingDelegationByValIndexKey)
|
||||||
|
migratePrefixAddressAddressAddress(store, v040staking.RedelegationKey)
|
||||||
|
migratePrefixAddressAddressAddress(store, v040staking.RedelegationByValSrcIndexKey)
|
||||||
|
migratePrefixAddressAddressAddress(store, v040staking.RedelegationByValDstIndexKey)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,137 @@
|
||||||
|
package v042_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/testutil"
|
||||||
|
"github.com/cosmos/cosmos-sdk/testutil/testdata"
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
v040staking "github.com/cosmos/cosmos-sdk/x/staking/legacy/v040"
|
||||||
|
v042staking "github.com/cosmos/cosmos-sdk/x/staking/legacy/v042"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/staking/teststaking"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/staking/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestStoreMigration(t *testing.T) {
|
||||||
|
stakingKey := sdk.NewKVStoreKey("staking")
|
||||||
|
ctx := testutil.DefaultContext(stakingKey, sdk.NewTransientStoreKey("transient_test"))
|
||||||
|
store := ctx.KVStore(stakingKey)
|
||||||
|
|
||||||
|
_, pk1, addr1 := testdata.KeyTestPubAddr()
|
||||||
|
valAddr1 := sdk.ValAddress(addr1)
|
||||||
|
val := teststaking.NewValidator(t, valAddr1, pk1)
|
||||||
|
_, pk1, addr2 := testdata.KeyTestPubAddr()
|
||||||
|
valAddr2 := sdk.ValAddress(addr2)
|
||||||
|
_, _, addr3 := testdata.KeyTestPubAddr()
|
||||||
|
consAddr := sdk.ConsAddress(addr3.String())
|
||||||
|
_, _, addr4 := testdata.KeyTestPubAddr()
|
||||||
|
now := time.Now()
|
||||||
|
// Use dummy value for all keys.
|
||||||
|
value := []byte("foo")
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
oldKey []byte
|
||||||
|
newKey []byte
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"LastValidatorPowerKey",
|
||||||
|
v040staking.GetLastValidatorPowerKey(valAddr1),
|
||||||
|
types.GetLastValidatorPowerKey(valAddr1),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"LastTotalPowerKey",
|
||||||
|
v040staking.LastTotalPowerKey,
|
||||||
|
types.LastTotalPowerKey,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ValidatorsKey",
|
||||||
|
v040staking.GetValidatorKey(valAddr1),
|
||||||
|
types.GetValidatorKey(valAddr1),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ValidatorsByConsAddrKey",
|
||||||
|
v040staking.GetValidatorByConsAddrKey(consAddr),
|
||||||
|
types.GetValidatorByConsAddrKey(consAddr),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ValidatorsByPowerIndexKey",
|
||||||
|
v040staking.GetValidatorsByPowerIndexKey(val),
|
||||||
|
types.GetValidatorsByPowerIndexKey(val),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"DelegationKey",
|
||||||
|
v040staking.GetDelegationKey(addr4, valAddr1),
|
||||||
|
types.GetDelegationKey(addr4, valAddr1),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"UnbondingDelegationKey",
|
||||||
|
v040staking.GetUBDKey(addr4, valAddr1),
|
||||||
|
types.GetUBDKey(addr4, valAddr1),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"UnbondingDelegationByValIndexKey",
|
||||||
|
v040staking.GetUBDByValIndexKey(addr4, valAddr1),
|
||||||
|
types.GetUBDByValIndexKey(addr4, valAddr1),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"RedelegationKey",
|
||||||
|
v040staking.GetREDKey(addr4, valAddr1, valAddr2),
|
||||||
|
types.GetREDKey(addr4, valAddr1, valAddr2),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"RedelegationByValSrcIndexKey",
|
||||||
|
v040staking.GetREDByValSrcIndexKey(addr4, valAddr1, valAddr2),
|
||||||
|
types.GetREDByValSrcIndexKey(addr4, valAddr1, valAddr2),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"RedelegationByValDstIndexKey",
|
||||||
|
v040staking.GetREDByValDstIndexKey(addr4, valAddr1, valAddr2),
|
||||||
|
types.GetREDByValDstIndexKey(addr4, valAddr1, valAddr2),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"UnbondingQueueKey",
|
||||||
|
v040staking.GetUnbondingDelegationTimeKey(now),
|
||||||
|
types.GetUnbondingDelegationTimeKey(now),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"RedelegationQueueKey",
|
||||||
|
v040staking.GetRedelegationTimeKey(now),
|
||||||
|
types.GetRedelegationTimeKey(now),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ValidatorQueueKey",
|
||||||
|
v040staking.GetValidatorQueueKey(now, 4),
|
||||||
|
types.GetValidatorQueueKey(now, 4),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"HistoricalInfoKey",
|
||||||
|
v040staking.GetHistoricalInfoKey(4),
|
||||||
|
types.GetHistoricalInfoKey(4),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set all the old keys to the store
|
||||||
|
for _, tc := range testCases {
|
||||||
|
store.Set(tc.oldKey, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run migrations.
|
||||||
|
err := v042staking.MigrateStore(ctx, stakingKey)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Make sure the new keys are set and old keys are deleted.
|
||||||
|
for _, tc := range testCases {
|
||||||
|
tc := tc
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
if bytes.Compare(tc.oldKey, tc.newKey) != 0 {
|
||||||
|
require.Nil(t, store.Get(tc.oldKey))
|
||||||
|
}
|
||||||
|
require.Equal(t, value, store.Get(tc.newKey))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -138,6 +138,9 @@ func (am AppModule) RegisterServices(cfg module.Configurator) {
|
||||||
types.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServerImpl(am.keeper))
|
types.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServerImpl(am.keeper))
|
||||||
querier := keeper.Querier{Keeper: am.keeper}
|
querier := keeper.Querier{Keeper: am.keeper}
|
||||||
types.RegisterQueryServer(cfg.QueryServer(), querier)
|
types.RegisterQueryServer(cfg.QueryServer(), querier)
|
||||||
|
|
||||||
|
m := keeper.NewMigrator(am.keeper)
|
||||||
|
cfg.RegisterMigration(types.ModuleName, 1, m.Migrate1to2)
|
||||||
}
|
}
|
||||||
|
|
||||||
// InitGenesis performs genesis initialization for the staking module. It returns
|
// InitGenesis performs genesis initialization for the staking module. It returns
|
||||||
|
@ -158,7 +161,7 @@ func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONMarshaler) json
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConsensusVersion implements AppModule/ConsensusVersion.
|
// ConsensusVersion implements AppModule/ConsensusVersion.
|
||||||
func (AppModule) ConsensusVersion() uint64 { return 1 }
|
func (AppModule) ConsensusVersion() uint64 { return 2 }
|
||||||
|
|
||||||
// BeginBlock returns the begin blocker for the staking module.
|
// BeginBlock returns the begin blocker for the staking module.
|
||||||
func (am AppModule) BeginBlock(ctx sdk.Context, _ abci.RequestBeginBlock) {
|
func (am AppModule) BeginBlock(ctx sdk.Context, _ abci.RequestBeginBlock) {
|
||||||
|
|
|
@ -44,10 +44,10 @@ required lookups for slashing and validator-set updates. A third special index
|
||||||
throughout each block, unlike the first two indices which mirror the validator
|
throughout each block, unlike the first two indices which mirror the validator
|
||||||
records within a block.
|
records within a block.
|
||||||
|
|
||||||
- Validators: `0x21 | OperatorAddr -> ProtocolBuffer(validator)`
|
- Validators: `0x21 | OperatorAddrLen (1 byte) | OperatorAddr -> ProtocolBuffer(validator)`
|
||||||
- ValidatorsByConsAddr: `0x22 | ConsAddr -> OperatorAddr`
|
- ValidatorsByConsAddr: `0x22 | ConsAddrLen (1 byte) | ConsAddr -> OperatorAddr`
|
||||||
- ValidatorsByPower: `0x23 | BigEndian(ConsensusPower) | OperatorAddr -> OperatorAddr`
|
- ValidatorsByPower: `0x23 | BigEndian(ConsensusPower) | OperatorAddrLen (1 byte) | OperatorAddr -> OperatorAddr`
|
||||||
- LastValidatorsPower: `0x11 OperatorAddr -> ProtocolBuffer(ConsensusPower)`
|
- LastValidatorsPower: `0x11 | OperatorAddrLen (1 byte) | OperatorAddr -> ProtocolBuffer(ConsensusPower)`
|
||||||
|
|
||||||
`Validators` is the primary index - it ensures that each operator can have only one
|
`Validators` is the primary index - it ensures that each operator can have only one
|
||||||
associated validator, where the public key of that validator can change in the
|
associated validator, where the public key of that validator can change in the
|
||||||
|
@ -79,7 +79,7 @@ Each validator's state is stored in a `Validator` struct:
|
||||||
Delegations are identified by combining `DelegatorAddr` (the address of the delegator)
|
Delegations are identified by combining `DelegatorAddr` (the address of the delegator)
|
||||||
with the `ValidatorAddr` Delegators are indexed in the store as follows:
|
with the `ValidatorAddr` Delegators are indexed in the store as follows:
|
||||||
|
|
||||||
- Delegation: `0x31 | DelegatorAddr | ValidatorAddr -> ProtocolBuffer(delegation)`
|
- Delegation: `0x31 | DelegatorAddrLen (1 byte) | DelegatorAddr | ValidatorAddrLen (1 byte) | ValidatorAddr -> ProtocolBuffer(delegation)`
|
||||||
|
|
||||||
Stake holders may delegate coins to validators; under this circumstance their
|
Stake holders may delegate coins to validators; under this circumstance their
|
||||||
funds are held in a `Delegation` data structure. It is owned by one
|
funds are held in a `Delegation` data structure. It is owned by one
|
||||||
|
@ -115,8 +115,8 @@ detected.
|
||||||
|
|
||||||
`UnbondingDelegation` are indexed in the store as:
|
`UnbondingDelegation` are indexed in the store as:
|
||||||
|
|
||||||
- UnbondingDelegation: `0x32 | DelegatorAddr | ValidatorAddr -> ProtocolBuffer(unbondingDelegation)`
|
- UnbondingDelegation: `0x32 | DelegatorAddrLen (1 byte) | DelegatorAddr | ValidatorAddrLen (1 byte) | ValidatorAddr -> ProtocolBuffer(unbondingDelegation)`
|
||||||
- UnbondingDelegationsFromValidator: `0x33 | ValidatorAddr | DelegatorAddr -> nil`
|
- UnbondingDelegationsFromValidator: `0x33 | ValidatorAddrLen (1 byte) | ValidatorAddr | DelegatorAddrLen (1 byte) | DelegatorAddr -> nil`
|
||||||
|
|
||||||
The first map here is used in queries, to lookup all unbonding delegations for
|
The first map here is used in queries, to lookup all unbonding delegations for
|
||||||
a given delegator, while the second map is used in slashing, to lookup all
|
a given delegator, while the second map is used in slashing, to lookup all
|
||||||
|
@ -137,9 +137,9 @@ committed by the source validator.
|
||||||
|
|
||||||
`Redelegation` are indexed in the store as:
|
`Redelegation` are indexed in the store as:
|
||||||
|
|
||||||
- Redelegations: `0x34 | DelegatorAddr | ValidatorSrcAddr | ValidatorDstAddr -> ProtocolBuffer(redelegation)`
|
- Redelegations: `0x34 | DelegatorAddrLen (1 byte) | DelegatorAddr | ValidatorAddrLen (1 byte) | ValidatorSrcAddr | ValidatorDstAddr -> ProtocolBuffer(redelegation)`
|
||||||
- RedelegationsBySrc: `0x35 | ValidatorSrcAddr | ValidatorDstAddr | DelegatorAddr -> nil`
|
- RedelegationsBySrc: `0x35 | ValidatorSrcAddrLen (1 byte) | ValidatorSrcAddr | ValidatorDstAddrLen (1 byte) | ValidatorDstAddr | DelegatorAddrLen (1 byte) | DelegatorAddr -> nil`
|
||||||
- RedelegationsByDst: `0x36 | ValidatorDstAddr | ValidatorSrcAddr | DelegatorAddr -> nil`
|
- RedelegationsByDst: `0x36 | ValidatorDstAddrLen (1 byte) | ValidatorDstAddr | ValidatorSrcAddrLen (1 byte) | ValidatorSrcAddr | DelegatorAddrLen (1 byte) | DelegatorAddr -> nil`
|
||||||
|
|
||||||
The first map here is used for queries, to lookup all redelegations for a given
|
The first map here is used for queries, to lookup all redelegations for a given
|
||||||
delegator. The second map is used for slashing based on the `ValidatorSrcAddr`,
|
delegator. The second map is used for slashing based on the `ValidatorSrcAddr`,
|
||||||
|
|
Loading…
Reference in New Issue