diff --git a/CHANGELOG.md b/CHANGELOG.md index ee7c588c0113..01329c14d4b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,12 +37,19 @@ Ref: https://keepachangelog.com/en/1.0.0/ ## [Unreleased] +## [State Breaking] + * (store) [#525](https://github.com/osmosis-labs/cosmos-sdk/pull/525) CacheKV speedups * (slashing) [#548](https://github.com/osmosis-labs/cosmos-sdk/pull/548) Implement v0.50 slashing bitmap logic * (slashing) [#543](https://github.com/osmosis-labs/cosmos-sdk/pull/543) Make slashing not write sign info every block * (authz) [#513](https://github.com/osmosis-labs/cosmos-sdk/pull/513) Limit expired authz grant pruning to 200 per block * (gov) [#514](https://github.com/osmosis-labs/cosmos-sdk/pull/514) Let gov hooks return an error * (gov) [#510](https://github.com/osmosis-labs/cosmos-sdk/pull/510) Implement a minimum amount per deposit +* (slashing) [#580](https://github.com/osmosis-labs/cosmos-sdk/pull/580) Less time intensive slashing migration + +## [State Compatible] + +* (coin) [#570](https://github.com/osmosis-labs/cosmos-sdk/pull/570) Removal of regex usage on denom validation ## IAVL v23 v1 Releases diff --git a/tests/e2e/authz/tx.go b/tests/e2e/authz/tx.go index 03d39c79441a..aad6f4cf6749 100644 --- a/tests/e2e/authz/tx.go +++ b/tests/e2e/authz/tx.go @@ -339,7 +339,7 @@ func (s *E2ETestSuite) TestCLITxGrantAuthorization() { }, 0, true, - "invalid decimal coin expression", + "invalid character in denomination", }, { "valid tx delegate authorization allowed validators", diff --git a/tests/integration/bank/keeper/deterministic_test.go b/tests/integration/bank/keeper/deterministic_test.go index 112d11489260..1bc3d4de8cb5 100644 --- a/tests/integration/bank/keeper/deterministic_test.go +++ b/tests/integration/bank/keeper/deterministic_test.go @@ -36,7 +36,7 @@ type DeterministicTestSuite struct { } var ( - denomRegex = sdk.DefaultCoinDenomRegex() + denomRegex = `[a-zA-Z][a-zA-Z0-9/:._-]{2,127}` addr1 = sdk.MustAccAddressFromBech32("cosmos139f7kncmglres2nf3h4hc4tade85ekfr8sulz5") coin1 = sdk.NewCoin("denom", sdk.NewInt(10)) metadataAtom = banktypes.Metadata{ diff --git a/tests/integration/staking/keeper/determinstic_test.go b/tests/integration/staking/keeper/determinstic_test.go index eefb8c7ec819..08f17fcd8ae8 100644 --- a/tests/integration/staking/keeper/determinstic_test.go +++ b/tests/integration/staking/keeper/determinstic_test.go @@ -687,9 +687,10 @@ func (suite *DeterministicTestSuite) TestGRPCRedelegations() { } func (suite *DeterministicTestSuite) TestGRPCParams() { + coinDenomRegex := `[a-zA-Z][a-zA-Z0-9/:._-]{2,127}` rapid.Check(suite.T(), func(t *rapid.T) { params := stakingtypes.Params{ - BondDenom: rapid.StringMatching(sdk.DefaultCoinDenomRegex()).Draw(t, "bond-denom"), + BondDenom: rapid.StringMatching(coinDenomRegex).Draw(t, "bond-denom"), UnbondingTime: durationGenerator().Draw(t, "duration"), MaxValidators: rapid.Uint32Min(1).Draw(t, "max-validators"), MaxEntries: rapid.Uint32Min(1).Draw(t, "max-entries"), diff --git a/types/coin.go b/types/coin.go index 41a8b24f52cf..dbf7dc50cc31 100644 --- a/types/coin.go +++ b/types/coin.go @@ -6,6 +6,7 @@ import ( "regexp" "sort" "strings" + "unicode" ) //----------------------------------------------------------------------------- @@ -835,30 +836,15 @@ func (coins Coins) Sort() Coins { return coins } -//----------------------------------------------------------------------------- -// Parsing - var ( - // Denominations can be 3 ~ 128 characters long and support letters, followed by either - // a letter, a number or a separator ('/', ':', '.', '_' or '-'). - reDnmString = `[a-zA-Z][a-zA-Z0-9/:._-]{2,127}` - reDecAmt = `[[:digit:]]+(?:\.[[:digit:]]+)?|\.[[:digit:]]+` - reSpc = `[[:space:]]*` - reDnm *regexp.Regexp - reDecCoin *regexp.Regexp -) + reDecAmt = `[[:digit:]]+(?:\.[[:digit:]]+)?|\.[[:digit:]]+` + reSpc = `[[:space:]]*` -func init() { - SetCoinDenomRegex(DefaultCoinDenomRegex) -} + coinDenomRegex func() string -// DefaultCoinDenomRegex returns the default regex string -func DefaultCoinDenomRegex() string { - return reDnmString -} - -// coinDenomRegex returns the current regex string and can be overwritten for custom validation -var coinDenomRegex = DefaultCoinDenomRegex + reDnm *regexp.Regexp + reDecCoin *regexp.Regexp +) // SetCoinDenomRegex allows for coin's custom validation by overriding the regular // expression string used for denom validation. @@ -871,12 +857,49 @@ func SetCoinDenomRegex(reFn func() string) { // ValidateDenom is the default validation function for Coin.Denom. func ValidateDenom(denom string) error { - if !reDnm.MatchString(denom) { + if reDnm == nil || reDecCoin == nil { //nolint:gocritic + // Convert the string to a byte slice as required by the Ragel-generated function. + + // Call the Ragel-generated function. + if !MatchDenom(denom) { //nolint:gocritic + return fmt.Errorf("invalid denom: %s", denom) + } + } else if !reDnm.MatchString(denom) { // If reDnm has been initialized, use it for matching. return fmt.Errorf("invalid denom: %s", denom) } + return nil } +// isValidRune checks if a given rune is a valid character for a rune. +// It returns true if the rune is a letter, digit, '/', ':', '.', '_', or '-'. +func isValidRune(r rune) bool { + return unicode.IsLetter(r) || unicode.IsDigit(r) || r == '/' || r == ':' || r == '.' || r == '_' || r == '-' +} + +// MatchDenom checks if the given string is a valid denomination. +// A valid denomination must have a length between 3 and 128 characters, +// start with a letter, and only contain valid runes. +func MatchDenom(s string) bool { + length := len(s) + if length < 3 || length > 128 { + return false + } + + firstRune := rune(s[0]) + if !unicode.IsLetter(firstRune) { + return false + } + + for _, r := range s[1:] { + if !isValidRune(r) { + return false + } + } + + return true +} + func mustValidateDenom(denom string) { if err := ValidateDenom(denom); err != nil { panic(err) diff --git a/types/coin_test.go b/types/coin_test.go index f2337c1586f3..1d8b663017b7 100644 --- a/types/coin_test.go +++ b/types/coin_test.go @@ -112,6 +112,8 @@ func (s *coinTestSuite) TestCoinIsValid() { func (s *coinTestSuite) TestCustomValidation() { newDnmRegex := `[\x{1F600}-\x{1F6FF}]` + reDnmString := `[a-zA-Z][a-zA-Z0-9/:._-]{2,127}` + sdk.SetCoinDenomRegex(func() string { return newDnmRegex }) @@ -130,7 +132,7 @@ func (s *coinTestSuite) TestCustomValidation() { for i, tc := range cases { s.Require().Equal(tc.expectPass, tc.coin.IsValid(), "unexpected result for IsValid, tc #%d", i) } - sdk.SetCoinDenomRegex(sdk.DefaultCoinDenomRegex) + sdk.SetCoinDenomRegex(func() string { return reDnmString }) } func (s *coinTestSuite) TestCoinsDenoms() { @@ -998,6 +1000,33 @@ func (s *coinTestSuite) TestParseCoins() { } } +func (s *coinTestSuite) TestValidateDenom() { + cases := []struct { + input string + valid bool + }{ + {"", false}, + {"stake", true}, + {"stake,", false}, + {"me coin", false}, + {"me coin much", false}, + {"not a coin", false}, + {"foo:bar", true}, // special characters '/' | ':' | '.' | '_' | '-' are allowed + {"atom10", true}, // number in denom is allowed + {"transfer/channelToA/uatom", true}, + {"ibc/7F1D3FCF4AE79E1554D670D1AD949A9BA4E4A3C76C63093E17E446A46061A7A2", true}, + } + + for _, tc := range cases { + err := sdk.ValidateDenom(tc.input) + if !tc.valid { + s.Require().Error(err) + } else { + s.Require().NoError(err) + } + } +} + func (s *coinTestSuite) TestSortCoins() { good := sdk.Coins{ sdk.NewInt64Coin("gas", 1), diff --git a/types/dec_coin.go b/types/dec_coin.go index 42ff885d58a6..a279471db307 100644 --- a/types/dec_coin.go +++ b/types/dec_coin.go @@ -4,6 +4,7 @@ import ( "fmt" "sort" "strings" + "unicode" "github.com/pkg/errors" ) @@ -621,14 +622,23 @@ func (coins DecCoins) Sort() DecCoins { // ParseDecCoin parses a decimal coin from a string, returning an error if // invalid. An empty string is considered invalid. func ParseDecCoin(coinStr string) (coin DecCoin, err error) { - coinStr = strings.TrimSpace(coinStr) + var amountStr, denomStr string + // if custom parsing has not been set, use default coin regex + if reDecCoin == nil { //nolint:gocritic + amountStr, denomStr, err = ParseDecAmount(coinStr) + if err != nil { + return DecCoin{}, err + } + } else { + coinStr = strings.TrimSpace(coinStr) - matches := reDecCoin.FindStringSubmatch(coinStr) - if matches == nil { - return DecCoin{}, fmt.Errorf("invalid decimal coin expression: %s", coinStr) - } + matches := reDecCoin.FindStringSubmatch(coinStr) + if matches == nil { + return DecCoin{}, fmt.Errorf("invalid decimal coin expression: %s", coinStr) + } - amountStr, denomStr := matches[1], matches[2] + amountStr, denomStr = matches[1], matches[2] + } amount, err := NewDecFromStr(amountStr) if err != nil { @@ -642,6 +652,50 @@ func ParseDecCoin(coinStr string) (coin DecCoin, err error) { return NewDecCoinFromDec(denomStr, amount), nil } +// ParseDecAmount parses the given string into amount, denomination. +func ParseDecAmount(coinStr string) (string, string, error) { + var amountRune, denomRune []rune + + // Indicates the start of denom parsing + seenLetter := false + // Indicates we're currently parsing the amount + parsingAmount := true + + for _, r := range strings.TrimSpace(coinStr) { + if parsingAmount { //nolint:gocritic + if unicode.IsDigit(r) || r == '.' { //nolint:gocritic + amountRune = append(amountRune, r) + } else if unicode.IsSpace(r) { // if space is seen, indicates that we have finished parsing amount + parsingAmount = false + } else if unicode.IsLetter(r) { // if letter is seen, indicates that it is the start of denom + parsingAmount = false + seenLetter = true + denomRune = append(denomRune, r) + } else { // Invalid character encountered in amount part + return "", "", fmt.Errorf("invalid character in coin string: %s", string(r)) + } + } else if !seenLetter { // This logic flow is for skipping spaces between amount and denomination + if unicode.IsLetter(r) { + seenLetter = true + denomRune = append(denomRune, r) + } else if !unicode.IsSpace(r) { + // Invalid character before denomination starts + return "", "", fmt.Errorf("invalid start of denomination: %s", string(r)) + } + } else { + // Parsing the denomination + if unicode.IsLetter(r) || unicode.IsDigit(r) || r == '/' || r == ':' || r == '.' || r == '_' || r == '-' { + denomRune = append(denomRune, r) + } else { + // Invalid character encountered in denomination part + return "", "", fmt.Errorf("invalid character in denomination: %s", string(r)) + } + } + } + + return string(amountRune), string(denomRune), nil +} + // ParseDecCoins will parse out a list of decimal coins separated by commas. If the parsing is successuful, // the provided coins will be sanitized by removing zero coins and sorting the coin set. Lastly // a validation of the coin set is executed. If the check passes, ParseDecCoins will return the sanitized coins. diff --git a/x/authz/client/cli/tx_test.go b/x/authz/client/cli/tx_test.go index 7355c3591601..b5e7ee74eddb 100644 --- a/x/authz/client/cli/tx_test.go +++ b/x/authz/client/cli/tx_test.go @@ -326,7 +326,7 @@ func (s *CLITestSuite) TestCLITxGrantAuthorization() { fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(10))).String()), }, true, - "invalid decimal coin expression", + "nvalid character in denomination: ", }, { "Valid tx send authorization", diff --git a/x/gov/client/cli/util_test.go b/x/gov/client/cli/util_test.go index 53b40293e376..05922f8c205d 100644 --- a/x/gov/client/cli/util_test.go +++ b/x/gov/client/cli/util_test.go @@ -347,7 +347,7 @@ func TestReadGovPropFlags(t *testing.T) { name: "only deposit invalid coins", fromAddr: nil, args: []string{argDeposit, "not really coins"}, - expErr: []string{"invalid deposit", "invalid decimal coin expression", "not really coins"}, + expErr: []string{"invalid deposit", "invalid character in denomination"}, }, { name: "only deposit two coins", @@ -377,19 +377,19 @@ func TestReadGovPropFlags(t *testing.T) { name: "only deposit coin 1 of 3 bad", fromAddr: nil, args: []string{argDeposit, "1bad^coin,2bcoin,3ccoin"}, - expErr: []string{"invalid deposit", "invalid decimal coin expression", "1bad^coin"}, + expErr: []string{"invalid deposit", "invalid character in denomination"}, }, { name: "only deposit coin 2 of 3 bad", fromAddr: nil, args: []string{argDeposit, "1acoin,2bad^coin,3ccoin"}, - expErr: []string{"invalid deposit", "invalid decimal coin expression", "2bad^coin"}, + expErr: []string{"invalid deposit", "invalid character in denomination"}, }, { name: "only deposit coin 3 of 3 bad", fromAddr: nil, args: []string{argDeposit, "1acoin,2bcoin,3bad^coin"}, - expErr: []string{"invalid deposit", "invalid decimal coin expression", "3bad^coin"}, + expErr: []string{"invalid deposit", "invalid character in denomination"}, }, // As far as I can tell, there's no way to make flagSet.GetString return an error for a defined string flag. // So I don't have a test for the "could not read deposit" error case. diff --git a/x/slashing/abci.go b/x/slashing/abci.go index 10ae34232a88..dc8099f146ef 100644 --- a/x/slashing/abci.go +++ b/x/slashing/abci.go @@ -11,6 +11,8 @@ import ( "github.com/cosmos/cosmos-sdk/x/slashing/types" ) +var deprecatedBitArrayPruneLimitPerBlock = 2000 + // BeginBlocker check for infraction evidence or downtime of validators // on every begin block func BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock, k keeper.Keeper) { @@ -23,4 +25,7 @@ func BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock, k keeper.Keeper) for _, voteInfo := range req.LastCommitInfo.GetVotes() { k.HandleValidatorSignatureWithParams(ctx, params, voteInfo.Validator.Address, voteInfo.Validator.Power, voteInfo.SignedLastBlock) } + + // If there are still entries for the deprecated MissedBlockBitArray, delete them up until we hit the per block limit + k.DeleteDeprecatedValidatorMissedBlockBitArray(ctx, deprecatedBitArrayPruneLimitPerBlock) } diff --git a/x/slashing/keeper/keeper.go b/x/slashing/keeper/keeper.go index 5535dd2436c9..60ebce041ebb 100644 --- a/x/slashing/keeper/keeper.go +++ b/x/slashing/keeper/keeper.go @@ -9,6 +9,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" sdk "github.com/cosmos/cosmos-sdk/types" + v4 "github.com/cosmos/cosmos-sdk/x/slashing/migrations/v4" "github.com/cosmos/cosmos-sdk/x/slashing/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) @@ -115,3 +116,44 @@ func (k Keeper) deleteAddrPubkeyRelation(ctx sdk.Context, addr cryptotypes.Addre store := ctx.KVStore(k.storeKey) store.Delete(types.AddrPubkeyRelationKey(addr)) } + +func (k Keeper) DeleteDeprecatedValidatorMissedBlockBitArray(ctx sdk.Context, iterationLimit int) { + store := ctx.KVStore(k.storeKey) + if store.Get(types.IsPruningKey) == nil { + return + } + + // Iterate over all the validator signing infos and delete the deprecated missed block bit arrays + valSignInfoIter := sdk.KVStorePrefixIterator(store, types.ValidatorSigningInfoKeyPrefix) + defer valSignInfoIter.Close() + + iterationCounter := 0 + for ; valSignInfoIter.Valid(); valSignInfoIter.Next() { + address := types.ValidatorSigningInfoAddress(valSignInfoIter.Key()) + + // Creat anonymous function to scope defer statement + func() { + iter := storetypes.KVStorePrefixIterator(store, v4.DeprecatedValidatorMissedBlockBitArrayPrefixKey(address)) + defer iter.Close() + + for ; iter.Valid(); iter.Next() { + store.Delete(iter.Key()) + iterationCounter++ + if iterationCounter >= iterationLimit { + break + } + } + }() + + if iterationCounter >= iterationLimit { + break + } + } + + ctx.Logger().Info("Deleted deprecated missed block bit arrays", "count", iterationCounter) + + // If we have deleted all the deprecated missed block bit arrays, we can delete the pruning key (set to nil) + if iterationCounter == 0 { + store.Delete(types.IsPruningKey) + } +} diff --git a/x/slashing/migrations/v4/keys.go b/x/slashing/migrations/v4/keys.go index c5ab30ff72bd..419b043cbe19 100644 --- a/x/slashing/migrations/v4/keys.go +++ b/x/slashing/migrations/v4/keys.go @@ -11,8 +11,13 @@ import ( const MissedBlockBitmapChunkSize = 1024 // 2^10 bits var ( - ValidatorSigningInfoKeyPrefix = []byte{0x01} - validatorMissedBlockBitArrayKeyPrefix = []byte{0x02} + ValidatorSigningInfoKeyPrefix = []byte{0x01} + deprecatedValidatorMissedBlockBitArrayKeyPrefix = []byte{0x02} + + // NOTE: sdk v0.50 uses the same key prefix for both deprecated and new missed block bitmaps. + // We needed to use a new key, because we are skipping deletion of all old keys at upgrade time + // due to how long this would bring the chain down. We use 0x10 here to prevent overlap with any future keys. + validatorMissedBlockBitMapKeyPrefix = []byte{0x10} ) func ValidatorSigningInfoKey(v sdk.ConsAddress) []byte { @@ -27,18 +32,18 @@ func ValidatorSigningInfoAddress(key []byte) (v sdk.ConsAddress) { return sdk.ConsAddress(addr) } -func validatorMissedBlockBitArrayPrefixKey(v sdk.ConsAddress) []byte { - return append(validatorMissedBlockBitArrayKeyPrefix, address.MustLengthPrefix(v.Bytes())...) +func DeprecatedValidatorMissedBlockBitArrayPrefixKey(v sdk.ConsAddress) []byte { + return append(deprecatedValidatorMissedBlockBitArrayKeyPrefix, address.MustLengthPrefix(v.Bytes())...) } -func ValidatorMissedBlockBitArrayKey(v sdk.ConsAddress, i int64) []byte { +func DeprecatedValidatorMissedBlockBitArrayKey(v sdk.ConsAddress, i int64) []byte { b := make([]byte, 8) binary.LittleEndian.PutUint64(b, uint64(i)) - return append(validatorMissedBlockBitArrayPrefixKey(v), b...) + return append(DeprecatedValidatorMissedBlockBitArrayPrefixKey(v), b...) } func validatorMissedBlockBitmapPrefixKey(v sdk.ConsAddress) []byte { - return append(validatorMissedBlockBitArrayKeyPrefix, address.MustLengthPrefix(v.Bytes())...) + return append(validatorMissedBlockBitMapKeyPrefix, address.MustLengthPrefix(v.Bytes())...) } func ValidatorMissedBlockBitmapKey(v sdk.ConsAddress, chunkIndex int64) []byte { diff --git a/x/slashing/migrations/v4/migrate.go b/x/slashing/migrations/v4/migrate.go index 75254ecaebc7..f9377799fd19 100644 --- a/x/slashing/migrations/v4/migrate.go +++ b/x/slashing/migrations/v4/migrate.go @@ -21,11 +21,13 @@ func Migrate(ctx sdk.Context, cdc codec.BinaryCodec, store storetypes.KVStore, p var missedBlocks []types.ValidatorMissedBlocks iterateValidatorSigningInfos(ctx, cdc, store, func(addr sdk.ConsAddress, info types.ValidatorSigningInfo) (stop bool) { bechAddr := addr.String() - localMissedBlocks := GetValidatorMissedBlocks(ctx, cdc, store, addr, params) + + // We opt to reset all validators missed blocks to improve upgrade performance + // localMissedBlocks := GetValidatorMissedBlocks(ctx, cdc, store, addr, params) missedBlocks = append(missedBlocks, types.ValidatorMissedBlocks{ Address: bechAddr, - MissedBlocks: localMissedBlocks, + MissedBlocks: []types.MissedBlock{}, }) return false @@ -39,7 +41,10 @@ func Migrate(ctx sdk.Context, cdc codec.BinaryCodec, store storetypes.KVStore, p return err } - deleteValidatorMissedBlockBitArray(ctx, store, addr) + // We skip the deletion here in favor of spreading out across multiple block for performance reasons + // We set the isPruning key to true to indicate that we are in the process of pruning + store.Set(types.IsPruningKey, []byte{1}) + // deleteDeprecatedValidatorMissedBlockBitArray(ctx, store, addr) for _, b := range mb.MissedBlocks { // Note: It is not necessary to store entries with missed=false, i.e. where @@ -86,7 +91,7 @@ func iterateValidatorMissedBlockBitArray( ) { for i := int64(0); i < params.SignedBlocksWindow; i++ { var missed gogotypes.BoolValue - bz := store.Get(ValidatorMissedBlockBitArrayKey(addr, i)) + bz := store.Get(DeprecatedValidatorMissedBlockBitArrayKey(addr, i)) if bz == nil { continue } @@ -114,14 +119,15 @@ func GetValidatorMissedBlocks( return missedBlocks } -func deleteValidatorMissedBlockBitArray(ctx sdk.Context, store storetypes.KVStore, addr sdk.ConsAddress) { - iter := storetypes.KVStorePrefixIterator(store, validatorMissedBlockBitArrayPrefixKey(addr)) - defer iter.Close() +// No longer use this +// func deleteDeprecatedValidatorMissedBlockBitArray(ctx sdk.Context, store storetypes.KVStore, addr sdk.ConsAddress) { +// iter := storetypes.KVStorePrefixIterator(store, DeprecatedValidatorMissedBlockBitArrayPrefixKey(addr)) +// defer iter.Close() - for ; iter.Valid(); iter.Next() { - store.Delete(iter.Key()) - } -} +// for ; iter.Valid(); iter.Next() { +// store.Delete(iter.Key()) +// } +// } func setMissedBlockBitmapValue(ctx sdk.Context, store storetypes.KVStore, addr sdk.ConsAddress, index int64, missed bool) error { // get the chunk or "word" in the logical bitmap diff --git a/x/slashing/migrations/v4/migrate_test.go b/x/slashing/migrations/v4/migrate_test.go index 1fe82241fa0b..b0c2f6338faf 100644 --- a/x/slashing/migrations/v4/migrate_test.go +++ b/x/slashing/migrations/v4/migrate_test.go @@ -3,7 +3,6 @@ package v4_test import ( "testing" - "github.com/bits-and-blooms/bitset" gogotypes "github.com/cosmos/gogoproto/types" "github.com/stretchr/testify/require" @@ -35,7 +34,7 @@ func TestMigrate(t *testing.T) { // all even blocks are missed missed := &gogotypes.BoolValue{Value: i%2 == 0} bz := cdc.MustMarshal(missed) - store.Set(v4.ValidatorMissedBlockBitArrayKey(consAddr, i), bz) + store.Set(v4.DeprecatedValidatorMissedBlockBitArrayKey(consAddr, i), bz) } err := v4.Migrate(ctx, cdc, store, params) @@ -44,15 +43,18 @@ func TestMigrate(t *testing.T) { for i := int64(0); i < params.SignedBlocksWindow; i++ { chunkIndex := i / v4.MissedBlockBitmapChunkSize chunk := store.Get(v4.ValidatorMissedBlockBitmapKey(consAddr, chunkIndex)) - require.NotNil(t, chunk) - bs := bitset.New(uint(v4.MissedBlockBitmapChunkSize)) - require.NoError(t, bs.UnmarshalBinary(chunk)) + // We reset all validators missed blocks to improve upgrade performance, + // so we expect all chunks to be empty + require.Nil(t, chunk) - // ensure all even blocks are missed - bitIndex := uint(i % v4.MissedBlockBitmapChunkSize) - require.Equal(t, i%2 == 0, bs.Test(bitIndex)) - require.Equal(t, i%2 == 1, !bs.Test(bitIndex)) + // bs := bitset.New(uint(v4.MissedBlockBitmapChunkSize)) + // require.NoError(t, bs.UnmarshalBinary(chunk)) + + // // ensure all even blocks are missed + // bitIndex := uint(i % v4.MissedBlockBitmapChunkSize) + // require.Equal(t, i%2 == 0, bs.Test(bitIndex)) + // require.Equal(t, i%2 == 1, !bs.Test(bitIndex)) } // ensure there's only one chunk for a window of size 100 diff --git a/x/slashing/types/keys.go b/x/slashing/types/keys.go index 48b236fac99c..3829b5989715 100644 --- a/x/slashing/types/keys.go +++ b/x/slashing/types/keys.go @@ -48,10 +48,14 @@ const ( // - 0x03: cryptotypes.PubKey var ( - ParamsKey = []byte{0x00} // Prefix for params key - ValidatorSigningInfoKeyPrefix = []byte{0x01} // Prefix for signing info - ValidatorMissedBlockBitmapKeyPrefix = []byte{0x02} // Prefix for missed block bitmap - AddrPubkeyRelationKeyPrefix = []byte{0x03} // Prefix for address-pubkey relation + ParamsKey = []byte{0x00} // Prefix for params key + ValidatorSigningInfoKeyPrefix = []byte{0x01} // Prefix for signing info + AddrPubkeyRelationKeyPrefix = []byte{0x03} // Prefix for address-pubkey relation + + ValidatorMissedBlockBitmapKeyPrefix = []byte{0x10} // Prefix for missed block bitmap + + IsPruningKey = []byte{0x09} + TrueByteValue = []byte{0x01} ) // ValidatorSigningInfoKey - stored by *Consensus* address (not operator address)