refactor(x/staking): Migrate ValidatorQueue to use Collections (#17562)
Co-authored-by: testinginprod <98415576+testinginprod@users.noreply.github.com> Co-authored-by: unknown unknown <unknown@unknown>
This commit is contained in:
parent
81d9ce9af5
commit
e53830d58a
@ -62,6 +62,9 @@ Ref: https://keepachangelog.com/en/1.0.0/
|
||||
|
||||
### API Breaking Changes
|
||||
|
||||
* (x/staking) [#17562](https://github.com/cosmos/cosmos-sdk/pull/17562) Use collections for `ValidatorQueue`
|
||||
* remove from `types`: `GetValidatorQueueKey`, `ParseValidatorQueueKey`
|
||||
* remove from `Keeper`: `ValidatorQueueIterator`
|
||||
* (x/staking) [#17498](https://github.com/cosmos/cosmos-sdk/pull/17498) Use collections for `LastValidatorPower`:
|
||||
* remove from `types`: `GetLastValidatorPowerKey`
|
||||
* remove from `Keeper`: `LastValidatorsIterator`, `IterateLastValidators`
|
||||
|
||||
@ -68,6 +68,8 @@ type Keeper struct {
|
||||
RedelegationsByValSrc collections.Map[collections.Triple[[]byte, []byte, []byte], []byte]
|
||||
// UnbondingDelegationByValIndex key: valAddr+delAddr | value: none used (index key for UnbondingDelegations stored by validator index)
|
||||
UnbondingDelegationByValIndex collections.Map[collections.Pair[[]byte, []byte], []byte]
|
||||
// ValidatorQueue key: len(timestamp bytes)+timestamp+height | value: ValAddresses
|
||||
ValidatorQueue collections.Map[collections.Triple[uint64, time.Time, uint64], types.ValAddresses]
|
||||
// LastValidatorPower key: valAddr | value: power(gogotypes.Int64Value())
|
||||
LastValidatorPower collections.Map[[]byte, gogotypes.Int64Value]
|
||||
}
|
||||
@ -186,7 +188,20 @@ func NewKeeper(
|
||||
collections.BytesKey,
|
||||
sdk.LengthPrefixedBytesKey, // sdk.LengthPrefixedBytesKey is needed to retain state compatibility
|
||||
),
|
||||
codec.CollValue[types.UnbondingDelegation](cdc)),
|
||||
codec.CollValue[types.UnbondingDelegation](cdc),
|
||||
),
|
||||
// key format is: 67 | length(timestamp Bytes) | timestamp | height
|
||||
// Note: We use 3 keys here because we prefixed time bytes with its length previously and to retain state compatibility we remain to use the same
|
||||
ValidatorQueue: collections.NewMap(
|
||||
sb, types.ValidatorQueueKey,
|
||||
"validator_queue",
|
||||
collections.TripleKeyCodec(
|
||||
collections.Uint64Key,
|
||||
sdk.TimeKey,
|
||||
collections.Uint64Key,
|
||||
),
|
||||
codec.CollValue[types.ValAddresses](cdc),
|
||||
),
|
||||
}
|
||||
|
||||
schema, err := sb.Build()
|
||||
|
||||
@ -202,6 +202,33 @@ func getLastValidatorPowerKey(operator sdk.ValAddress) []byte {
|
||||
return append(lastValidatorPowerKey, addresstypes.MustLengthPrefix(operator)...)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
validatorQueueKey := []byte{0x43}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func (s *KeeperTestSuite) TestLastTotalPowerMigrationToColls() {
|
||||
s.SetupTest()
|
||||
|
||||
@ -445,6 +472,44 @@ func (s *KeeperTestSuite) TestValidatorsMigrationToColls() {
|
||||
s.Require().NoError(err)
|
||||
}
|
||||
|
||||
func (s *KeeperTestSuite) TestValidatorQueueMigrationToColls() {
|
||||
s.SetupTest()
|
||||
_, valAddrs := createValAddrs(100)
|
||||
endTime := time.Unix(0, 0).UTC()
|
||||
endHeight := int64(10)
|
||||
err := testutil.DiffCollectionsMigration(
|
||||
s.ctx,
|
||||
s.key,
|
||||
100,
|
||||
func(i int64) {
|
||||
var addrs []string
|
||||
addrs = append(addrs, valAddrs[i].String())
|
||||
bz, err := s.cdc.Marshal(&stakingtypes.ValAddresses{Addresses: addrs})
|
||||
s.Require().NoError(err)
|
||||
|
||||
// legacy Set method
|
||||
s.ctx.KVStore(s.key).Set(getValidatorQueueKey(endTime, endHeight), bz)
|
||||
},
|
||||
"8cf5fb4def683e83ea4cc4f14d8b2abc4c6af66709ad8af391dc749e63ef7524",
|
||||
)
|
||||
s.Require().NoError(err)
|
||||
|
||||
err = testutil.DiffCollectionsMigration(
|
||||
s.ctx,
|
||||
s.key,
|
||||
100,
|
||||
func(i int64) {
|
||||
var addrs []string
|
||||
addrs = append(addrs, valAddrs[i].String())
|
||||
|
||||
err := s.stakingKeeper.SetUnbondingValidatorsQueue(s.ctx, endTime, endHeight, addrs)
|
||||
s.Require().NoError(err)
|
||||
},
|
||||
"8cf5fb4def683e83ea4cc4f14d8b2abc4c6af66709ad8af391dc749e63ef7524",
|
||||
)
|
||||
s.Require().NoError(err)
|
||||
}
|
||||
|
||||
func TestKeeperTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(KeeperTestSuite))
|
||||
}
|
||||
|
||||
@ -20,6 +20,8 @@ import (
|
||||
"github.com/cosmos/cosmos-sdk/x/staking/types"
|
||||
)
|
||||
|
||||
var timeBzKeySize = uint64(29) // time bytes key size is 29 by default
|
||||
|
||||
// GetValidator gets a single validator
|
||||
func (k Keeper) GetValidator(ctx context.Context, addr sdk.ValAddress) (validator types.Validator, err error) {
|
||||
validator, err = k.Validators.Get(ctx, addr)
|
||||
@ -403,34 +405,20 @@ func (k Keeper) GetLastValidators(ctx context.Context) (validators []types.Valid
|
||||
// GetUnbondingValidators returns a slice of mature validator addresses that
|
||||
// complete their unbonding at a given time and height.
|
||||
func (k Keeper) GetUnbondingValidators(ctx context.Context, endTime time.Time, endHeight int64) ([]string, error) {
|
||||
store := k.storeService.OpenKVStore(ctx)
|
||||
|
||||
bz, err := store.Get(types.GetValidatorQueueKey(endTime, endHeight))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
timeSize := sdk.TimeKey.Size(endTime)
|
||||
valAddrs, err := k.ValidatorQueue.Get(ctx, collections.Join3(uint64(timeSize), endTime, uint64(endHeight)))
|
||||
if err != nil && !errors.Is(err, collections.ErrNotFound) {
|
||||
return []string{}, err
|
||||
}
|
||||
|
||||
if bz == nil {
|
||||
return []string{}, nil
|
||||
}
|
||||
|
||||
addrs := types.ValAddresses{}
|
||||
if err = k.cdc.Unmarshal(bz, &addrs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return addrs.Addresses, nil
|
||||
return valAddrs.Addresses, nil
|
||||
}
|
||||
|
||||
// SetUnbondingValidatorsQueue sets a given slice of validator addresses into
|
||||
// the unbonding validator queue by a given height and time.
|
||||
func (k Keeper) SetUnbondingValidatorsQueue(ctx context.Context, endTime time.Time, endHeight int64, addrs []string) error {
|
||||
store := k.storeService.OpenKVStore(ctx)
|
||||
bz, err := k.cdc.Marshal(&types.ValAddresses{Addresses: addrs})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return store.Set(types.GetValidatorQueueKey(endTime, endHeight), bz)
|
||||
valAddrs := types.ValAddresses{Addresses: addrs}
|
||||
return k.ValidatorQueue.Set(ctx, collections.Join3(timeBzKeySize, endTime, uint64(endHeight)), valAddrs)
|
||||
}
|
||||
|
||||
// InsertUnbondingValidatorQueue inserts a given unbonding validator address into
|
||||
@ -447,8 +435,7 @@ func (k Keeper) InsertUnbondingValidatorQueue(ctx context.Context, val types.Val
|
||||
// DeleteValidatorQueueTimeSlice deletes all entries in the queue indexed by a
|
||||
// given height and time.
|
||||
func (k Keeper) DeleteValidatorQueueTimeSlice(ctx context.Context, endTime time.Time, endHeight int64) error {
|
||||
store := k.storeService.OpenKVStore(ctx)
|
||||
return store.Delete(types.GetValidatorQueueKey(endTime, endHeight))
|
||||
return k.ValidatorQueue.Remove(ctx, collections.Join3(timeBzKeySize, endTime, uint64(endHeight)))
|
||||
}
|
||||
|
||||
// DeleteValidatorQueue removes a validator by address from the unbonding queue
|
||||
@ -485,92 +472,85 @@ func (k Keeper) DeleteValidatorQueue(ctx context.Context, val types.Validator) e
|
||||
return k.SetUnbondingValidatorsQueue(ctx, val.UnbondingTime, val.UnbondingHeight, newAddrs)
|
||||
}
|
||||
|
||||
// ValidatorQueueIterator returns an interator ranging over validators that are
|
||||
// unbonding whose unbonding completion occurs at the given height and time.
|
||||
func (k Keeper) ValidatorQueueIterator(ctx context.Context, endTime time.Time, endHeight int64) (corestore.Iterator, error) {
|
||||
store := k.storeService.OpenKVStore(ctx)
|
||||
return store.Iterator(types.ValidatorQueueKey, storetypes.InclusiveEndBytes(types.GetValidatorQueueKey(endTime, endHeight)))
|
||||
}
|
||||
|
||||
// UnbondAllMatureValidators unbonds all the mature unbonding validators that
|
||||
// have finished their unbonding period.
|
||||
func (k Keeper) UnbondAllMatureValidators(ctx context.Context) error {
|
||||
sdkCtx := sdk.UnwrapSDKContext(ctx)
|
||||
blockTime := sdkCtx.BlockTime()
|
||||
blockHeight := sdkCtx.BlockHeight()
|
||||
blockHeight := uint64(sdkCtx.BlockHeight())
|
||||
|
||||
// unbondingValIterator will contains all validator addresses indexed under
|
||||
// the ValidatorQueueKey prefix. Note, the entire index key is composed as
|
||||
// ValidatorQueueKey | timeBzLen (8-byte big endian) | timeBz | heightBz (8-byte big endian),
|
||||
// so it may be possible that certain validator addresses that are iterated
|
||||
// over are not ready to unbond, so an explicit check is required.
|
||||
unbondingValIterator, err := k.ValidatorQueueIterator(ctx, blockTime, blockHeight)
|
||||
if err != nil {
|
||||
return err
|
||||
rng := new(collections.Range[collections.Triple[uint64, time.Time, uint64]]).
|
||||
EndInclusive(collections.Join3(uint64(29), blockTime, blockHeight))
|
||||
|
||||
return k.ValidatorQueue.Walk(ctx, rng, func(key collections.Triple[uint64, time.Time, uint64], value types.ValAddresses) (stop bool, err error) {
|
||||
return false, k.unbondMatureValidators(ctx, blockHeight, blockTime, key, value)
|
||||
})
|
||||
}
|
||||
|
||||
func (k Keeper) unbondMatureValidators(
|
||||
ctx context.Context,
|
||||
blockHeight uint64,
|
||||
blockTime time.Time,
|
||||
key collections.Triple[uint64, time.Time, uint64],
|
||||
addrs types.ValAddresses,
|
||||
) error {
|
||||
keyTime, keyHeight := key.K2(), key.K3()
|
||||
|
||||
// All addresses for the given key have the same unbonding height and time.
|
||||
// We only unbond if the height and time are less than the current height
|
||||
// and time.
|
||||
if keyHeight > blockHeight || keyTime.After(blockTime) {
|
||||
return nil
|
||||
}
|
||||
defer unbondingValIterator.Close()
|
||||
|
||||
for ; unbondingValIterator.Valid(); unbondingValIterator.Next() {
|
||||
key := unbondingValIterator.Key()
|
||||
keyTime, keyHeight, err := types.ParseValidatorQueueKey(key)
|
||||
// finalize unbonding
|
||||
for _, valAddr := range addrs.Addresses {
|
||||
addr, err := k.validatorAddressCodec.StringToBytes(valAddr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse unbonding key: %w", err)
|
||||
return err
|
||||
}
|
||||
val, err := k.GetValidator(ctx, addr)
|
||||
if err != nil {
|
||||
return errorsmod.Wrap(err, "validator in the unbonding queue was not found")
|
||||
}
|
||||
|
||||
// All addresses for the given key have the same unbonding height and time.
|
||||
// We only unbond if the height and time are less than the current height
|
||||
// and time.
|
||||
if keyHeight <= blockHeight && (keyTime.Before(blockTime) || keyTime.Equal(blockTime)) {
|
||||
addrs := types.ValAddresses{}
|
||||
if err = k.cdc.Unmarshal(unbondingValIterator.Value(), &addrs); err != nil {
|
||||
if !val.IsUnbonding() {
|
||||
return fmt.Errorf("unexpected validator in unbonding queue; status was not unbonding")
|
||||
}
|
||||
|
||||
// if the ref count is not zero, early exit.
|
||||
if val.UnbondingOnHoldRefCount != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// otherwise do proper unbonding
|
||||
for _, id := range val.UnbondingIds {
|
||||
if err = k.DeleteUnbondingIndex(ctx, id); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, valAddr := range addrs.Addresses {
|
||||
addr, err := k.validatorAddressCodec.StringToBytes(valAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
val, err := k.GetValidator(ctx, addr)
|
||||
if err != nil {
|
||||
return errorsmod.Wrap(err, "validator in the unbonding queue was not found")
|
||||
}
|
||||
val, err = k.UnbondingToUnbonded(ctx, val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !val.IsUnbonding() {
|
||||
return fmt.Errorf("unexpected validator in unbonding queue; status was not unbonding")
|
||||
}
|
||||
|
||||
if val.UnbondingOnHoldRefCount == 0 {
|
||||
for _, id := range val.UnbondingIds {
|
||||
if err = k.DeleteUnbondingIndex(ctx, id); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
val, err = k.UnbondingToUnbonded(ctx, val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if val.GetDelegatorShares().IsZero() {
|
||||
str, err := k.validatorAddressCodec.StringToBytes(val.GetOperator())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = k.RemoveValidator(ctx, str); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// remove unbonding ids
|
||||
val.UnbondingIds = []uint64{}
|
||||
}
|
||||
|
||||
// remove validator from queue
|
||||
if err = k.DeleteValidatorQueue(ctx, val); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if val.GetDelegatorShares().IsZero() {
|
||||
str, err := k.validatorAddressCodec.StringToBytes(val.GetOperator())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = k.RemoveValidator(ctx, str); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// remove unbonding ids
|
||||
val.UnbondingIds = []uint64{}
|
||||
}
|
||||
|
||||
// remove validator from queue
|
||||
if err = k.DeleteValidatorQueue(ctx, val); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
@ -111,7 +111,7 @@ func TestStoreMigration(t *testing.T) {
|
||||
{
|
||||
"ValidatorQueueKey",
|
||||
v1.GetValidatorQueueKey(now, 4),
|
||||
types.GetValidatorQueueKey(now, 4),
|
||||
getValidatorQueueKey(now, 4),
|
||||
},
|
||||
{
|
||||
"HistoricalInfoKey",
|
||||
@ -161,3 +161,26 @@ func getValidatorKey(operatorAddr sdk.ValAddress) []byte {
|
||||
func unbondingKey(delAddr sdk.AccAddress, valAddr sdk.ValAddress) []byte {
|
||||
return append(append(types.UnbondingDelegationKey, sdkaddress.MustLengthPrefix(delAddr)...), sdkaddress.MustLengthPrefix(valAddr)...)
|
||||
}
|
||||
|
||||
func getValidatorQueueKey(timestamp time.Time, height int64) []byte {
|
||||
heightBz := sdk.Uint64ToBigEndian(uint64(height))
|
||||
timeBz := sdk.FormatTimeBytes(timestamp)
|
||||
timeBzL := len(timeBz)
|
||||
prefixL := len(types.ValidatorQueueKey)
|
||||
|
||||
bz := make([]byte, prefixL+8+timeBzL+8)
|
||||
|
||||
// copy the prefix
|
||||
copy(bz[:prefixL], types.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
|
||||
}
|
||||
|
||||
@ -1,9 +1,7 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"cosmossdk.io/collections"
|
||||
@ -53,7 +51,7 @@ var (
|
||||
|
||||
UnbondingQueueKey = collections.NewPrefix(65) // 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
|
||||
ValidatorQueueKey = collections.NewPrefix(67) // prefix for the timestamps in validator queue
|
||||
|
||||
HistoricalInfoKey = collections.NewPrefix(80) // prefix for the historical info
|
||||
ValidatorUpdatesKey = collections.NewPrefix(97) // prefix for the end block validator updates key
|
||||
@ -142,50 +140,6 @@ func ParseValidatorPowerRankKey(key []byte) (operAddr []byte) {
|
||||
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
|
||||
}
|
||||
|
||||
// GetUBDKey creates the key for an unbonding delegation by delegator and validator addr
|
||||
// VALUE: staking/UnbondingDelegation
|
||||
func GetUBDKey(delAddr sdk.AccAddress, valAddr sdk.ValAddress) []byte {
|
||||
|
||||
@ -1,11 +1,9 @@
|
||||
package types_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"math/big"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
@ -47,29 +45,3 @@ func TestGetValidatorPowerRank(t *testing.T) {
|
||||
require.Equal(t, tt.wantHex, got, "Keys did not match on test case %d", i)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetValidatorQueueKey(t *testing.T) {
|
||||
ts := time.Now()
|
||||
height := int64(1024)
|
||||
|
||||
bz := types.GetValidatorQueueKey(ts, height)
|
||||
rTs, rHeight, err := types.ParseValidatorQueueKey(bz)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, ts.UTC(), rTs.UTC())
|
||||
require.Equal(t, rHeight, height)
|
||||
}
|
||||
|
||||
func TestTestGetValidatorQueueKeyOrder(t *testing.T) {
|
||||
ts := time.Now().UTC()
|
||||
height := int64(1000)
|
||||
|
||||
endKey := types.GetValidatorQueueKey(ts, height)
|
||||
|
||||
keyA := types.GetValidatorQueueKey(ts.Add(-10*time.Minute), height-10)
|
||||
keyB := types.GetValidatorQueueKey(ts.Add(-5*time.Minute), height+50)
|
||||
keyC := types.GetValidatorQueueKey(ts.Add(10*time.Minute), height+100)
|
||||
|
||||
require.Equal(t, -1, bytes.Compare(keyA, endKey)) // keyA <= endKey
|
||||
require.Equal(t, -1, bytes.Compare(keyB, endKey)) // keyB <= endKey
|
||||
require.Equal(t, 1, bytes.Compare(keyC, endKey)) // keyB >= endKey
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user