From 66e209cbbb33ff85cd28b764aa55af07c9d22714 Mon Sep 17 00:00:00 2001 From: Marius Poke Date: Tue, 17 Sep 2024 12:48:00 +0200 Subject: [PATCH] refactor: change byte prefixing scheme for consumer (#2148) * update consumer keys prefixing scheme * make changes consistent * mark PendingDataPackets key as v1 * apply review suggestions * fix import --- x/ccv/consumer/keeper/keeper.go | 32 +- x/ccv/consumer/migrations/v2/migration.go | 10 +- .../consumer/migrations/v2/migration_test.go | 2 +- x/ccv/consumer/types/keys.go | 325 ++++++++++++------ x/ccv/consumer/types/keys_test.go | 140 +++++--- x/ccv/provider/types/keys.go | 4 +- 6 files changed, 337 insertions(+), 176 deletions(-) diff --git a/x/ccv/consumer/keeper/keeper.go b/x/ccv/consumer/keeper/keeper.go index 67146f7b8c..c07a3e9428 100644 --- a/x/ccv/consumer/keeper/keeper.go +++ b/x/ccv/consumer/keeper/keeper.go @@ -244,13 +244,13 @@ func (k Keeper) GetProviderClientID(ctx sdk.Context) (string, bool) { // SetProviderChannel sets the channelID for the channel to the provider. func (k Keeper) SetProviderChannel(ctx sdk.Context, channelID string) { store := ctx.KVStore(k.storeKey) - store.Set(types.ProviderChannelKey(), []byte(channelID)) + store.Set(types.ProviderChannelIDKey(), []byte(channelID)) } // GetProviderChannel gets the channelID for the channel to the provider. func (k Keeper) GetProviderChannel(ctx sdk.Context) (string, bool) { store := ctx.KVStore(k.storeKey) - channelIdBytes := store.Get(types.ProviderChannelKey()) + channelIdBytes := store.Get(types.ProviderChannelIDKey()) if len(channelIdBytes) == 0 { return "", false } @@ -260,7 +260,7 @@ func (k Keeper) GetProviderChannel(ctx sdk.Context) (string, bool) { // DeleteProviderChannel deletes the channelID for the channel to the provider. func (k Keeper) DeleteProviderChannel(ctx sdk.Context) { store := ctx.KVStore(k.storeKey) - store.Delete(types.ProviderChannelKey()) + store.Delete(types.ProviderChannelIDKey()) } // SetPendingChanges sets the pending validator set change packet that haven't been flushed to ABCI @@ -360,7 +360,7 @@ func (k Keeper) GetLastStandaloneValidators(ctx sdk.Context) ([]stakingtypes.Val // i.e., the slice contains the IDs of the matured VSCPackets. func (k Keeper) GetElapsedPacketMaturityTimes(ctx sdk.Context) (maturingVSCPackets []types.MaturingVSCPacket) { store := ctx.KVStore(k.storeKey) - iterator := storetypes.KVStorePrefixIterator(store, []byte{types.PacketMaturityTimeBytePrefix}) + iterator := storetypes.KVStorePrefixIterator(store, types.PacketMaturityTimeKeyPrefix()) defer iterator.Close() @@ -392,7 +392,7 @@ func (k Keeper) GetElapsedPacketMaturityTimes(ctx sdk.Context) (maturingVSCPacke // If two entries have the same maturityTime, then they are ordered by vscID. func (k Keeper) GetAllPacketMaturityTimes(ctx sdk.Context) (maturingVSCPackets []types.MaturingVSCPacket) { store := ctx.KVStore(k.storeKey) - iterator := storetypes.KVStorePrefixIterator(store, []byte{types.PacketMaturityTimeBytePrefix}) + iterator := storetypes.KVStorePrefixIterator(store, types.PacketMaturityTimeKeyPrefix()) defer iterator.Close() for ; iterator.Valid(); iterator.Next() { @@ -493,7 +493,7 @@ func (k Keeper) DeleteHeightValsetUpdateID(ctx sdk.Context, height uint64) { // Thus, the returned array is in ascending order of heights. func (k Keeper) GetAllHeightToValsetUpdateIDs(ctx sdk.Context) (heightToValsetUpdateIDs []types.HeightToValsetUpdateID) { store := ctx.KVStore(k.storeKey) - iterator := storetypes.KVStorePrefixIterator(store, []byte{types.HeightValsetUpdateIDBytePrefix}) + iterator := storetypes.KVStorePrefixIterator(store, types.HeightValsetUpdateIDKeyPrefix()) defer iterator.Close() for ; iterator.Valid(); iterator.Next() { @@ -535,7 +535,7 @@ func (k Keeper) DeleteOutstandingDowntime(ctx sdk.Context, address sdk.ConsAddre // Thus, the returned array is in ascending order of consAddresses. func (k Keeper) GetAllOutstandingDowntimes(ctx sdk.Context) (downtimes []types.OutstandingDowntime) { store := ctx.KVStore(k.storeKey) - iterator := storetypes.KVStorePrefixIterator(store, []byte{types.OutstandingDowntimeBytePrefix}) + iterator := storetypes.KVStorePrefixIterator(store, types.OutstandingDowntimeKeyPrefix()) defer iterator.Close() for ; iterator.Valid(); iterator.Next() { @@ -584,7 +584,7 @@ func (k Keeper) DeleteCCValidator(ctx sdk.Context, addr []byte) { // Thus, the returned array is in ascending order of addresses. func (k Keeper) GetAllCCValidator(ctx sdk.Context) (validators []types.CrossChainValidator) { store := ctx.KVStore(k.storeKey) - iterator := storetypes.KVStorePrefixIterator(store, []byte{types.CrossChainValidatorBytePrefix}) + iterator := storetypes.KVStorePrefixIterator(store, types.CrossChainValidatorKeyPrefix()) defer iterator.Close() for ; iterator.Valid(); iterator.Next() { @@ -612,7 +612,7 @@ func (k Keeper) getAndIncrementPendingPacketsIdx(ctx sdk.Context) (toReturn uint // DeleteHeadOfPendingPackets deletes the head of the pending packets queue. func (k Keeper) DeleteHeadOfPendingPackets(ctx sdk.Context) { store := ctx.KVStore(k.storeKey) - iterator := storetypes.KVStorePrefixIterator(store, []byte{types.PendingDataPacketsBytePrefix}) + iterator := storetypes.KVStorePrefixIterator(store, types.PendingDataPacketsV1KeyPrefix()) defer iterator.Close() if !iterator.Valid() { return @@ -644,9 +644,7 @@ type ConsumerPacketDataWithIdx struct { func (k Keeper) GetAllPendingPacketsWithIdx(ctx sdk.Context) []ConsumerPacketDataWithIdx { packets := []ConsumerPacketDataWithIdx{} store := ctx.KVStore(k.storeKey) - // Note: PendingDataPacketsBytePrefix is the correct prefix, NOT PendingDataPacketsByteKey. - // See consistency with PendingDataPacketsKey(). - iterator := storetypes.KVStorePrefixIterator(store, []byte{types.PendingDataPacketsBytePrefix}) + iterator := storetypes.KVStorePrefixIterator(store, types.PendingDataPacketsV1KeyPrefix()) defer iterator.Close() for ; iterator.Valid(); iterator.Next() { var packet ccv.ConsumerPacketData @@ -658,7 +656,7 @@ func (k Keeper) GetAllPendingPacketsWithIdx(ctx sdk.Context) []ConsumerPacketDat } packetWithIdx := ConsumerPacketDataWithIdx{ ConsumerPacketData: packet, - // index stored in key after prefix, see PendingDataPacketsKey() + // index stored in key after prefix, see PendingDataPacketsV1Key() Idx: sdk.BigEndianToUint64(iterator.Key()[1:]), } packets = append(packets, packetWithIdx) @@ -670,15 +668,13 @@ func (k Keeper) GetAllPendingPacketsWithIdx(ctx sdk.Context) []ConsumerPacketDat func (k Keeper) DeletePendingDataPackets(ctx sdk.Context, idxs ...uint64) { store := ctx.KVStore(k.storeKey) for _, idx := range idxs { - store.Delete(types.PendingDataPacketsKey(idx)) + store.Delete(types.PendingDataPacketsV1Key(idx)) } } func (k Keeper) DeleteAllPendingDataPackets(ctx sdk.Context) { store := ctx.KVStore(k.storeKey) - // Note: PendingDataPacketsBytePrefix is the correct prefix, NOT PendingDataPacketsByteKey. - // See consistency with PendingDataPacketsKey(). - iterator := storetypes.KVStorePrefixIterator(store, []byte{types.PendingDataPacketsBytePrefix}) + iterator := storetypes.KVStorePrefixIterator(store, types.PendingDataPacketsV1KeyPrefix()) keysToDel := [][]byte{} defer iterator.Close() for ; iterator.Valid(); iterator.Next() { @@ -692,7 +688,7 @@ func (k Keeper) DeleteAllPendingDataPackets(ctx sdk.Context) { // AppendPendingPacket enqueues the given data packet to the end of the pending data packets queue func (k Keeper) AppendPendingPacket(ctx sdk.Context, packetType ccv.ConsumerPacketDataType, data ccv.ExportedIsConsumerPacketData_Data) { idx := k.getAndIncrementPendingPacketsIdx(ctx) // for FIFO queue - key := types.PendingDataPacketsKey(idx) + key := types.PendingDataPacketsV1Key(idx) store := ctx.KVStore(k.storeKey) cpd := ccv.NewConsumerPacketData(packetType, data) bz, err := cpd.Marshal() diff --git a/x/ccv/consumer/migrations/v2/migration.go b/x/ccv/consumer/migrations/v2/migration.go index 353da75ef5..99253cf2d8 100644 --- a/x/ccv/consumer/migrations/v2/migration.go +++ b/x/ccv/consumer/migrations/v2/migration.go @@ -18,7 +18,7 @@ import ( func MigrateConsumerPacketData(ctx sdk.Context, store storetypes.KVStore) error { // retrieve old data an deserialize var oldData consumertypes.ConsumerPacketDataList - bz := store.Get([]byte{consumertypes.PendingDataPacketsBytePrefix}) + bz := store.Get(consumertypes.PendingDataPacketsV1KeyPrefix()) if bz == nil { ctx.Logger().Info("no pending data packets to migrate") return nil @@ -32,8 +32,8 @@ func MigrateConsumerPacketData(ctx sdk.Context, store storetypes.KVStore) error // index incrementation happens in getAndIncrementPendingPacketsIdx // the loop operations are equivalent to consumerkeeper.AppendPendingPacket() for _, data := range oldData.List { - idx := getAndIncrementPendingPacketsIdx(ctx, store) - key := consumertypes.PendingDataPacketsKey(idx) + idx := getAndIncrementPendingPacketsIdx(store) + key := consumertypes.PendingDataPacketsV1Key(idx) cpd := ccvtypes.NewConsumerPacketData(data.Type, data.Data) bz, err := cpd.Marshal() if err != nil { @@ -43,12 +43,12 @@ func MigrateConsumerPacketData(ctx sdk.Context, store storetypes.KVStore) error store.Set(key, bz) } - store.Delete([]byte{consumertypes.PendingDataPacketsBytePrefix}) + store.Delete(consumertypes.PendingDataPacketsV1KeyPrefix()) return nil } // getAndIncrementPendingPacketsIdx returns the current pending packets index and increments it. -func getAndIncrementPendingPacketsIdx(ctx sdk.Context, store storetypes.KVStore) (toReturn uint64) { +func getAndIncrementPendingPacketsIdx(store storetypes.KVStore) (toReturn uint64) { bz := store.Get(consumertypes.PendingPacketsIndexKey()) if bz != nil { toReturn = sdk.BigEndianToUint64(bz) diff --git a/x/ccv/consumer/migrations/v2/migration_test.go b/x/ccv/consumer/migrations/v2/migration_test.go index 500be893b6..15790691e7 100644 --- a/x/ccv/consumer/migrations/v2/migration_test.go +++ b/x/ccv/consumer/migrations/v2/migration_test.go @@ -83,5 +83,5 @@ func setPendingPackets(ctx sdk.Context, store storetypes.KVStore, packets consum if err != nil { panic(fmt.Errorf("failed to marshal ConsumerPacketDataList: %w", err)) } - store.Set([]byte{consumertypes.PendingDataPacketsBytePrefix}, bz) + store.Set(consumertypes.PendingDataPacketsV1KeyPrefix(), bz) } diff --git a/x/ccv/consumer/types/keys.go b/x/ccv/consumer/types/keys.go index 302b45883e..30b8a4f725 100644 --- a/x/ccv/consumer/types/keys.go +++ b/x/ccv/consumer/types/keys.go @@ -2,6 +2,8 @@ package types import ( "encoding/binary" + fmt "fmt" + "sort" time "time" sdk "github.com/cosmos/cosmos-sdk/types" @@ -28,135 +30,241 @@ const ( // ConsumerToSendToProviderName is a "buffer" address for outgoing fees to be transferred to the provider chain //#nosec G101 -- (false positive) this is not a hardcoded credential ConsumerToSendToProviderName = "cons_to_send_to_provider" -) -// Iota generated keys/key prefixes (as a byte), supports 256 possible values -const ( - // PortByteKey defines the byte key to store the port ID in store - PortByteKey byte = iota + // Names for the store keys. + // Used for storing the byte prefixes in the constant map. + // See getKeyPrefixes(). - // LastDistributionTransmissionByteKey defines the byte key to store the last distribution transmission - LastDistributionTransmissionByteKey + PortKeyName = "PortKey" - // UnbondingTimeKeyString is the byte key for storing the unbonding period - UnbondingTimeByteKey + LastDistributionTransmissionKeyName = "LastDistributionTransmissionKey" - // ProviderClientKeyString is the byte key for storing the clientID of the provider client - ProviderClientByteKey + UnbondingTimeKeyName = "UnbondingTimeKey" - // ProviderChannelKeyString is the byte key for storing the channelID of the CCV channel - ProviderChannelByteKey + ProviderClientIDKeyName = "ProviderClientIDKey" - // PendingChangesKeyString is the byte key that will store any pending validator set changes - // received over CCV channel but not yet flushed over ABCI - PendingChangesByteKey + ProviderChannelIDKeyName = "ProviderChannelIDKey" - // NOTE: This prefix is deprecated, but left in place to avoid consumer state migrations - // [DEPRECATED] - PendingDataPacketsByteKey + PendingChangesKeyName = "PendingChangesKey" - // PreCCVByteKey is the byte to store the consumer is running on democracy staking module without consumer - PreCCVByteKey + DeprecatedPendingDataPacketsV0KeyName = "DeprecatedPendingDataPacketsV0Key" - // InitialValSetByteKey is the byte to store the initial validator set for a consumer - InitialValSetByteKey + PreCCVKeyName = "PreCCVKey" - // NOTE: This prefix is deprecated, but left in place to avoid consumer state migrations - // [DEPRECATED] - LastStandaloneHeightByteKey + InitialValSetKeyName = "InitialValSetKey" - // NOTE: This key is deprecated, but left in place to avoid consumer state migrations - // [DEPRECATED] - DeprecatedSmallestNonOptOutPowerByteKey + DeprecatedLastStandaloneHeightKeyName = "DeprecatedLastStandaloneHeightKey" - // HistoricalInfoKey is the byte prefix that will store the historical info for a given height - HistoricalInfoBytePrefix + DeprecatedSmallestNonOptOutPowerKeyName = "DeprecatedSmallestNonOptOutPowerKey" - // PacketMaturityTimePrefix is the byte prefix that will store maturity time for each received VSC packet - PacketMaturityTimeBytePrefix + HistoricalInfoKeyName = "HistoricalInfoKey" - // HeightValsetUpdateIDPrefix is the byte prefix that will store the mapping from block height to valset update ID - HeightValsetUpdateIDBytePrefix + PacketMaturityTimeKeyName = "PacketMaturityTimeKey" - // OutstandingDowntimePrefix is the byte prefix that will store the validators outstanding downtime by consensus address - OutstandingDowntimeBytePrefix + HeightValsetUpdateIDKeyName = "HeightValsetUpdateIDKey" - // PendingDataPacketsBytePrefix is the byte prefix for storing - // a list of data packets that cannot be sent yet to the provider - // chain either because the CCV channel is not established or - // because the client is expired - PendingDataPacketsBytePrefix + OutstandingDowntimeKeyName = "OutstandingDowntimeKey" - // CrossChainValidatorPrefix is the byte prefix that will store cross-chain validators by consensus address - CrossChainValidatorBytePrefix + PendingDataPacketsV1KeyName = "PendingDataPacketsV1Key" - // InitGenesisHeightByteKey is the byte that will store the init genesis height - InitGenesisHeightByteKey + CrossChainValidatorKeyName = "CrossChainValidatorKey" - // StandaloneTransferChannelIDByteKey is the byte storing the channelID of transfer channel - // that existed from a standalone chain changing over to a consumer - StandaloneTransferChannelIDByteKey + InitGenesisHeightKeyName = "InitGenesisHeightKey" - // PrevStandaloneChainByteKey is the byte storing the flag marking whether this chain was previously standalone - PrevStandaloneChainByteKey + StandaloneTransferChannelIDKeyName = "StandaloneTransferChannelIDKey" - // PendingPacketsIndexBytePrefix is the single byte key to the pending packets index. - // This index is used for implementing a FIFO queue of pending packets in the KV store. - PendingPacketsIndexByteKey + PrevStandaloneChainKeyName = "PrevStandaloneChainKey" - // SlashRecordByteKey is the single byte key storing the consumer's slash record. - SlashRecordByteKey + PendingPacketsIndexKeyName = "PendingPacketsIndexKey" - // ParametersKey is the single byte key for storing consumer's parameters. - ParametersByteKey + SlashRecordKeyName = "SlashRecordKey" - // NOTE: DO NOT ADD NEW BYTE PREFIXES HERE WITHOUT ADDING THEM TO getAllKeyPrefixes() IN keys_test.go + ParametersKeyName = "ParametersKey" ) +// getKeyPrefixes returns a constant map of all the byte prefixes for existing keys +func getKeyPrefixes() map[string]byte { + return map[string]byte{ + // PortKey is the key for storing the port ID + PortKeyName: 0, + + // LastDistributionTransmissionKey is the key for storing the last distribution transmission + LastDistributionTransmissionKeyName: 1, + + // UnbondingTimeKey is the key for storing the unbonding period + UnbondingTimeKeyName: 2, + + // ProviderClientIDKey is the key for storing the clientID of the provider client + ProviderClientIDKeyName: 3, + + // ProviderChannelIDKey is the key for storing the channelID of the CCV channel + ProviderChannelIDKeyName: 4, + + // PendingChangesKey is the key for storing any pending validator set changes + // received over CCV channel but not yet flushed over ABCI + PendingChangesKeyName: 5, + + // NOTE: This prefix is deprecated, but left in place to avoid consumer state migrations + // [DEPRECATED] + DeprecatedPendingDataPacketsV0KeyName: 6, + + // PreCCVKey is the key for storing the preCCV flag, which is set to true + // during the process of a standalone to consumer changeover. + PreCCVKeyName: 7, + + // InitialValSetKey is the key for storing the initial validator set for a consumer + InitialValSetKeyName: 8, + + // NOTE: This prefix is deprecated, but left in place to avoid consumer state migrations + // [DEPRECATED] + DeprecatedLastStandaloneHeightKeyName: 9, + + // NOTE: This key is deprecated, but left in place to avoid consumer state migrations + // [DEPRECATED] + DeprecatedSmallestNonOptOutPowerKeyName: 10, + + // HistoricalInfoKey is the key for storing the historical info for a given height + HistoricalInfoKeyName: 11, + + // PacketMaturityTimeKey is the key for storing maturity time for each received VSC packet + PacketMaturityTimeKeyName: 12, + + // HeightValsetUpdateIDKey is the key for storing the mapping from block height to valset update ID + HeightValsetUpdateIDKeyName: 13, + + // OutstandingDowntimeKey is the key for storing the validators outstanding downtime by consensus address + OutstandingDowntimeKeyName: 14, + + // PendingDataPacketsV1Key is the key for storing a list of data packets + // that cannot be sent yet to the provider chain either because the + // CCV channel is not established or because the client is expired + PendingDataPacketsV1KeyName: 15, + + // CrossChainValidatorKey is the key for storing cross-chain validators by consensus address + CrossChainValidatorKeyName: 16, + + // InitGenesisHeightKey is the key for storing the init genesis height + InitGenesisHeightKeyName: 17, + + // StandaloneTransferChannelIDKey is the key for storing the channelID of transfer channel + // that existed from a standalone chain changing over to a consumer + StandaloneTransferChannelIDKeyName: 18, + + // PrevStandaloneChainKey is the key for storing the flag marking whether this chain was previously standalone + PrevStandaloneChainKeyName: 19, + + // PendingPacketsIndexKey is the key for storing a FIFO queue of pending packets. + PendingPacketsIndexKeyName: 20, + + // SlashRecordKey is the key for storing the consumer's slash record. + SlashRecordKeyName: 21, + + // ParametersKey is the key for storing the consumer's parameters. + ParametersKeyName: 22, + + // NOTE: DO NOT ADD NEW BYTE PREFIXES HERE WITHOUT ADDING THEM TO TestPreserveBytePrefix() IN keys_test.go + } +} + +// mustGetKeyPrefix returns the key prefix for a given key. +// It panics if there is not byte prefix for the index. +func mustGetKeyPrefix(key string) byte { + keyPrefixes := getKeyPrefixes() + if prefix, found := keyPrefixes[key]; !found { + panic(fmt.Sprintf("could not find key prefix for index %s", key)) + } else { + return prefix + } +} + +// GetAllKeyPrefixes returns all the key prefixes. +// Only used for testing +func GetAllKeyPrefixes() []byte { + prefixMap := getKeyPrefixes() + keys := make([]string, 0, len(prefixMap)) + for k := range prefixMap { + keys = append(keys, k) + } + sort.Strings(keys) + prefixList := make([]byte, 0, len(prefixMap)) + for _, k := range keys { + prefixList = append(prefixList, prefixMap[k]) + } + return prefixList +} + +// GetAllKeys returns the names of all the keys. +// Only used for testing +func GetAllKeyNames() []string { + prefixMap := getKeyPrefixes() + keys := make([]string, 0, len(prefixMap)) + for k := range prefixMap { + keys = append(keys, k) + } + sort.Strings(keys) + return keys +} + // // Fully defined key func section // -// ParametersKey returns the key for the consumer parameters in the store -func ParametersKey() []byte { - return []byte{ParametersByteKey} -} - -// PortKey returns the key to the port ID in the store +// PortKey returns the key for storing the port ID func PortKey() []byte { - return []byte{PortByteKey} + return []byte{mustGetKeyPrefix(PortKeyName)} } -// LastDistributionTransmissionKey returns the key to the last distribution transmission in the store +// LastDistributionTransmissionKey returns the key for storing the last distribution transmission func LastDistributionTransmissionKey() []byte { - return []byte{LastDistributionTransmissionByteKey} + return []byte{mustGetKeyPrefix(LastDistributionTransmissionKeyName)} } // UnbondingTimeKey returns the key for storing the unbonding period func UnbondingTimeKey() []byte { - return []byte{UnbondingTimeByteKey} + return []byte{mustGetKeyPrefix(UnbondingTimeKeyName)} } // ProviderClientIDKey returns the key for storing clientID of the provider func ProviderClientIDKey() []byte { - return []byte{ProviderClientByteKey} + return []byte{mustGetKeyPrefix(ProviderClientIDKeyName)} } -// ProviderChannelKey returns the key for storing channelID of the provider chain -func ProviderChannelKey() []byte { - return []byte{ProviderChannelByteKey} +// ProviderChannelIDKey returns the key for storing channelID of the provider chain +func ProviderChannelIDKey() []byte { + return []byte{mustGetKeyPrefix(ProviderChannelIDKeyName)} } // PendingChangesKey returns the key for storing pending validator set changes func PendingChangesKey() []byte { - return []byte{PendingChangesByteKey} + return []byte{mustGetKeyPrefix(PendingChangesKeyName)} +} + +// PreCCVKey returns the key for storing the preCCV flag, which is set to true +// during the process of a standalone to consumer changeover. +func PreCCVKey() []byte { + return []byte{mustGetKeyPrefix(PreCCVKeyName)} +} + +// InitialValSetKey returns the key for storing the initial validator set for a consumer +func InitialValSetKey() []byte { + return []byte{mustGetKeyPrefix(InitialValSetKeyName)} +} + +// HistoricalInfoKeyPrefix the key prefix for storing the historical info for a given height +func HistoricalInfoKeyPrefix() []byte { + return []byte{mustGetKeyPrefix(HistoricalInfoKeyName)} } -// HistoricalInfoKey returns the key to historical info to a given block height +// HistoricalInfoKey returns the key for storing the historical info for a given height func HistoricalInfoKey(height int64) []byte { hBytes := make([]byte, 8) binary.BigEndian.PutUint64(hBytes, uint64(height)) - return append([]byte{HistoricalInfoBytePrefix}, hBytes...) + return append(HistoricalInfoKeyPrefix(), hBytes...) +} + +// PacketMaturityTimeKeyPrefix returns the key prefix for storing maturity time for each received VSC packet +func PacketMaturityTimeKeyPrefix() []byte { + return []byte{mustGetKeyPrefix(PacketMaturityTimeKeyName)} } // PacketMaturityTimeKey returns the key for storing the maturity time for a given received VSC packet id @@ -164,7 +272,7 @@ func PacketMaturityTimeKey(vscID uint64, maturityTime time.Time) []byte { ts := uint64(maturityTime.UTC().UnixNano()) return ccvtypes.AppendMany( // Append the prefix - []byte{PacketMaturityTimeBytePrefix}, + PacketMaturityTimeKeyPrefix(), // Append the time sdk.Uint64ToBigEndian(ts), // Append the vscID @@ -172,21 +280,31 @@ func PacketMaturityTimeKey(vscID uint64, maturityTime time.Time) []byte { ) } -// HeightValsetUpdateIDKey returns the key to a valset update ID for a given block height +// HeightValsetUpdateIDKeyPrefix returns the key for storing a valset update ID for a given block height +func HeightValsetUpdateIDKeyPrefix() []byte { + return []byte{mustGetKeyPrefix(HeightValsetUpdateIDKeyName)} +} + +// HeightValsetUpdateIDKey returns the key for storing a valset update ID for a given block height func HeightValsetUpdateIDKey(height uint64) []byte { hBytes := make([]byte, 8) binary.BigEndian.PutUint64(hBytes, height) - return append([]byte{HeightValsetUpdateIDBytePrefix}, hBytes...) + return append(HeightValsetUpdateIDKeyPrefix(), hBytes...) +} + +// OutstandingDowntimeKeyPrefix returns the key prefix for storing a validators' outstanding downtime by consensus address +func OutstandingDowntimeKeyPrefix() []byte { + return []byte{mustGetKeyPrefix(OutstandingDowntimeKeyName)} } -// OutstandingDowntimeKey returns the key to a validators' outstanding downtime by consensus address +// OutstandingDowntimeKey returns the key for storing a validators' outstanding downtime by consensus address func OutstandingDowntimeKey(address sdk.ConsAddress) []byte { - return append([]byte{OutstandingDowntimeBytePrefix}, address.Bytes()...) + return append(OutstandingDowntimeKeyPrefix(), address.Bytes()...) } -// CrossChainValidatorKey returns the key to a cross chain validator by consensus address -func CrossChainValidatorKey(addr []byte) []byte { - return append([]byte{CrossChainValidatorBytePrefix}, addr...) +// PendingDataPacketsV1KeyPrefix returns the key prefix for storing a queue of data packets to be sent to the provider. +func PendingDataPacketsV1KeyPrefix() []byte { + return []byte{mustGetKeyPrefix(PendingDataPacketsV1KeyName)} } // PendingDataPacketsKey returns the key for storing a queue of data packets to be sent to the provider. @@ -194,42 +312,49 @@ func CrossChainValidatorKey(addr []byte) []byte { // - the CCV channel is not yet established // - the client is expired // - A slash packet is being bounced between consumer and provider (not yet implemented) -func PendingDataPacketsKey(idx uint64) []byte { - return append([]byte{PendingDataPacketsBytePrefix}, sdk.Uint64ToBigEndian(idx)...) +func PendingDataPacketsV1Key(idx uint64) []byte { + return append(PendingDataPacketsV1KeyPrefix(), sdk.Uint64ToBigEndian(idx)...) } -func PreCCVKey() []byte { - return []byte{PreCCVByteKey} +// CrossChainValidatorKeyPrefix returns the key prefix for storing a cross chain validator by consensus address +func CrossChainValidatorKeyPrefix() []byte { + return []byte{mustGetKeyPrefix(CrossChainValidatorKeyName)} } -func InitialValSetKey() []byte { - return []byte{InitialValSetByteKey} +// CrossChainValidatorKey returns the key for storing a cross chain validator by consensus address +func CrossChainValidatorKey(addr []byte) []byte { + return append(CrossChainValidatorKeyPrefix(), addr...) } +// InitGenesisHeightKey returns the key for storing the init genesis height func InitGenesisHeightKey() []byte { - return []byte{InitGenesisHeightByteKey} + return []byte{mustGetKeyPrefix(InitGenesisHeightKeyName)} } -// StandaloneTransferChannelIDKey returns the key to the transfer channelID that existed from a standalone chain +// StandaloneTransferChannelIDKey returns the key for storing the transfer channelID that existed from a standalone chain // changing over to a consumer func StandaloneTransferChannelIDKey() []byte { - return []byte{StandaloneTransferChannelIDByteKey} + return []byte{mustGetKeyPrefix(StandaloneTransferChannelIDKeyName)} } -// PrevStandaloneChainKey returns the key to the flag marking whether this chain was previously standalone +// PrevStandaloneChainKey returns the key for storing the flag marking whether this chain was previously standalone func PrevStandaloneChainKey() []byte { - return []byte{PrevStandaloneChainByteKey} + return []byte{mustGetKeyPrefix(PrevStandaloneChainKeyName)} } -// PendingPacketsIndexKey returns the key to the pending packets index. -// This index is used for implementing a FIFO queue of pending packets in the KV store. +// PendingPacketsIndexKey returns the key for storing the FIFO queue of pending packets. func PendingPacketsIndexKey() []byte { - return []byte{PendingPacketsIndexByteKey} + return []byte{mustGetKeyPrefix(PendingPacketsIndexKeyName)} } -// SlashRecordKey returns the key storing the consumer's slash record. +// SlashRecordKey returns the key for storing the consumer's slash record. func SlashRecordKey() []byte { - return []byte{SlashRecordByteKey} + return []byte{mustGetKeyPrefix(SlashRecordKeyName)} +} + +// ParametersKey returns the key for storing the consumer parameters +func ParametersKey() []byte { + return []byte{mustGetKeyPrefix(ParametersKeyName)} } // NOTE: DO NOT ADD FULLY DEFINED KEY FUNCTIONS WITHOUT ADDING THEM TO getAllFullyDefinedKeys() IN keys_test.go diff --git a/x/ccv/consumer/types/keys_test.go b/x/ccv/consumer/types/keys_test.go index 14f31a64ee..059699a6d2 100644 --- a/x/ccv/consumer/types/keys_test.go +++ b/x/ccv/consumer/types/keys_test.go @@ -1,15 +1,19 @@ -package types +package types_test import ( + "strings" "testing" "time" "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" + consumertypes "github.com/cosmos/interchain-security/v6/x/ccv/consumer/types" ) // Tests that all singular keys, or prefixes to fully resolves keys are non duplicate byte values. func TestNoDuplicates(t *testing.T) { - prefixes := getAllKeyPrefixes() + prefixes := consumertypes.GetAllKeyPrefixes() seen := []byte{} for _, prefix := range prefixes { @@ -18,37 +22,74 @@ func TestNoDuplicates(t *testing.T) { } } -// Returns all key prefixes to fully resolved keys, any of which should be a single, unique byte. -func getAllKeyPrefixes() []byte { - return []byte{ - PortByteKey, - LastDistributionTransmissionByteKey, - UnbondingTimeByteKey, - ProviderClientByteKey, - ProviderChannelByteKey, - PendingChangesByteKey, - PendingDataPacketsByteKey, - PreCCVByteKey, - InitialValSetByteKey, - LastStandaloneHeightByteKey, - DeprecatedSmallestNonOptOutPowerByteKey, - HistoricalInfoBytePrefix, - PacketMaturityTimeBytePrefix, - HeightValsetUpdateIDBytePrefix, - OutstandingDowntimeBytePrefix, - PendingDataPacketsBytePrefix, - CrossChainValidatorBytePrefix, - InitGenesisHeightByteKey, - StandaloneTransferChannelIDByteKey, - PrevStandaloneChainByteKey, - PendingPacketsIndexByteKey, - SlashRecordByteKey, - ParametersByteKey, - } +// Test that the value of all byte prefixes is preserved +func TestPreserveBytePrefix(t *testing.T) { + i := 0 + require.Equal(t, byte(0), consumertypes.PortKey()[0]) + i++ + require.Equal(t, byte(1), consumertypes.LastDistributionTransmissionKey()[0]) + i++ + require.Equal(t, byte(2), consumertypes.UnbondingTimeKey()[0]) + i++ + require.Equal(t, byte(3), consumertypes.ProviderClientIDKey()[0]) + i++ + require.Equal(t, byte(4), consumertypes.ProviderChannelIDKey()[0]) + i++ + require.Equal(t, byte(5), consumertypes.PendingChangesKey()[0]) + i++ + // reserve 6 as deprecated + i++ + require.Equal(t, byte(7), consumertypes.PreCCVKey()[0]) + i++ + require.Equal(t, byte(8), consumertypes.InitialValSetKey()[0]) + i++ + // reserve 9 as deprecated + i++ + // reserve 10 as deprecated + i++ + require.Equal(t, byte(11), consumertypes.HistoricalInfoKeyPrefix()[0]) + i++ + require.Equal(t, byte(12), consumertypes.PacketMaturityTimeKeyPrefix()[0]) + i++ + require.Equal(t, byte(13), consumertypes.HeightValsetUpdateIDKeyPrefix()[0]) + i++ + require.Equal(t, byte(14), consumertypes.OutstandingDowntimeKeyPrefix()[0]) + i++ + require.Equal(t, byte(15), consumertypes.PendingDataPacketsV1KeyPrefix()[0]) + i++ + require.Equal(t, byte(16), consumertypes.CrossChainValidatorKeyPrefix()[0]) + i++ + require.Equal(t, byte(17), consumertypes.InitGenesisHeightKey()[0]) + i++ + require.Equal(t, byte(18), consumertypes.StandaloneTransferChannelIDKey()[0]) + i++ + require.Equal(t, byte(19), consumertypes.PrevStandaloneChainKey()[0]) + i++ + require.Equal(t, byte(20), consumertypes.PendingPacketsIndexKey()[0]) + i++ + require.Equal(t, byte(21), consumertypes.SlashRecordKey()[0]) + i++ + require.Equal(t, byte(22), consumertypes.ParametersKey()[0]) + i++ + + prefixes := consumertypes.GetAllKeyPrefixes() + require.Equal(t, len(prefixes), i) } func TestNoPrefixOverlap(t *testing.T) { keys := getAllFullyDefinedKeys() + + // Make sure that we check all the fully defined keys. + // All non-deprecated keys should have such a function. + keyNames := consumertypes.GetAllKeyNames() + nonDeprecatedKey := []string{} + for _, name := range keyNames { + if !strings.Contains(name, "Deprecated") { + nonDeprecatedKey = append(nonDeprecatedKey, name) + } + } + require.Equal(t, len(nonDeprecatedKey), len(keys)) + seenPrefixes := []byte{} for _, key := range keys { require.NotContains(t, seenPrefixes, key[0], "Duplicate key prefix: %v", key[0]) @@ -60,26 +101,25 @@ func TestNoPrefixOverlap(t *testing.T) { // Note we only care about checking prefixes here, so parameters into the key functions are arbitrary. func getAllFullyDefinedKeys() [][]byte { return [][]byte{ - PortKey(), - LastDistributionTransmissionKey(), - UnbondingTimeKey(), - ProviderClientIDKey(), - ProviderChannelKey(), - PendingChangesKey(), - // PendingDataPacketsKey() does not use duplicated prefix with value of 0x06 - PreCCVKey(), - InitialValSetKey(), - // LastStandaloneHeightKey() is deprecated - HistoricalInfoKey(0), - PacketMaturityTimeKey(0, time.Time{}), - HeightValsetUpdateIDKey(0), - OutstandingDowntimeKey([]byte{}), - PendingDataPacketsKey(473289), - CrossChainValidatorKey([]byte{}), - InitGenesisHeightKey(), - StandaloneTransferChannelIDKey(), - PrevStandaloneChainKey(), - PendingPacketsIndexKey(), - SlashRecordKey(), + consumertypes.PortKey(), + consumertypes.LastDistributionTransmissionKey(), + consumertypes.UnbondingTimeKey(), + consumertypes.ProviderClientIDKey(), + consumertypes.ProviderChannelIDKey(), + consumertypes.PendingChangesKey(), + consumertypes.HistoricalInfoKey(0), + consumertypes.PacketMaturityTimeKey(0, time.Time{}), + consumertypes.HeightValsetUpdateIDKey(0), + consumertypes.OutstandingDowntimeKey(sdk.ConsAddress([]byte{0x05})), + consumertypes.CrossChainValidatorKey([]byte{0x05}), + consumertypes.PendingDataPacketsV1Key(0), + consumertypes.PreCCVKey(), + consumertypes.InitialValSetKey(), + consumertypes.InitGenesisHeightKey(), + consumertypes.StandaloneTransferChannelIDKey(), + consumertypes.PrevStandaloneChainKey(), + consumertypes.PendingPacketsIndexKey(), + consumertypes.SlashRecordKey(), + consumertypes.ParametersKey(), } } diff --git a/x/ccv/provider/types/keys.go b/x/ccv/provider/types/keys.go index 829fa85103..683a0927d5 100644 --- a/x/ccv/provider/types/keys.go +++ b/x/ccv/provider/types/keys.go @@ -151,7 +151,7 @@ const ( // getKeyPrefixes returns a constant map of all the byte prefixes for existing keys func getKeyPrefixes() map[string]byte { return map[string]byte{ - // ParametersKey is the is the key for storing provider's parameters. + // ParametersKey is the key for storing provider's parameters. // note that this was set to the max uint8 type value 0xFF in order to protect // from using the ICS v5.0.0 provider module by mistake ParametersKeyName: byte(0xFF), @@ -337,7 +337,7 @@ func getKeyPrefixes() map[string]byte { // consumer validators addresses that need to be pruned. ConsumerAddrsToPruneV2KeyName: 41, - // LastProviderConsensusValsKey is the byte prefix for storing the last validator set + // LastProviderConsensusValsKey is the key for storing the last validator set // sent to the consensus engine of the provider chain LastProviderConsensusValsKeyName: 42,