From 01b20efbbce813a9d3f617b5d2560ace17620561 Mon Sep 17 00:00:00 2001 From: mpoke Date: Wed, 14 Dec 2022 12:20:12 +0100 Subject: [PATCH 1/6] bringing changes from #583 wip --- tests/e2e/stop_consumer.go | 2 +- x/ccv/provider/keeper/genesis.go | 23 +-- x/ccv/provider/keeper/genesis_test.go | 2 +- x/ccv/provider/keeper/grpc_query.go | 14 +- x/ccv/provider/keeper/hooks.go | 3 +- x/ccv/provider/keeper/keeper.go | 6 +- x/ccv/provider/keeper/keeper_test.go | 8 +- x/ccv/provider/keeper/key_assignment.go | 55 +++-- x/ccv/provider/keeper/key_assignment_test.go | 9 +- x/ccv/provider/keeper/proposal.go | 202 ++++++++----------- x/ccv/provider/keeper/proposal_test.go | 109 ++++++---- x/ccv/provider/types/keys.go | 50 +++-- 12 files changed, 250 insertions(+), 233 deletions(-) diff --git a/tests/e2e/stop_consumer.go b/tests/e2e/stop_consumer.go index 758eb4653a..5854f5da2e 100644 --- a/tests/e2e/stop_consumer.go +++ b/tests/e2e/stop_consumer.go @@ -137,7 +137,7 @@ func (s *CCVTestSuite) checkConsumerChainIsRemoved(chainID string, checkChannel } // check UnbondingOps were deleted and undelegation entries aren't onHold - providerKeeper.IterateOverUnbondingOpIndex( + providerKeeper.IterateUnbondingOpIndex( s.providerCtx(), chainID, func(vscID uint64, ubdIndex []uint64) (stop bool) { diff --git a/x/ccv/provider/keeper/genesis.go b/x/ccv/provider/keeper/genesis.go index 12ae1f046b..45f66651aa 100644 --- a/x/ccv/provider/keeper/genesis.go +++ b/x/ccv/provider/keeper/genesis.go @@ -2,7 +2,6 @@ package keeper import ( "fmt" - "time" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/interchain-security/x/ccv/provider/types" @@ -33,12 +32,12 @@ func (k Keeper) InitGenesis(ctx sdk.Context, genState *types.GenesisState) { for _, prop := range genState.ConsumerAdditionProposals { // prevent implicit memory aliasing prop := prop - if err := k.SetPendingConsumerAdditionProp(ctx, &prop); err != nil { - panic(fmt.Errorf("pending create consumer chain proposal could not be persisted: %w", err)) - } + k.SetPendingConsumerAdditionProp(ctx, &prop) } for _, prop := range genState.ConsumerRemovalProposals { - k.SetPendingConsumerRemovalProp(ctx, prop.ChainId, prop.StopTime) + // prevent implicit memory aliasing + prop := prop + k.SetPendingConsumerRemovalProp(ctx, &prop) } for _, ubdOp := range genState.UnbondingOps { k.SetUnbondingOp(ctx, ubdOp) @@ -116,7 +115,7 @@ func (k Keeper) ExportGenesis(ctx sdk.Context) *types.GenesisState { panic(fmt.Errorf("cannot find genesis for consumer chain %s with client %s", chainID, clientID)) } cs.SlashDowntimeAck = k.GetSlashAcks(ctx, chainID) - k.IterateOverUnbondingOpIndex(ctx, chainID, func(vscID uint64, ubdIndex []uint64) (stop bool) { + k.IterateUnbondingOpIndex(ctx, chainID, func(vscID uint64, ubdIndex []uint64) (stop bool) { cs.UnbondingOpsIndex = append(cs.UnbondingOpsIndex, types.UnbondingOpIndex{ValsetUpdateId: vscID, UnbondingOpIndex: ubdIndex}, ) @@ -138,7 +137,7 @@ func (k Keeper) ExportGenesis(ctx sdk.Context) *types.GenesisState { }) ubdOps := []ccv.UnbondingOp{} - k.IterateOverUnbondingOps(ctx, func(id uint64, ubdOp ccv.UnbondingOp) (stop bool) { + k.IterateUnbondingOps(ctx, func(id uint64, ubdOp ccv.UnbondingOp) (stop bool) { ubdOps = append(ubdOps, ubdOp) return false // do not stop the iteration }) @@ -149,26 +148,25 @@ func (k Keeper) ExportGenesis(ctx sdk.Context) *types.GenesisState { } addProps := []types.ConsumerAdditionProposal{} - k.IteratePendingConsumerAdditionProps(ctx, func(_ time.Time, prop types.ConsumerAdditionProposal) (stop bool) { + k.IteratePendingConsumerAdditionProps(ctx, func(prop types.ConsumerAdditionProposal) (stop bool) { addProps = append(addProps, prop) return false // do not stop the iteration }) remProps := []types.ConsumerRemovalProposal{} - k.IteratePendingConsumerRemovalProps(ctx, func(_ time.Time, prop types.ConsumerRemovalProposal) (stop bool) { + k.IteratePendingConsumerRemovalProps(ctx, func(prop types.ConsumerRemovalProposal) (stop bool) { remProps = append(remProps, prop) return false // do not stop the iteration }) // Export key assignment states validatorConsumerPubKeys := []types.ValidatorConsumerPubKey{} - k.IterateAllValidatorConsumerPubKeys(ctx, func(chainID string, providerAddr sdk.ConsAddress, consumerKey tmcrypto.PublicKey) (stop bool) { + k.IterateAllValidatorConsumerPubKeys(ctx, func(chainID string, providerAddr sdk.ConsAddress, consumerKey tmcrypto.PublicKey) { validatorConsumerPubKeys = append(validatorConsumerPubKeys, types.ValidatorConsumerPubKey{ ChainId: chainID, ProviderAddr: providerAddr, ConsumerKey: &consumerKey, }) - return false // do not stop the iteration }) validatorsByConsumerAddr := []types.ValidatorByConsumerAddr{} @@ -184,13 +182,12 @@ func (k Keeper) ExportGenesis(ctx sdk.Context) *types.GenesisState { consumerAddrsToPrune := []types.ConsumerAddrsToPrune{} // ConsumerAddrsToPrune are added only for registered consumer chains k.IterateConsumerChains(ctx, func(ctx sdk.Context, chainID string, _ string) (stopOuter bool) { - k.IterateConsumerAddrsToPrune(ctx, chainID, func(vscID uint64, consumerAddrs types.AddressList) (stopInner bool) { + k.IterateConsumerAddrsToPrune(ctx, chainID, func(vscID uint64, consumerAddrs types.AddressList) { consumerAddrsToPrune = append(consumerAddrsToPrune, types.ConsumerAddrsToPrune{ ChainId: chainID, VscId: vscID, ConsumerAddrs: &consumerAddrs, }) - return false // do not stop the iteration }) return false // do not stop the iteration }) diff --git a/x/ccv/provider/keeper/genesis_test.go b/x/ccv/provider/keeper/genesis_test.go index 1be0e4add5..fcc3f207e3 100644 --- a/x/ccv/provider/keeper/genesis_test.go +++ b/x/ccv/provider/keeper/genesis_test.go @@ -129,7 +129,7 @@ func TestInitAndExportGenesis(t *testing.T) { addProp, found := pk.GetPendingConsumerAdditionProp(ctx, oneHourFromNow, cChainIDs[0]) require.True(t, found) require.Equal(t, provGenesis.ConsumerAdditionProposals[0], addProp) - require.True(t, pk.GetPendingConsumerRemovalProp(ctx, cChainIDs[0], oneHourFromNow)) + require.True(t, pk.IsPendingConsumerRemovalProp(ctx, cChainIDs[0], oneHourFromNow)) require.Equal(t, provGenesis.Params, pk.GetParams(ctx)) gotConsTmPubKey, found := pk.GetValidatorConsumerPubKey(ctx, cChainIDs[0], provAddr) diff --git a/x/ccv/provider/keeper/grpc_query.go b/x/ccv/provider/keeper/grpc_query.go index af24b6cc00..19616042b0 100644 --- a/x/ccv/provider/keeper/grpc_query.go +++ b/x/ccv/provider/keeper/grpc_query.go @@ -56,8 +56,13 @@ func (k Keeper) QueryConsumerChainStarts(goCtx context.Context, req *types.Query return nil, status.Error(codes.InvalidArgument, "empty request") } + // get all pending consumer addition proposals ctx := sdk.UnwrapSDKContext(goCtx) - props := k.GetAllConsumerAdditionProps(ctx) + props := types.ConsumerAdditionProposals{} + k.IteratePendingConsumerAdditionProps(ctx, func(prop types.ConsumerAdditionProposal) (stop bool) { + props.Pending = append(props.Pending, &prop) + return false // do not stop the iteration + }) return &types.QueryConsumerChainStartProposalsResponse{Proposals: &props}, nil } @@ -67,8 +72,13 @@ func (k Keeper) QueryConsumerChainStops(goCtx context.Context, req *types.QueryC return nil, status.Error(codes.InvalidArgument, "empty request") } + // get all pending consumer removal proposals ctx := sdk.UnwrapSDKContext(goCtx) - props := k.GetAllConsumerRemovalProps(ctx) + props := types.ConsumerRemovalProposals{} + k.IteratePendingConsumerRemovalProps(ctx, func(prop types.ConsumerRemovalProposal) (stop bool) { + props.Pending = append(props.Pending, &prop) + return false // do not stop the iteration + }) return &types.QueryConsumerChainStopProposalsResponse{Proposals: &props}, nil } diff --git a/x/ccv/provider/keeper/hooks.go b/x/ccv/provider/keeper/hooks.go index 21d0deb57f..fd2cf40313 100644 --- a/x/ccv/provider/keeper/hooks.go +++ b/x/ccv/provider/keeper/hooks.go @@ -105,7 +105,7 @@ func (h Hooks) AfterValidatorRemoved(ctx sdk.Context, valConsAddr sdk.ConsAddres chainID string, providerAddr sdk.ConsAddress, consumerKey tmprotocrypto.PublicKey, - ) (stop bool) { + ) { if providerAddr.Equals(valConsAddr) { toDelete = append(toDelete, StoreKey{ @@ -114,7 +114,6 @@ func (h Hooks) AfterValidatorRemoved(ctx sdk.Context, valConsAddr sdk.ConsAddres ConsumerKey: consumerKey, }) } - return false // do not stop }) for _, key := range toDelete { consumerAddr := utils.TMCryptoPublicKeyToConsAddr(key.ConsumerKey) diff --git a/x/ccv/provider/keeper/keeper.go b/x/ccv/provider/keeper/keeper.go index 9daf0101f3..96853a940b 100644 --- a/x/ccv/provider/keeper/keeper.go +++ b/x/ccv/provider/keeper/keeper.go @@ -326,7 +326,7 @@ func (k Keeper) DeleteUnbondingOp(ctx sdk.Context, id uint64) { store.Delete(types.UnbondingOpKey(id)) } -func (k Keeper) IterateOverUnbondingOps(ctx sdk.Context, cb func(id uint64, ubdOp ccv.UnbondingOp) (stop bool)) { +func (k Keeper) IterateUnbondingOps(ctx sdk.Context, cb func(id uint64, ubdOp ccv.UnbondingOp) (stop bool)) { store := ctx.KVStore(k.storeKey) iterator := sdk.KVStorePrefixIterator(store, []byte{types.UnbondingOpBytePrefix}) @@ -361,8 +361,8 @@ func (k Keeper) SetUnbondingOpIndex(ctx sdk.Context, chainID string, valsetUpdat store.Set(types.UnbondingOpIndexKey(chainID, valsetUpdateID), bz) } -// IterateOverUnbondingOpIndex iterates over the unbonding indexes for a given chain id. -func (k Keeper) IterateOverUnbondingOpIndex( +// IterateUnbondingOpIndex iterates over the unbonding indexes for a given chain id. +func (k Keeper) IterateUnbondingOpIndex( ctx sdk.Context, chainID string, cb func(vscID uint64, ubdIndex []uint64) (stop bool), diff --git a/x/ccv/provider/keeper/keeper_test.go b/x/ccv/provider/keeper/keeper_test.go index 895fb113c8..c5d44f26ad 100644 --- a/x/ccv/provider/keeper/keeper_test.go +++ b/x/ccv/provider/keeper/keeper_test.go @@ -183,7 +183,7 @@ func TestInitHeight(t *testing.T) { } } -func TestIterateOverUnbondingOpIndex(t *testing.T) { +func TestIterateUnbondingOpIndex(t *testing.T) { providerKeeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) defer ctrl.Finish() @@ -200,7 +200,7 @@ func TestIterateOverUnbondingOpIndex(t *testing.T) { // check iterator returns expected entries i := 1 - providerKeeper.IterateOverUnbondingOpIndex(ctx, chainID, func(vscID uint64, ubdIndex []uint64) (stop bool) { + providerKeeper.IterateUnbondingOpIndex(ctx, chainID, func(vscID uint64, ubdIndex []uint64) (stop bool) { require.Equal(t, uint64(i), vscID) require.EqualValues(t, unbondingOpIndex[:i], ubdIndex) i++ @@ -454,7 +454,7 @@ func TestIterateOverUnbondingOps(t *testing.T) { require.Empty(t, result, "initial result not empty") // iterate and check all results are returned - pk.IterateOverUnbondingOps(ctx, testIterateAll) + pk.IterateUnbondingOps(ctx, testIterateAll) require.Len(t, result, 2, "wrong result len - should be 2, got %d", len(result)) require.Contains(t, result, ops[0], "result does not contain '%s'", ops[0]) require.Contains(t, result, ops[1], "result does not contain '%s'", ops[1]) @@ -467,7 +467,7 @@ func TestIterateOverUnbondingOps(t *testing.T) { require.Empty(t, result, "initial result not empty") // iterate and check 1 result is returned - pk.IterateOverUnbondingOps(ctx, testGetFirst) + pk.IterateUnbondingOps(ctx, testGetFirst) require.Len(t, result, 1, "wrong result len - should be 1, got %d", len(result)) require.Contains(t, result, ops[0], "result does not contain '%s'", ops[0]) require.NotContains(t, result, ops[1], "result should not contain '%s'", ops[1]) diff --git a/x/ccv/provider/keeper/key_assignment.go b/x/ccv/provider/keeper/key_assignment.go index 8c995409bc..9eb27cee55 100644 --- a/x/ccv/provider/keeper/key_assignment.go +++ b/x/ccv/provider/keeper/key_assignment.go @@ -46,6 +46,10 @@ func (k Keeper) SetValidatorConsumerPubKey( } // IterateValidatorConsumerPubKeys iterates over the validators public keys assigned for a consumer chain +// +// Note that the validators public keys assigned for a consumer chain are stored under keys +// with the following format: UnbondingOpIndexBytePrefix | len(chainID) | chainID | providerAddress +// Thus, the iteration is in ascending order of providerAddresses. func (k Keeper) IterateValidatorConsumerPubKeys( ctx sdk.Context, chainID string, @@ -53,15 +57,15 @@ func (k Keeper) IterateValidatorConsumerPubKeys( ) { store := ctx.KVStore(k.storeKey) prefix := types.ChainIdWithLenKey(types.ConsumerValidatorsBytePrefix, chainID) - iter := sdk.KVStorePrefixIterator(store, prefix) - defer iter.Close() - for ; iter.Valid(); iter.Next() { - _, providerAddr, err := types.ParseChainIdAndConsAddrKey(types.ConsumerValidatorsBytePrefix, iter.Key()) + iterator := sdk.KVStorePrefixIterator(store, prefix) + defer iterator.Close() + for ; iterator.Valid(); iterator.Next() { + _, providerAddr, err := types.ParseChainIdAndConsAddrKey(types.ConsumerValidatorsBytePrefix, iterator.Key()) if err != nil { panic(err) } var consumerKey tmprotocrypto.PublicKey - err = consumerKey.Unmarshal(iter.Value()) + err = consumerKey.Unmarshal(iterator.Value()) if err != nil { panic(err) } @@ -75,9 +79,13 @@ func (k Keeper) IterateValidatorConsumerPubKeys( // IterateAllValidatorConsumerPubKeys iterates over the validators public keys assigned for all consumer chain. // Note that IterateValidatorConsumerPubKeys cannot be used as consumer keys can be assigned for chain IDs // that are not yet known to the provider. +// +// Note that the validators public keys assigned for all consumer chain are stored under keys +// with the following format: UnbondingOpIndexBytePrefix | len(chainID) | chainID | providerAddress +// Thus, the order of iteration is undetermined. func (k Keeper) IterateAllValidatorConsumerPubKeys( ctx sdk.Context, - cb func(chainID string, providerAddr sdk.ConsAddress, consumerKey tmprotocrypto.PublicKey) (stop bool), + cb func(chainID string, providerAddr sdk.ConsAddress, consumerKey tmprotocrypto.PublicKey), ) { store := ctx.KVStore(k.storeKey) iter := sdk.KVStorePrefixIterator(store, []byte{types.ConsumerValidatorsBytePrefix}) @@ -92,10 +100,8 @@ func (k Keeper) IterateAllValidatorConsumerPubKeys( if err != nil { panic(err) } - stop := cb(chainID, providerAddr, consumerKey) - if stop { - break - } + cb(chainID, providerAddr, consumerKey) + // never stop the iteration } } @@ -142,6 +148,10 @@ func (k Keeper) SetValidatorByConsumerAddr( // IterateValidatorsByConsumerAddr iterates over all the mappings from consensus addresses // on a given consumer chain to consensus addresses on the provider chain +// +// Note that the mappings for a consumer chain are stored under keys with the following format: +// ValidatorsByConsumerAddrBytePrefix | len(chainID) | chainID | consumerAddress +// Thus, the iteration is in ascending order of consumerAddresses. func (k Keeper) IterateValidatorsByConsumerAddr( ctx sdk.Context, chainID string, @@ -172,6 +182,10 @@ func (k Keeper) IterateValidatorsByConsumerAddr( // on any consumer chain to consensus addresses on the provider chain. // Note that IterateValidatorsByConsumerAddr cannot be used as consumer keys can be assigned // for chain IDs that are not yet known to the provider. +// +// Note that the mappings for all consumer chain are stored under keys with the following format: +// ValidatorsByConsumerAddrBytePrefix | len(chainID) | chainID | consumerAddress +// Thus, the order of iteration is undetermined. func (k Keeper) IterateAllValidatorsByConsumerAddr( ctx sdk.Context, cb func(chainID string, consumerAddr sdk.ConsAddress, providerAddr sdk.ConsAddress) (stop bool), @@ -246,6 +260,10 @@ func (k Keeper) SetKeyAssignmentReplacement( // IterateKeyAssignmentReplacements iterates through all pairs of previous assigned consumer keys // and current powers for all provider validator for which key assignments were received in this block. +// +// Note that the pairs are stored under keys with the following format: +// KeyAssignmentReplacementsBytePrefix | len(chainID) | chainID | providerAddress +// Thus, the iteration is in ascending order of providerAddresses. func (k Keeper) IterateKeyAssignmentReplacements( ctx sdk.Context, chainID string, @@ -325,10 +343,16 @@ func (k Keeper) GetConsumerAddrsToPrune( return } +// IterateConsumerAddrsToPrune iterates over the list of all consumer addresses +// that can be pruned for a given chainID. +// +// Note that the list of all consumer addresses is stored under keys with the following format: +// ConsumerAddrsToPruneBytePrefix | len(chainID) | chainID | vscID +// Thus, the iteration is in ascending order of vscIDs. func (k Keeper) IterateConsumerAddrsToPrune( ctx sdk.Context, chainID string, - cb func(vscID uint64, consumerAddrsToPrune types.AddressList) (stop bool), + cb func(vscID uint64, consumerAddrsToPrune types.AddressList), ) { store := ctx.KVStore(k.storeKey) iteratorPrefix := types.ChainIdWithLenKey(types.ConsumerAddrsToPruneBytePrefix, chainID) @@ -344,10 +368,8 @@ func (k Keeper) IterateConsumerAddrsToPrune( if err != nil { panic(err) } - stop := cb(vscID, consumerAddrsToPrune) - if stop { - break - } + cb(vscID, consumerAddrsToPrune) + // never stop the iteration } } @@ -605,9 +627,8 @@ func (k Keeper) DeleteKeyAssignments(ctx sdk.Context, chainID string) { } // delete ValidatorConsumerPubKey var ids []uint64 - k.IterateConsumerAddrsToPrune(ctx, chainID, func(vscID uint64, _ types.AddressList) (stop bool) { + k.IterateConsumerAddrsToPrune(ctx, chainID, func(vscID uint64, _ types.AddressList) { ids = append(ids, vscID) - return false // do not stop the iteration }) for _, id := range ids { k.DeleteConsumerAddrsToPrune(ctx, chainID, id) diff --git a/x/ccv/provider/keeper/key_assignment_test.go b/x/ccv/provider/keeper/key_assignment_test.go index 0bcd00b81e..6f9d4c937c 100644 --- a/x/ccv/provider/keeper/key_assignment_test.go +++ b/x/ccv/provider/keeper/key_assignment_test.go @@ -134,14 +134,13 @@ func TestIterateAllValidatorConsumerPubKeys(t *testing.T) { } result := []testAssignment{} - cbIterateAll := func(chainID string, iteratorProviderAddr sdk.ConsAddress, consumerKey tmprotocrypto.PublicKey) (stop bool) { + cbIterateAll := func(chainID string, iteratorProviderAddr sdk.ConsAddress, consumerKey tmprotocrypto.PublicKey) { require.Equal(t, providerAddr, iteratorProviderAddr, "unexpected provider address in iterator - expecting just 1 provider address") result = append(result, testAssignment{ chainID: chainID, providerAddr: iteratorProviderAddr, consumerPubKey: consumerKey, }) - return false } keeper.IterateAllValidatorConsumerPubKeys(ctx, cbIterateAll) @@ -152,14 +151,13 @@ func TestIterateAllValidatorConsumerPubKeys(t *testing.T) { } result = []testAssignment{} - cbIterateOne := func(chainID string, iteratorProviderAddr sdk.ConsAddress, consumerKey tmprotocrypto.PublicKey) (stop bool) { + cbIterateOne := func(chainID string, iteratorProviderAddr sdk.ConsAddress, consumerKey tmprotocrypto.PublicKey) { require.Equal(t, providerAddr, iteratorProviderAddr, "unexpected provider address in iterator - expecting just 1 provider address") result = append(result, testAssignment{ chainID: chainID, providerAddr: iteratorProviderAddr, consumerPubKey: consumerKey, }) - return false } keeper.IterateAllValidatorConsumerPubKeys(ctx, cbIterateOne) @@ -421,12 +419,11 @@ func checkCorrectPruningProperty(ctx sdk.Context, k providerkeeper.Keeper, chain - or there exists a vscID in ConsumerAddrsToPrune s.t. cAddr in ConsumerAddrsToPrune(vscID) */ willBePruned := map[string]bool{} - k.IterateConsumerAddrsToPrune(ctx, chainID, func(vscID uint64, addrList providertypes.AddressList) (stop bool) { + k.IterateConsumerAddrsToPrune(ctx, chainID, func(vscID uint64, addrList providertypes.AddressList) { for _, cAddr := range addrList.Addresses { addr := sdk.ConsAddress(cAddr) willBePruned[addr.String()] = true } - return false }) good := true k.IterateAllValidatorsByConsumerAddr(ctx, func(chainID string, consumerAddr sdk.ConsAddress, providerAddr sdk.ConsAddress) (stop bool) { diff --git a/x/ccv/provider/keeper/proposal.go b/x/ccv/provider/keeper/proposal.go index 1b4b3094ca..b158c1f1ec 100644 --- a/x/ccv/provider/keeper/proposal.go +++ b/x/ccv/provider/keeper/proposal.go @@ -33,11 +33,7 @@ func (k Keeper) HandleConsumerAdditionProposal(ctx sdk.Context, p *types.Consume return k.CreateConsumerClient(ctx, p) } - err := k.SetPendingConsumerAdditionProp(ctx, p) - if err != nil { - return err - } - + k.SetPendingConsumerAdditionProp(ctx, p) return nil } @@ -119,7 +115,7 @@ func (k Keeper) HandleConsumerRemovalProposal(ctx sdk.Context, p *types.Consumer return k.StopConsumerChain(ctx, p.ChainId, true) } - k.SetPendingConsumerRemovalProp(ctx, p.ChainId, p.StopTime) + k.SetPendingConsumerRemovalProp(ctx, p) return nil } @@ -168,7 +164,7 @@ func (k Keeper) StopConsumerChain(ctx sdk.Context, chainID string, closeChan boo // release unbonding operations var vscIDs []uint64 // iterate over the consumer chain's unbonding operation VSC ids - k.IterateOverUnbondingOpIndex(ctx, chainID, func(vscID uint64, ids []uint64) (stop bool) { + k.IterateUnbondingOpIndex(ctx, chainID, func(vscID uint64, ids []uint64) (stop bool) { // iterate over the unbonding operations for the current VSC ID var maturedIds []uint64 for _, id := range ids { @@ -292,21 +288,30 @@ func (k Keeper) MakeConsumerGenesis(ctx sdk.Context, prop *types.ConsumerAdditio return gen, hash, nil } -// SetPendingConsumerAdditionProp stores a pending proposal to create a consumer chain client -func (k Keeper) SetPendingConsumerAdditionProp(ctx sdk.Context, clientInfo *types.ConsumerAdditionProposal) error { +// SetPendingConsumerAdditionProp stores a pending consumer addition proposal. +// +// Note that the pending consumer addition proposals are stored under keys with +// the following format: PendingCAPBytePrefix | spawnTime | chainID +// Thus, if multiple consumer addition proposal for the same chain will pass at +// the same time, then only the last one will be stored. +func (k Keeper) SetPendingConsumerAdditionProp(ctx sdk.Context, clientInfo *types.ConsumerAdditionProposal) { store := ctx.KVStore(k.storeKey) bz, err := k.cdc.Marshal(clientInfo) if err != nil { - return err + panic(fmt.Errorf("consumer addition proposal could not be marshaled: %w", err)) } - store.Set(types.PendingCAPKey(clientInfo.SpawnTime, clientInfo.ChainId), bz) - return nil } -// GetPendingConsumerAdditionProp retrieves a pending proposal to create a consumer chain client (by spawn time and chain id) -func (k Keeper) GetPendingConsumerAdditionProp(ctx sdk.Context, spawnTime time.Time, - chainID string) (prop types.ConsumerAdditionProposal, found bool) { +// GetPendingConsumerAdditionProp retrieves a pending consumer addition proposal +// by spawn time and chain id. +// +// Note: this method is only used in testing +func (k Keeper) GetPendingConsumerAdditionProp( + ctx sdk.Context, + spawnTime time.Time, + chainID string, +) (prop types.ConsumerAdditionProposal, found bool) { store := ctx.KVStore(k.storeKey) bz := store.Get(types.PendingCAPKey(spawnTime, chainID)) if len(bz) == 0 { @@ -317,10 +322,12 @@ func (k Keeper) GetPendingConsumerAdditionProp(ctx sdk.Context, spawnTime time.T return prop, true } -// PendingConsumerAdditionPropIterator returns an iterator for iterating through pending consumer addition proposals -func (k Keeper) PendingConsumerAdditionPropIterator(ctx sdk.Context) sdk.Iterator { +// DeletePendingConsumerAdditionProps deletes the given consumer addition proposals +func (k Keeper) DeletePendingConsumerAdditionProps(ctx sdk.Context, proposals ...types.ConsumerAdditionProposal) { store := ctx.KVStore(k.storeKey) - return sdk.KVStorePrefixIterator(store, []byte{types.PendingCAPBytePrefix}) + for _, p := range proposals { + store.Delete(types.PendingCAPKey(p.SpawnTime, p.ChainId)) + } } // BeginBlockInit iterates over the pending consumer addition proposals in order, and creates @@ -329,7 +336,7 @@ func (k Keeper) PendingConsumerAdditionPropIterator(ctx sdk.Context) sdk.Iterato // See: https://github.com/cosmos/ibc/blob/main/spec/app/ics-028-cross-chain-validation/methods.md#ccv-pcf-bblock-init1 // Spec tag:[CCV-PCF-BBLOCK-INIT.1] func (k Keeper) BeginBlockInit(ctx sdk.Context) { - propsToExecute := k.ConsumerAdditionPropsToExecute(ctx) + propsToExecute := k.GetConsumerAdditionPropsToExecute(ctx) for _, prop := range propsToExecute { p := prop @@ -342,91 +349,71 @@ func (k Keeper) BeginBlockInit(ctx sdk.Context) { k.DeletePendingConsumerAdditionProps(ctx, propsToExecute...) } -// ConsumerAdditionPropsToExecute iterates over the pending consumer addition proposals -// and returns an ordered list of proposals to be executed, ie. consumer clients to be created. +// GetConsumerAdditionPropsToExecute returns the pending consumer addition proposals +// that are ready to be executed, i.e., consumer clients to be created. // A prop is included in the returned list if its proposed spawn time has passed. // // Note: this method is split out from BeginBlockInit to be easily unit tested. -func (k Keeper) ConsumerAdditionPropsToExecute(ctx sdk.Context) []types.ConsumerAdditionProposal { - - // store the (to be) executed proposals in order +func (k Keeper) GetConsumerAdditionPropsToExecute(ctx sdk.Context) []types.ConsumerAdditionProposal { propsToExecute := []types.ConsumerAdditionProposal{} - - iterator := k.PendingConsumerAdditionPropIterator(ctx) - defer iterator.Close() - - k.IteratePendingConsumerAdditionProps(ctx, func(spawnTime time.Time, prop types.ConsumerAdditionProposal) (stop bool) { - if !ctx.BlockTime().Before(spawnTime) { + k.IteratePendingConsumerAdditionProps(ctx, func(prop types.ConsumerAdditionProposal) (stop bool) { + if !ctx.BlockTime().Before(prop.SpawnTime) { propsToExecute = append(propsToExecute, prop) return false // do not stop the iteration } - return true // stop iteration, proposals are ordered by spawn time, so no additional pending props are ready to act upon + // stop iteration; proposals are ordered by spawn time, + // so no additional pending props are ready to act upon + return true }) - return propsToExecute } +// IteratePendingConsumerAdditionProps iterates through the pending consumer addition proposals. +// +// Note that the pending consumer addition proposals are stored under keys with the following format: +// PendingCAPBytePrefix | spawnTime | chainID +// Thus, the iteration is in ascending order of spawnTimes. func (k Keeper) IteratePendingConsumerAdditionProps( ctx sdk.Context, - cb func(spawnTime time.Time, prop types.ConsumerAdditionProposal) (stop bool), + cb func(prop types.ConsumerAdditionProposal) (stop bool), ) { - iterator := k.PendingConsumerAdditionPropIterator(ctx) + store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, []byte{types.PendingCAPBytePrefix}) defer iterator.Close() for ; iterator.Valid(); iterator.Next() { - key := iterator.Key() - spawnTime, _, err := types.ParsePendingCAPKey(key) - if err != nil { - panic(fmt.Errorf("failed to parse pending client key: %w", err)) - } - var prop types.ConsumerAdditionProposal k.cdc.MustUnmarshal(iterator.Value(), &prop) - stop := cb(spawnTime, prop) + stop := cb(prop) if stop { break } } } -// GetAllConsumerAdditionProps returns all consumer addition proposals separated into matured and pending. -func (k Keeper) GetAllConsumerAdditionProps(ctx sdk.Context) types.ConsumerAdditionProposals { - props := types.ConsumerAdditionProposals{} - - store := ctx.KVStore(k.storeKey) - iterator := sdk.KVStorePrefixIterator(store, []byte{types.PendingCAPBytePrefix}) - defer iterator.Close() - - for ; iterator.Valid(); iterator.Next() { - var prop types.ConsumerAdditionProposal - k.cdc.MustUnmarshal(iterator.Value(), &prop) - - props.Pending = append(props.Pending, &prop) - } - return props -} - -// DeletePendingConsumerAdditionProps deletes the given consumer addition proposals -func (k Keeper) DeletePendingConsumerAdditionProps(ctx sdk.Context, proposals ...types.ConsumerAdditionProposal) { +// SetPendingConsumerRemovalProp stores a pending consumer removal proposal. +// +// Note that the pending removal addition proposals are stored under keys with +// the following format: PendingCRPBytePrefix | stopTime | chainID +// Thus, if multiple removal addition proposal for the same chain will pass at +// the same time, then only the last one will be stored. +func (k Keeper) SetPendingConsumerRemovalProp(ctx sdk.Context, prop *types.ConsumerRemovalProposal) { store := ctx.KVStore(k.storeKey) - - for _, p := range proposals { - store.Delete(types.PendingCAPKey(p.SpawnTime, p.ChainId)) + bz, err := k.cdc.Marshal(prop) + if err != nil { + panic(fmt.Errorf("consumer removal proposal could not be marshaled: %w", err)) } + store.Set(types.PendingCRPKey(prop.StopTime, prop.ChainId), bz) } -// SetPendingConsumerRemovalProp stores a pending proposal to remove and stop a consumer chain -func (k Keeper) SetPendingConsumerRemovalProp(ctx sdk.Context, chainID string, timestamp time.Time) { - store := ctx.KVStore(k.storeKey) - store.Set(types.PendingCRPKey(timestamp, chainID), []byte{}) -} - -// GetPendingConsumerRemovalProp returns a boolean if a pending consumer removal proposal -// exists for the given consumer chain ID and timestamp -func (k Keeper) GetPendingConsumerRemovalProp(ctx sdk.Context, chainID string, timestamp time.Time) bool { +// IsPendingConsumerRemovalProp checks whether a pending consumer removal proposal +// exists for the given consumer chain ID and stopTime +// +// Note: this method is only used in testing +func (k Keeper) IsPendingConsumerRemovalProp(ctx sdk.Context, chainID string, stopTime time.Time) bool { store := ctx.KVStore(k.storeKey) - bz := store.Get(types.PendingCRPKey(timestamp, chainID)) + bz := store.Get(types.PendingCRPKey(stopTime, chainID)) return bz != nil } @@ -441,12 +428,6 @@ func (k Keeper) DeletePendingConsumerRemovalProps(ctx sdk.Context, proposals ... } } -// PendingConsumerRemovalPropIterator returns an iterator for iterating through pending consumer removal proposals -func (k Keeper) PendingConsumerRemovalPropIterator(ctx sdk.Context) sdk.Iterator { - store := ctx.KVStore(k.storeKey) - return sdk.KVStorePrefixIterator(store, []byte{types.PendingCRPBytePrefix}) -} - // BeginBlockCCR iterates over the pending consumer removal proposals // in order and stop/removes the chain if the stop time has passed, // otherwise it will break out of loop and return. Executed proposals are deleted. @@ -454,7 +435,7 @@ func (k Keeper) PendingConsumerRemovalPropIterator(ctx sdk.Context) sdk.Iterator // See: https://github.com/cosmos/ibc/blob/main/spec/app/ics-028-cross-chain-validation/methods.md#ccv-pcf-bblock-ccr1 // Spec tag: [CCV-PCF-BBLOCK-CCR.1] func (k Keeper) BeginBlockCCR(ctx sdk.Context) { - propsToExecute := k.ConsumerRemovalPropsToExecute(ctx) + propsToExecute := k.GetConsumerRemovalPropsToExecute(ctx) for _, prop := range propsToExecute { err := k.StopConsumerChain(ctx, prop.ChainId, true) @@ -466,73 +447,52 @@ func (k Keeper) BeginBlockCCR(ctx sdk.Context) { k.DeletePendingConsumerRemovalProps(ctx, propsToExecute...) } -// ConsumerRemovalPropsToExecute iterates over the pending consumer removal proposals -// and returns an ordered list of consumer removal proposals to be executed, -// ie. consumer chains to be stopped and removed from the provider chain. +// GetConsumerRemovalPropsToExecute returns the pending consumer removal proposals +// that are ready to be executed, i.e., consumer chains to be stopped and removed +// from the provider chain. // A prop is included in the returned list if its proposed stop time has passed. // // Note: this method is split out from BeginBlockCCR to be easily unit tested. -func (k Keeper) ConsumerRemovalPropsToExecute(ctx sdk.Context) []types.ConsumerRemovalProposal { - - // store the (to be) executed consumer removal proposals in order +func (k Keeper) GetConsumerRemovalPropsToExecute(ctx sdk.Context) []types.ConsumerRemovalProposal { propsToExecute := []types.ConsumerRemovalProposal{} - k.IteratePendingConsumerRemovalProps(ctx, func(stopTime time.Time, prop types.ConsumerRemovalProposal) (stop bool) { - if !ctx.BlockTime().Before(stopTime) { + k.IteratePendingConsumerRemovalProps(ctx, func(prop types.ConsumerRemovalProposal) (stop bool) { + if !ctx.BlockTime().Before(prop.StopTime) { propsToExecute = append(propsToExecute, prop) return false // do not stop the iteration } - // No more proposals to check, since they're stored/ordered by timestamp. - return true // stop + // stop iteration; proposals are ordered by stop time, + // so no additional pending props are ready to act upon + return true }) return propsToExecute } +// IteratePendingConsumerRemovalProps iterates through the pending consumer removal proposals. +// +// Note that the pending consumer removal proposals are stored under keys with the following format: +// PendingCRPBytePrefix | stopTime | chainID +// Thus, the iteration is in ascending order of stopTimes. func (k Keeper) IteratePendingConsumerRemovalProps( ctx sdk.Context, - cb func(stopTime time.Time, prop types.ConsumerRemovalProposal) (stop bool), + cb func(prop types.ConsumerRemovalProposal) (stop bool), ) { - iterator := k.PendingConsumerRemovalPropIterator(ctx) + store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, []byte{types.PendingCRPBytePrefix}) defer iterator.Close() for ; iterator.Valid(); iterator.Next() { + var prop types.ConsumerRemovalProposal + k.cdc.MustUnmarshal(iterator.Value(), &prop) - key := iterator.Key() - stopTime, chainID, err := types.ParsePendingCRPKey(key) - if err != nil { - panic(fmt.Errorf("failed to parse pending consumer removal proposal key: %w", err)) - } - - stop := cb(stopTime, types.ConsumerRemovalProposal{ChainId: chainID, StopTime: stopTime}) + stop := cb(prop) if stop { break } } } -// GetAllConsumerRemovalProps returns all consumer removal proposals separated into matured and pending. -func (k Keeper) GetAllConsumerRemovalProps(ctx sdk.Context) types.ConsumerRemovalProposals { - props := types.ConsumerRemovalProposals{} - - store := ctx.KVStore(k.storeKey) - iterator := sdk.KVStorePrefixIterator(store, []byte{types.PendingCRPBytePrefix}) - defer iterator.Close() - - for ; iterator.Valid(); iterator.Next() { - key := iterator.Key() - stopTime, chainID, err := types.ParsePendingCRPKey(key) - if err != nil { - panic(fmt.Errorf("failed to parse pending consumer removal proposal key: %w", err)) - } - - props.Pending = append(props.Pending, - &types.ConsumerRemovalProposal{ChainId: chainID, StopTime: stopTime}) - } - - return props -} - // CloseChannel closes the channel for the given channel ID on the condition // that the channel exists and isn't already in the CLOSED state func (k Keeper) CloseChannel(ctx sdk.Context, channelID string) { diff --git a/x/ccv/provider/keeper/proposal_test.go b/x/ccv/provider/keeper/proposal_test.go index 5d0d5e101f..4b9e049109 100644 --- a/x/ccv/provider/keeper/proposal_test.go +++ b/x/ccv/provider/keeper/proposal_test.go @@ -225,13 +225,14 @@ func TestPendingConsumerAdditionPropDeletion(t *testing.T) { defer ctrl.Finish() for _, tc := range testCases { - err := providerKeeper.SetPendingConsumerAdditionProp(ctx, &tc.ConsumerAdditionProposal) - require.NoError(t, err) + require.NotPanics(t, func() { + providerKeeper.SetPendingConsumerAdditionProp(ctx, &tc.ConsumerAdditionProposal) + }) } ctx = ctx.WithBlockTime(time.Now().UTC()) - propsToExecute := providerKeeper.ConsumerAdditionPropsToExecute(ctx) + propsToExecute := providerKeeper.GetConsumerAdditionPropsToExecute(ctx) // Delete consumer addition proposals, same as what would be done by BeginBlockInit providerKeeper.DeletePendingConsumerAdditionProps(ctx, propsToExecute...) numDeleted := 0 @@ -302,10 +303,11 @@ func TestPendingConsumerAdditionPropOrder(t *testing.T) { ctx = ctx.WithBlockTime(tc.accessTime) for _, prop := range tc.propSubmitOrder { - err := providerKeeper.SetPendingConsumerAdditionProp(ctx, &prop) - require.NoError(t, err) + require.NotPanics(t, func() { + providerKeeper.SetPendingConsumerAdditionProp(ctx, &prop) + }) } - propsToExecute := providerKeeper.ConsumerAdditionPropsToExecute(ctx) + propsToExecute := providerKeeper.GetConsumerAdditionPropsToExecute(ctx) require.Equal(t, tc.expectedOrderedProps, propsToExecute) } } @@ -379,13 +381,13 @@ func TestHandleConsumerRemovalProposal(t *testing.T) { if tc.expStop { // Expect no pending proposal to exist - found := providerKeeper.GetPendingConsumerRemovalProp(ctx, tc.prop.ChainId, tc.prop.StopTime) + found := providerKeeper.IsPendingConsumerRemovalProp(ctx, tc.prop.ChainId, tc.prop.StopTime) require.False(t, found) testProviderStateIsCleaned(t, ctx, providerKeeper, tc.prop.ChainId, "channelID") } else { // Proposal should be stored as pending - found := providerKeeper.GetPendingConsumerRemovalProp(ctx, tc.prop.ChainId, tc.prop.StopTime) + found := providerKeeper.IsPendingConsumerRemovalProp(ctx, tc.prop.ChainId, tc.prop.StopTime) require.True(t, found) } @@ -502,9 +504,8 @@ func testProviderStateIsCleaned(t *testing.T, ctx sdk.Context, providerKeeper pr }) require.False(t, found) found = false - providerKeeper.IterateConsumerAddrsToPrune(ctx, expectedChainID, func(_ uint64, _ types.AddressList) (stop bool) { + providerKeeper.IterateConsumerAddrsToPrune(ctx, expectedChainID, func(_ uint64, _ types.AddressList) { found = true - return true // stop the iteration }) require.False(t, found) } @@ -530,17 +531,19 @@ func TestPendingConsumerRemovalPropDeletion(t *testing.T) { defer ctrl.Finish() for _, tc := range testCases { - providerKeeper.SetPendingConsumerRemovalProp(ctx, tc.ChainId, tc.StopTime) + require.NotPanics(t, func() { + providerKeeper.SetPendingConsumerRemovalProp(ctx, &tc.ConsumerRemovalProposal) + }) } ctx = ctx.WithBlockTime(time.Now().UTC()) - propsToExecute := providerKeeper.ConsumerRemovalPropsToExecute(ctx) + propsToExecute := providerKeeper.GetConsumerRemovalPropsToExecute(ctx) // Delete consumer removal proposals, same as what would be done by BeginBlockCCR providerKeeper.DeletePendingConsumerRemovalProps(ctx, propsToExecute...) numDeleted := 0 for _, tc := range testCases { - res := providerKeeper.GetPendingConsumerRemovalProp(ctx, tc.ChainId, tc.StopTime) + res := providerKeeper.IsPendingConsumerRemovalProp(ctx, tc.ChainId, tc.StopTime) if !tc.ExpDeleted { require.NotEmpty(t, res, "consumer removal prop was deleted: %s %s", tc.ChainId, tc.StopTime.String()) continue @@ -603,9 +606,11 @@ func TestPendingConsumerRemovalPropOrder(t *testing.T) { ctx = ctx.WithBlockTime(tc.accessTime) for _, prop := range tc.propSubmitOrder { - providerKeeper.SetPendingConsumerRemovalProp(ctx, prop.ChainId, prop.StopTime) + require.NotPanics(t, func() { + providerKeeper.SetPendingConsumerRemovalProp(ctx, &prop) + }) } - propsToExecute := providerKeeper.ConsumerRemovalPropsToExecute(ctx) + propsToExecute := providerKeeper.GetConsumerRemovalPropsToExecute(ctx) require.Equal(t, tc.expectedOrderedProps, propsToExecute) } } @@ -763,8 +768,9 @@ func TestBeginBlockInit(t *testing.T) { ) for _, prop := range pendingProps { - err := providerKeeper.SetPendingConsumerAdditionProp(ctx, prop) - require.NoError(t, err) + require.NotPanics(t, func() { + providerKeeper.SetPendingConsumerAdditionProp(ctx, prop) + }) } providerKeeper.BeginBlockInit(ctx) @@ -836,7 +842,9 @@ func TestBeginBlockCCR(t *testing.T) { require.NoError(t, err) // Set removal props for all consumer chains - providerKeeper.SetPendingConsumerRemovalProp(ctx, prop.ChainId, prop.StopTime) + require.NotPanics(t, func() { + providerKeeper.SetPendingConsumerRemovalProp(ctx, prop) + }) } // @@ -845,26 +853,26 @@ func TestBeginBlockCCR(t *testing.T) { providerKeeper.BeginBlockCCR(ctx) // Only the 3rd (final) proposal is still stored as pending - found := providerKeeper.GetPendingConsumerRemovalProp( + found := providerKeeper.IsPendingConsumerRemovalProp( ctx, pendingProps[0].ChainId, pendingProps[0].StopTime) require.False(t, found) - found = providerKeeper.GetPendingConsumerRemovalProp( + found = providerKeeper.IsPendingConsumerRemovalProp( ctx, pendingProps[1].ChainId, pendingProps[1].StopTime) require.False(t, found) - found = providerKeeper.GetPendingConsumerRemovalProp( + found = providerKeeper.IsPendingConsumerRemovalProp( ctx, pendingProps[2].ChainId, pendingProps[2].StopTime) require.True(t, found) } -// Test getting both matured and pending comnsumer addition proposals -func TestGetAllConsumerAdditionProps(t *testing.T) { +// Test iterating through all consumer addition proposals +func TestIteratePendingConsumerAdditionProps(t *testing.T) { now := time.Now().UTC() props := []types.ConsumerAdditionProposal{ - {ChainId: "1", SpawnTime: now.Add(1 * time.Hour)}, - {ChainId: "2", SpawnTime: now.Add(2 * time.Hour)}, {ChainId: "3", SpawnTime: now.Add(3 * time.Hour)}, + {ChainId: "1", SpawnTime: now.Add(1 * time.Hour)}, {ChainId: "4", SpawnTime: now.Add(4 * time.Hour)}, + {ChainId: "2", SpawnTime: now.Add(2 * time.Hour)}, } keeperParams := testkeeper.NewInMemKeeperParams(t) @@ -872,28 +880,37 @@ func TestGetAllConsumerAdditionProps(t *testing.T) { defer ctrl.Finish() for _, prop := range props { - cpProp := prop // bring into loop scope - avoids using iterator pointer instead of value pointer - err := providerKeeper.SetPendingConsumerAdditionProp(ctx, &cpProp) - require.NoError(t, err) + require.NotPanics(t, func() { + cpProp := prop // bring into loop scope - avoids using iterator pointer instead of value pointer + providerKeeper.SetPendingConsumerAdditionProp(ctx, &cpProp) + }) } // advance the clock to be 1 minute after first proposal ctx = ctx.WithBlockTime(now.Add(time.Minute)) - res := providerKeeper.GetAllConsumerAdditionProps(ctx) - require.NotEmpty(t, res, "GetAllConsumerAdditionProps returned empty result") - require.Len(t, res.Pending, 4, "wrong len for pending addition props") - require.Equal(t, props[0].ChainId, res.Pending[0].ChainId, "wrong chain ID for pending addition prop") + storedProps := types.ConsumerAdditionProposals{} + providerKeeper.IteratePendingConsumerAdditionProps(ctx, func(prop types.ConsumerAdditionProposal) (stop bool) { + storedProps.Pending = append(storedProps.Pending, &prop) + return false // do not stop the iteration + }) + require.NotEmpty(t, storedProps) + require.Len(t, storedProps.Pending, 4, "wrong len for pending addition props") + // check that the order is correct + require.Equal(t, props[1].ChainId, storedProps.Pending[0].ChainId) + require.Equal(t, props[3].ChainId, storedProps.Pending[1].ChainId) + require.Equal(t, props[0].ChainId, storedProps.Pending[2].ChainId) + require.Equal(t, props[2].ChainId, storedProps.Pending[3].ChainId) } -// Test getting both matured and pending consumer removal proposals -func TestGetAllConsumerRemovalProps(t *testing.T) { +// Test iterating through all consumer removal proposals +func TestIteratePendingConsumerRemovalProps(t *testing.T) { now := time.Now().UTC() props := []types.ConsumerRemovalProposal{ - {ChainId: "1", StopTime: now.Add(1 * time.Hour)}, - {ChainId: "2", StopTime: now.Add(2 * time.Hour)}, {ChainId: "3", StopTime: now.Add(3 * time.Hour)}, + {ChainId: "1", StopTime: now.Add(1 * time.Hour)}, {ChainId: "4", StopTime: now.Add(4 * time.Hour)}, + {ChainId: "2", StopTime: now.Add(2 * time.Hour)}, } keeperParams := testkeeper.NewInMemKeeperParams(t) @@ -901,13 +918,23 @@ func TestGetAllConsumerRemovalProps(t *testing.T) { defer ctrl.Finish() for _, prop := range props { - providerKeeper.SetPendingConsumerRemovalProp(ctx, prop.ChainId, prop.StopTime) + require.NotPanics(t, func() { + providerKeeper.SetPendingConsumerRemovalProp(ctx, &prop) + }) } // advance the clock to be 1 minute after first proposal ctx = ctx.WithBlockTime(now.Add(time.Minute)) - res := providerKeeper.GetAllConsumerRemovalProps(ctx) - require.NotEmpty(t, res, "GetAllConsumerRemovalProps returned empty result") - require.Len(t, res.Pending, 4, "wrong len for pending removal props") - require.Equal(t, props[0].ChainId, res.Pending[0].ChainId, "wrong chain ID for pending removal prop") + storedProps := types.ConsumerRemovalProposals{} + providerKeeper.IteratePendingConsumerRemovalProps(ctx, func(prop types.ConsumerRemovalProposal) (stop bool) { + storedProps.Pending = append(storedProps.Pending, &prop) + return false // do not stop the iteration + }) + require.NotEmpty(t, storedProps) + require.Len(t, storedProps.Pending, 4, "wrong len for pending removal props") + // check that the order is correct + require.Equal(t, props[1].ChainId, storedProps.Pending[0].ChainId) + require.Equal(t, props[3].ChainId, storedProps.Pending[1].ChainId) + require.Equal(t, props[0].ChainId, storedProps.Pending[2].ChainId) + require.Equal(t, props[2].ChainId, storedProps.Pending[3].ChainId) } diff --git a/x/ccv/provider/types/keys.go b/x/ccv/provider/types/keys.go index e011b09372..2d9a726736 100644 --- a/x/ccv/provider/types/keys.go +++ b/x/ccv/provider/types/keys.go @@ -66,14 +66,14 @@ const ( PendingCRPBytePrefix // UnbondingOpBytePrefix is the byte prefix that stores a record of all the ids of consumer chains that - // need to unbond before a given delegation can unbond on this chain. + // need to unbond before a given unbonding operation can unbond on this chain. UnbondingOpBytePrefix // UnbondingOpIndexBytePrefix is byte prefix of the index for looking up which unbonding - // delegation entries are waiting for a given consumer chain to unbond + // operations are waiting for a given consumer chain to unbond UnbondingOpIndexBytePrefix - // ValsetUpdateBlockHeightBytePrefix is the byte prefix that will store the mapping from valset update ID to block height + // ValsetUpdateBlockHeightBytePrefix is the byte prefix that will store the mapping from vscIDs to block heights ValsetUpdateBlockHeightBytePrefix // ConsumerGenesisBytePrefix stores consumer genesis state material (consensus state and client state) indexed by consumer chain id @@ -143,30 +143,36 @@ func InitTimeoutTimestampKey(chainID string) []byte { return append([]byte{InitTimeoutTimestampBytePrefix}, []byte(chainID)...) } -// PendingCAPKey returns the key under which a pending consumer addition proposal is stored +// PendingCAPKey returns the key under which a pending consumer addition proposal is stored. +// The key has the following format: PendingCAPBytePrefix | timestamp | chainID func PendingCAPKey(timestamp time.Time, chainID string) []byte { - return TsAndChainIdKey(PendingCAPBytePrefix, timestamp, chainID) -} - -// ParsePendingCAPKey returns the time and chain ID for a pending consumer addition proposal key -// or an error if unparsable -func ParsePendingCAPKey(bz []byte) (time.Time, string, error) { - return ParseTsAndChainIdKey(PendingCAPBytePrefix, bz) + timeBz := sdk.FormatTimeBytes(timestamp) + return AppendMany( + // Append the prefix + []byte{PendingCAPBytePrefix}, + // Append the time bytes + timeBz, + // Append the chainId + []byte(chainID), + ) } -// PendingCRPKey returns the key under which pending consumer removal proposals are stored +// PendingCRPKey returns the key under which pending consumer removal proposals are stored. +// The key has the following format: PendingCRPBytePrefix | timestamp | chainID func PendingCRPKey(timestamp time.Time, chainID string) []byte { - return TsAndChainIdKey(PendingCRPBytePrefix, timestamp, chainID) -} - -// ParsePendingCRPKey returns the time and chain ID for a pending consumer removal proposal key or an error if unparseable -func ParsePendingCRPKey(bz []byte) (time.Time, string, error) { - return ParseTsAndChainIdKey(PendingCRPBytePrefix, bz) + timeBz := sdk.FormatTimeBytes(timestamp) + return AppendMany( + // Append the prefix + []byte{PendingCRPBytePrefix}, + // Append the time bytes + timeBz, + // Append the chainId + []byte(chainID), + ) } -// UnbondingOpIndexKey returns an unbonding op index key -// Note: chainId is hashed to a fixed length sequence of bytes here to prevent -// injection attack between chainIDs. +// UnbondingOpIndexKey returns the key under which the index for looking up which +// unbonding operations are waiting for a given consumer chain to unbond is stored func UnbondingOpIndexKey(chainID string, vscID uint64) []byte { return ChainIdAndVscIdKey(UnbondingOpIndexBytePrefix, chainID, vscID) } @@ -178,7 +184,7 @@ func ParseUnbondingOpIndexKey(key []byte) (string, uint64, error) { } // UnbondingOpKey returns the key that stores a record of all the ids of consumer chains that -// need to unbond before a given delegation can unbond on this chain +// need to unbond before a given unbonding operation can complete func UnbondingOpKey(id uint64) []byte { bz := make([]byte, 8) binary.BigEndian.PutUint64(bz, id) From e219e411d67b31fe93039871dc804df2afd2ff44 Mon Sep 17 00:00:00 2001 From: mpoke Date: Wed, 14 Dec 2022 12:33:28 +0100 Subject: [PATCH 2/6] bringing changes from #583 provider done --- x/ccv/provider/keeper/keeper.go | 129 +++++++++++++++++++++----------- 1 file changed, 84 insertions(+), 45 deletions(-) diff --git a/x/ccv/provider/keeper/keeper.go b/x/ccv/provider/keeper/keeper.go index 96853a940b..8eaf8ff324 100644 --- a/x/ccv/provider/keeper/keeper.go +++ b/x/ccv/provider/keeper/keeper.go @@ -133,9 +133,12 @@ func (k Keeper) DeleteChainToChannel(ctx sdk.Context, chainID string) { store.Delete(types.ChainToChannelKey(chainID)) } -// IterateConsumerChains iterates over all of the consumer chains that the provider module controls -// It calls the provided callback function which takes in a chainID and client ID to return -// a stop boolean which will stop the iteration. +// IterateConsumerChains iterates over the IDs of all clients to consumer chains. +// Consumer chains with created clients are also referred to as registered. +// +// Note that the registered consumer chains are stored under keys with the following format: +// ChainToClientBytePrefix | chainID +// Thus, the iteration is in ascending order of chainIDs. func (k Keeper) IterateConsumerChains(ctx sdk.Context, cb func(ctx sdk.Context, chainID, clientID string) (stop bool)) { store := ctx.KVStore(k.storeKey) iterator := sdk.KVStorePrefixIterator(store, []byte{types.ChainToClientBytePrefix}) @@ -175,8 +178,11 @@ func (k Keeper) DeleteChannelToChain(ctx sdk.Context, channelID string) { store.Delete(types.ChannelToChainKey(channelID)) } -// IterateChannelToChain iterates over the channel to chain mappings and calls the provided callback until the iteration ends -// or the callback returns stop=true +// IterateChannelToChain iterates over the channel to chain mappings. +// +// Note that mapping from CCV channel IDs to consumer chainIDs is stored under keys with the following format: +// ChannelToChainBytePrefix | channelID +// Thus, the iteration is in ascending order of channelIDs. func (k Keeper) IterateChannelToChain(ctx sdk.Context, cb func(ctx sdk.Context, channelID, chainID string) (stop bool)) { store := ctx.KVStore(k.storeKey) iterator := sdk.KVStorePrefixIterator(store, []byte{types.ChannelToChainBytePrefix}) @@ -300,7 +306,7 @@ func (k Keeper) SetConsumerChain(ctx sdk.Context, channelID string) error { return nil } -// Save UnbondingOp by unique ID +// SetUnbondingOp sets the UnbondingOp by its unique ID func (k Keeper) SetUnbondingOp(ctx sdk.Context, unbondingOp ccv.UnbondingOp) { store := ctx.KVStore(k.storeKey) bz, err := unbondingOp.Marshal() @@ -310,7 +316,7 @@ func (k Keeper) SetUnbondingOp(ctx sdk.Context, unbondingOp ccv.UnbondingOp) { store.Set(types.UnbondingOpKey(unbondingOp.Id), bz) } -// Get UnbondingOp by unique ID +// GetUnbondingOp gets a UnbondingOp by its unique ID func (k Keeper) GetUnbondingOp(ctx sdk.Context, id uint64) (ccv.UnbondingOp, bool) { store := ctx.KVStore(k.storeKey) bz := store.Get(types.UnbondingOpKey(id)) @@ -321,11 +327,17 @@ func (k Keeper) GetUnbondingOp(ctx sdk.Context, id uint64) (ccv.UnbondingOp, boo return types.MustUnmarshalUnbondingOp(k.cdc, bz), true } +// DeleteUnbondingOp deletes a UnbondingOp given its ID func (k Keeper) DeleteUnbondingOp(ctx sdk.Context, id uint64) { store := ctx.KVStore(k.storeKey) store.Delete(types.UnbondingOpKey(id)) } +// IterateUnbondingOps iterates over UnbondingOps. +// +// Note that UnbondingOps are stored under keys with the following format: +// UnbondingOpBytePrefix | ID +// Thus, the iteration is in ascending order of IDs. func (k Keeper) IterateUnbondingOps(ctx sdk.Context, cb func(id uint64, ubdOp ccv.UnbondingOp) (stop bool)) { store := ctx.KVStore(k.storeKey) iterator := sdk.KVStorePrefixIterator(store, []byte{types.UnbondingOpBytePrefix}) @@ -346,8 +358,10 @@ func (k Keeper) IterateUnbondingOps(ctx sdk.Context, cb func(id uint64, ubdOp cc } } -// This index allows retreiving UnbondingDelegationEntries by chainID and valsetUpdateID -func (k Keeper) SetUnbondingOpIndex(ctx sdk.Context, chainID string, valsetUpdateID uint64, IDs []uint64) { +// SetUnbondingOpIndex sets an unbonding index, +// i.e., the IDs of unbonding operations that are waiting for +// a VSCMaturedPacket with vscID from a consumer with chainID +func (k Keeper) SetUnbondingOpIndex(ctx sdk.Context, chainID string, vscID uint64, IDs []uint64) { store := ctx.KVStore(k.storeKey) index := ccv.UnbondingOpsIndex{ @@ -358,10 +372,14 @@ func (k Keeper) SetUnbondingOpIndex(ctx sdk.Context, chainID string, valsetUpdat panic("Failed to marshal UnbondingOpsIndex") } - store.Set(types.UnbondingOpIndexKey(chainID, valsetUpdateID), bz) + store.Set(types.UnbondingOpIndexKey(chainID, vscID), bz) } -// IterateUnbondingOpIndex iterates over the unbonding indexes for a given chain id. +// IterateUnbondingOpIndex iterates over the unbonding indexes for a given chainID. +// +// Note that the unbonding indexes for a given chainID are stored under keys with the following format: +// UnbondingOpIndexBytePrefix | len(chainID) | chainID | vscID +// Thus, the iteration is in ascending order of vscIDs. func (k Keeper) IterateUnbondingOpIndex( ctx sdk.Context, chainID string, @@ -390,11 +408,13 @@ func (k Keeper) IterateUnbondingOpIndex( } } -// This index allows retrieving UnbondingDelegationEntries by chainID and valsetUpdateID -func (k Keeper) GetUnbondingOpIndex(ctx sdk.Context, chainID string, valsetUpdateID uint64) ([]uint64, bool) { +// GetUnbondingOpIndex gets an unbonding index, +// i.e., the IDs of unbonding operations that are waiting for +// a VSCMaturedPacket with vscID from a consumer with chainID +func (k Keeper) GetUnbondingOpIndex(ctx sdk.Context, chainID string, vscID uint64) ([]uint64, bool) { store := ctx.KVStore(k.storeKey) - bz := store.Get(types.UnbondingOpIndexKey(chainID, valsetUpdateID)) + bz := store.Get(types.UnbondingOpIndexKey(chainID, vscID)) if bz == nil { return []uint64{}, false } @@ -407,22 +427,27 @@ func (k Keeper) GetUnbondingOpIndex(ctx sdk.Context, chainID string, valsetUpdat return idx.GetIds(), true } -// This index allows retreiving UnbondingDelegationEntries by chainID and valsetUpdateID -func (k Keeper) DeleteUnbondingOpIndex(ctx sdk.Context, chainID string, valsetUpdateID uint64) { +// DeleteUnbondingOpIndex deletes an unbonding index, +// i.e., the IDs of unbonding operations that are waiting for +// a VSCMaturedPacket with vscID from a consumer with chainID +func (k Keeper) DeleteUnbondingOpIndex(ctx sdk.Context, chainID string, vscID uint64) { store := ctx.KVStore(k.storeKey) - store.Delete(types.UnbondingOpIndexKey(chainID, valsetUpdateID)) + store.Delete(types.UnbondingOpIndexKey(chainID, vscID)) } -// Retrieve UnbondingDelegationEntries by chainID and valsetUpdateID -func (k Keeper) GetUnbondingOpsFromIndex(ctx sdk.Context, chainID string, valsetUpdateID uint64) (entries []ccv.UnbondingOp, found bool) { - ids, found := k.GetUnbondingOpIndex(ctx, chainID, valsetUpdateID) +// GetUnbondingOpsFromIndex gets the unbonding ops waiting for a given chainID and vscID +func (k Keeper) GetUnbondingOpsFromIndex( + ctx sdk.Context, + chainID string, + vscID uint64, +) (entries []ccv.UnbondingOp, found bool) { + ids, found := k.GetUnbondingOpIndex(ctx, chainID, vscID) if !found { return entries, false } for _, id := range ids { entry, found := k.GetUnbondingOp(ctx, id) if !found { - // TODO JEHAN: is this the correct way to deal with this? panic("did not find UnbondingOp according to index- index was probably not correctly updated") } entries = append(entries, entry) @@ -517,70 +542,73 @@ func (k Keeper) IncrementValidatorSetUpdateId(ctx sdk.Context) { k.SetValidatorSetUpdateId(ctx, validatorSetUpdateId+1) } -func (k Keeper) SetValidatorSetUpdateId(ctx sdk.Context, valUpdateID uint64) { +func (k Keeper) SetValidatorSetUpdateId(ctx sdk.Context, vscID uint64) { store := ctx.KVStore(k.storeKey) // Convert back into bytes for storage bz := make([]byte, 8) - binary.BigEndian.PutUint64(bz, valUpdateID) + binary.BigEndian.PutUint64(bz, vscID) store.Set(types.ValidatorSetUpdateIdKey(), bz) } -func (k Keeper) GetValidatorSetUpdateId(ctx sdk.Context) (validatorSetUpdateId uint64) { +func (k Keeper) GetValidatorSetUpdateId(ctx sdk.Context) (vscID uint64) { store := ctx.KVStore(k.storeKey) bz := store.Get(types.ValidatorSetUpdateIdKey()) if bz == nil { - validatorSetUpdateId = 0 + vscID = 0 } else { // Unmarshal - validatorSetUpdateId = binary.BigEndian.Uint64(bz) + vscID = binary.BigEndian.Uint64(bz) } - return validatorSetUpdateId + return vscID } -// SetValsetUpdateBlockHeight sets the block height for a given valset update id -func (k Keeper) SetValsetUpdateBlockHeight(ctx sdk.Context, valsetUpdateId, blockHeight uint64) { +// SetValsetUpdateBlockHeight sets the block height for a given vscID +func (k Keeper) SetValsetUpdateBlockHeight(ctx sdk.Context, vscID, blockHeight uint64) { store := ctx.KVStore(k.storeKey) heightBytes := make([]byte, 8) binary.BigEndian.PutUint64(heightBytes, blockHeight) - store.Set(types.ValsetUpdateBlockHeightKey(valsetUpdateId), heightBytes) + store.Set(types.ValsetUpdateBlockHeightKey(vscID), heightBytes) } -// GetValsetUpdateBlockHeight gets the block height for a given valset update id -func (k Keeper) GetValsetUpdateBlockHeight(ctx sdk.Context, valsetUpdateId uint64) (uint64, bool) { +// GetValsetUpdateBlockHeight gets the block height for a given vscID +func (k Keeper) GetValsetUpdateBlockHeight(ctx sdk.Context, vscID uint64) (uint64, bool) { store := ctx.KVStore(k.storeKey) - bz := store.Get(types.ValsetUpdateBlockHeightKey(valsetUpdateId)) + bz := store.Get(types.ValsetUpdateBlockHeightKey(vscID)) if bz == nil { return 0, false } return binary.BigEndian.Uint64(bz), true } -// IterateSlashAcks iterates through the slash acks set in the store -func (k Keeper) IterateValsetUpdateBlockHeight(ctx sdk.Context, cb func(valsetUpdateId, height uint64) (stop bool)) { +// IterateValsetUpdateBlockHeight iterates through the mapping from vscIDs to block heights. +// +// Note that the mapping from vscIDs to block heights is stored under keys with the following format: +// ValsetUpdateBlockHeightBytePrefix | vscID +// Thus, the iteration is in ascending order of vscIDs. +func (k Keeper) IterateValsetUpdateBlockHeight(ctx sdk.Context, cb func(vscID, height uint64) (stop bool)) { store := ctx.KVStore(k.storeKey) iterator := sdk.KVStorePrefixIterator(store, []byte{types.ValsetUpdateBlockHeightBytePrefix}) defer iterator.Close() for ; iterator.Valid(); iterator.Next() { - - valsetUpdateId := binary.BigEndian.Uint64(iterator.Key()[1:]) + vscID := binary.BigEndian.Uint64(iterator.Key()[1:]) height := binary.BigEndian.Uint64(iterator.Value()) - stop := cb(valsetUpdateId, height) + stop := cb(vscID, height) if stop { return } } } -// DeleteValsetUpdateBlockHeight deletes the block height value for a given vaset update id -func (k Keeper) DeleteValsetUpdateBlockHeight(ctx sdk.Context, valsetUpdateId uint64) { +// DeleteValsetUpdateBlockHeight deletes the block height value for a given vscID +func (k Keeper) DeleteValsetUpdateBlockHeight(ctx sdk.Context, vscID uint64) { store := ctx.KVStore(k.storeKey) - store.Delete(types.ValsetUpdateBlockHeightKey(valsetUpdateId)) + store.Delete(types.ValsetUpdateBlockHeightKey(vscID)) } // SetSlashAcks sets the slash acks under the given chain ID @@ -624,7 +652,12 @@ func (k Keeper) ConsumeSlashAcks(ctx sdk.Context, chainID string) (acks []string } // IterateSlashAcks iterates through the slash acks set in the store. -// Note: this method is only used in testing +// +// Note that the slash acks are stored under keys with the following format: +// SlashAcksBytePrefix | chainID +// Thus, the iteration is in ascending order of chainIDs. +// +// Note: This method is only used in testing func (k Keeper) IterateSlashAcks(ctx sdk.Context, cb func(chainID string, acks []string) (stop bool)) { store := ctx.KVStore(k.storeKey) iterator := sdk.KVStorePrefixIterator(store, []byte{types.SlashAcksBytePrefix}) @@ -763,7 +796,10 @@ func (k Keeper) DeleteInitTimeoutTimestamp(ctx sdk.Context, chainID string) { } // IterateInitTimeoutTimestamp iterates through the init timeout timestamps in the store. -// Note: as the keys have the `bytePrefix | chainID` format, the iteration is NOT done in timestamp order. +// +// Note that the init timeout timestamps are stored under keys with the following format: +// InitTimeoutTimestampBytePrefix | chainID +// Thus, the iteration is in ascending order of chainIDs (NOT in timestamp order). func (k Keeper) IterateInitTimeoutTimestamp(ctx sdk.Context, cb func(chainID string, ts uint64) (stop bool)) { store := ctx.KVStore(k.storeKey) iterator := sdk.KVStorePrefixIterator(store, []byte{types.InitTimeoutTimestampBytePrefix}) @@ -821,8 +857,11 @@ func (k Keeper) DeleteVscSendTimestamp(ctx sdk.Context, chainID string, vscID ui store.Delete(types.VscSendingTimestampKey(chainID, vscID)) } -// IterateVscSendTimestamps iterates in order (lowest first) -// over the vsc send timestamps of the given chainID. +// IterateVscSendTimestamps iterates over the vsc send timestamps of the given chainID. +// +// Note that the vsc send timestamps of a given chainID are stored under keys with the following format: +// VscSendTimestampBytePrefix | len(chainID) | chainID | vscID +// Thus, the iteration is in ascending order of vscIDs, and as a result in send timestamp order. func (k Keeper) IterateVscSendTimestamps( ctx sdk.Context, chainID string, From 2f1d376cb575f436fe81a25ff39fdb009058d1ea Mon Sep 17 00:00:00 2001 From: mpoke Date: Wed, 14 Dec 2022 13:14:34 +0100 Subject: [PATCH 3/6] bringing changes from #583 consumer done --- x/ccv/consumer/keeper/genesis.go | 21 +-------------------- x/ccv/consumer/keeper/keeper.go | 30 +++++++++++++++++++++--------- 2 files changed, 22 insertions(+), 29 deletions(-) diff --git a/x/ccv/consumer/keeper/genesis.go b/x/ccv/consumer/keeper/genesis.go index 088ce6adc4..eb25856bce 100644 --- a/x/ccv/consumer/keeper/genesis.go +++ b/x/ccv/consumer/keeper/genesis.go @@ -128,25 +128,6 @@ func (k Keeper) ExportGenesis(ctx sdk.Context) (genesis *consumertypes.GenesisSt return false // do not stop the iteration }) - heightToVCIDs := []consumertypes.HeightToValsetUpdateID{} - k.IterateHeightToValsetUpdateID(ctx, func(height, vscID uint64) (stop bool) { - hv := consumertypes.HeightToValsetUpdateID{ - Height: height, - ValsetUpdateId: vscID, - } - heightToVCIDs = append(heightToVCIDs, hv) - return false // do not stop the iteration - }) - - outstandingDowntimes := []consumertypes.OutstandingDowntime{} - k.IterateOutstandingDowntime(ctx, func(addr string) (stop bool) { - od := consumertypes.OutstandingDowntime{ - ValidatorConsensusAddress: addr, - } - outstandingDowntimes = append(outstandingDowntimes, od) - return false // do not stop the iteration - }) - // TODO: update GetLastTransmissionBlockHeight to not return an error ltbh, err := k.GetLastTransmissionBlockHeight(ctx) if err != nil { @@ -160,7 +141,7 @@ func (k Keeper) ExportGenesis(ctx sdk.Context) (genesis *consumertypes.GenesisSt valset, k.GetHeightToValsetUpdateIDs(ctx), k.GetPendingPackets(ctx), - outstandingDowntimes, + k.GetOutstandingDowntimes(ctx), *ltbh, params, ) diff --git a/x/ccv/consumer/keeper/keeper.go b/x/ccv/consumer/keeper/keeper.go index e04b00a965..e878bf5b60 100644 --- a/x/ccv/consumer/keeper/keeper.go +++ b/x/ccv/consumer/keeper/keeper.go @@ -275,15 +275,15 @@ func (k Keeper) VerifyProviderChain(ctx sdk.Context, connectionHops []string) er return nil } -// SetHeightValsetUpdateID sets the valset update id for a given block height -func (k Keeper) SetHeightValsetUpdateID(ctx sdk.Context, height, valsetUpdateId uint64) { +// SetHeightValsetUpdateID sets the vscID for a given block height +func (k Keeper) SetHeightValsetUpdateID(ctx sdk.Context, height, vscID uint64) { store := ctx.KVStore(k.storeKey) valBytes := make([]byte, 8) - binary.BigEndian.PutUint64(valBytes, valsetUpdateId) + binary.BigEndian.PutUint64(valBytes, vscID) store.Set(types.HeightValsetUpdateIDKey(height), valBytes) } -// GetHeightValsetUpdateID gets the valset update id recorded for a given block height +// GetHeightValsetUpdateID gets the vscID recorded for a given block height func (k Keeper) GetHeightValsetUpdateID(ctx sdk.Context, height uint64) uint64 { store := ctx.KVStore(k.storeKey) bz := store.Get(types.HeightValsetUpdateIDKey(height)) @@ -293,13 +293,17 @@ func (k Keeper) GetHeightValsetUpdateID(ctx sdk.Context, height uint64) uint64 { return binary.BigEndian.Uint64(bz) } -// DeleteHeightValsetUpdateID deletes the valset update id for a given block height +// DeleteHeightValsetUpdateID deletes the vscID for a given block height func (k Keeper) DeleteHeightValsetUpdateID(ctx sdk.Context, height uint64) { store := ctx.KVStore(k.storeKey) store.Delete(types.HeightValsetUpdateIDKey(height)) } -// IterateHeightToValsetUpdateID iterates over the block height to valset update ID mapping in store +// IterateHeightToValsetUpdateID iterates over the block height to vscID mapping in store. +// +// Note that the block height to vscID mapping is stored under keys with the following format: +// HeightValsetUpdateIDBytePrefix | height +// Thus, the iteration is in ascending order of heights. func (k Keeper) IterateHeightToValsetUpdateID(ctx sdk.Context, cb func(height, vscID uint64) (stop bool)) { store := ctx.KVStore(k.storeKey) iterator := sdk.KVStorePrefixIterator(store, []byte{types.HeightValsetUpdateIDBytePrefix}) @@ -341,7 +345,11 @@ func (k Keeper) DeleteOutstandingDowntime(ctx sdk.Context, consAddress string) { store.Delete(types.OutstandingDowntimeKey(consAddr)) } -// IterateOutstandingDowntime iterates over the validator addresses of outstanding downtime flags +// IterateOutstandingDowntime iterates over the outstanding downtime flags. +// +// Note that the outstanding downtime flags are stored under keys with the following format: +// OutstandingDowntimeBytePrefix | consAddress +// Thus, the iteration is in ascending order of consAddresses. func (k Keeper) IterateOutstandingDowntime(ctx sdk.Context, cb func(address string) (stop bool)) { store := ctx.KVStore(k.storeKey) iterator := sdk.KVStorePrefixIterator(store, []byte{types.OutstandingDowntimeBytePrefix}) @@ -384,7 +392,11 @@ func (k Keeper) DeleteCCValidator(ctx sdk.Context, addr []byte) { store.Delete(types.CrossChainValidatorKey(addr)) } -// GetAllCCValidator returns all cross-chain validators +// GetAllCCValidator returns all cross-chain validators. +// +// Note that the cross-chain validators are stored under keys with the following format: +// CrossChainValidatorBytePrefix | address +// Thus, the iteration is in ascending order of addresses. func (k Keeper) GetAllCCValidator(ctx sdk.Context) (validators []types.CrossChainValidator) { store := ctx.KVStore(k.storeKey) iterator := sdk.KVStorePrefixIterator(store, []byte{types.CrossChainValidatorBytePrefix}) @@ -463,7 +475,7 @@ func (k Keeper) GetOutstandingDowntimes(ctx sdk.Context) []consumertypes.Outstan ValidatorConsensusAddress: addr, } outstandingDowntimes = append(outstandingDowntimes, od) - return false + return false // do not stop iteration }) return outstandingDowntimes } From 22ee82fb5f8b0717a82ccbd11c15328947793c0c Mon Sep 17 00:00:00 2001 From: mpoke Date: Thu, 15 Dec 2022 16:12:07 +0100 Subject: [PATCH 4/6] IsPendingConsumerRemovalProp -> PendingConsumerRemovalPropExists --- x/ccv/provider/keeper/genesis_test.go | 2 +- x/ccv/provider/keeper/proposal.go | 4 ++-- x/ccv/provider/keeper/proposal_test.go | 12 ++++++------ 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/x/ccv/provider/keeper/genesis_test.go b/x/ccv/provider/keeper/genesis_test.go index fcc3f207e3..b438874ac5 100644 --- a/x/ccv/provider/keeper/genesis_test.go +++ b/x/ccv/provider/keeper/genesis_test.go @@ -129,7 +129,7 @@ func TestInitAndExportGenesis(t *testing.T) { addProp, found := pk.GetPendingConsumerAdditionProp(ctx, oneHourFromNow, cChainIDs[0]) require.True(t, found) require.Equal(t, provGenesis.ConsumerAdditionProposals[0], addProp) - require.True(t, pk.IsPendingConsumerRemovalProp(ctx, cChainIDs[0], oneHourFromNow)) + require.True(t, pk.PendingConsumerRemovalPropExists(ctx, cChainIDs[0], oneHourFromNow)) require.Equal(t, provGenesis.Params, pk.GetParams(ctx)) gotConsTmPubKey, found := pk.GetValidatorConsumerPubKey(ctx, cChainIDs[0], provAddr) diff --git a/x/ccv/provider/keeper/proposal.go b/x/ccv/provider/keeper/proposal.go index b158c1f1ec..8d4fa8ca28 100644 --- a/x/ccv/provider/keeper/proposal.go +++ b/x/ccv/provider/keeper/proposal.go @@ -407,11 +407,11 @@ func (k Keeper) SetPendingConsumerRemovalProp(ctx sdk.Context, prop *types.Consu store.Set(types.PendingCRPKey(prop.StopTime, prop.ChainId), bz) } -// IsPendingConsumerRemovalProp checks whether a pending consumer removal proposal +// PendingConsumerRemovalPropExists checks whether a pending consumer removal proposal // exists for the given consumer chain ID and stopTime // // Note: this method is only used in testing -func (k Keeper) IsPendingConsumerRemovalProp(ctx sdk.Context, chainID string, stopTime time.Time) bool { +func (k Keeper) PendingConsumerRemovalPropExists(ctx sdk.Context, chainID string, stopTime time.Time) bool { store := ctx.KVStore(k.storeKey) bz := store.Get(types.PendingCRPKey(stopTime, chainID)) diff --git a/x/ccv/provider/keeper/proposal_test.go b/x/ccv/provider/keeper/proposal_test.go index 4b9e049109..4448ed6b76 100644 --- a/x/ccv/provider/keeper/proposal_test.go +++ b/x/ccv/provider/keeper/proposal_test.go @@ -381,13 +381,13 @@ func TestHandleConsumerRemovalProposal(t *testing.T) { if tc.expStop { // Expect no pending proposal to exist - found := providerKeeper.IsPendingConsumerRemovalProp(ctx, tc.prop.ChainId, tc.prop.StopTime) + found := providerKeeper.PendingConsumerRemovalPropExists(ctx, tc.prop.ChainId, tc.prop.StopTime) require.False(t, found) testProviderStateIsCleaned(t, ctx, providerKeeper, tc.prop.ChainId, "channelID") } else { // Proposal should be stored as pending - found := providerKeeper.IsPendingConsumerRemovalProp(ctx, tc.prop.ChainId, tc.prop.StopTime) + found := providerKeeper.PendingConsumerRemovalPropExists(ctx, tc.prop.ChainId, tc.prop.StopTime) require.True(t, found) } @@ -543,7 +543,7 @@ func TestPendingConsumerRemovalPropDeletion(t *testing.T) { providerKeeper.DeletePendingConsumerRemovalProps(ctx, propsToExecute...) numDeleted := 0 for _, tc := range testCases { - res := providerKeeper.IsPendingConsumerRemovalProp(ctx, tc.ChainId, tc.StopTime) + res := providerKeeper.PendingConsumerRemovalPropExists(ctx, tc.ChainId, tc.StopTime) if !tc.ExpDeleted { require.NotEmpty(t, res, "consumer removal prop was deleted: %s %s", tc.ChainId, tc.StopTime.String()) continue @@ -853,13 +853,13 @@ func TestBeginBlockCCR(t *testing.T) { providerKeeper.BeginBlockCCR(ctx) // Only the 3rd (final) proposal is still stored as pending - found := providerKeeper.IsPendingConsumerRemovalProp( + found := providerKeeper.PendingConsumerRemovalPropExists( ctx, pendingProps[0].ChainId, pendingProps[0].StopTime) require.False(t, found) - found = providerKeeper.IsPendingConsumerRemovalProp( + found = providerKeeper.PendingConsumerRemovalPropExists( ctx, pendingProps[1].ChainId, pendingProps[1].StopTime) require.False(t, found) - found = providerKeeper.IsPendingConsumerRemovalProp( + found = providerKeeper.PendingConsumerRemovalPropExists( ctx, pendingProps[2].ChainId, pendingProps[2].StopTime) require.True(t, found) } From e44082489aa32ae552d9636d56034845054652c1 Mon Sep 17 00:00:00 2001 From: mpoke Date: Thu, 15 Dec 2022 18:30:35 +0100 Subject: [PATCH 5/6] use uint64 in keys instead of time.Time --- x/ccv/provider/types/keys.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/x/ccv/provider/types/keys.go b/x/ccv/provider/types/keys.go index 2d9a726736..5465cb3ac7 100644 --- a/x/ccv/provider/types/keys.go +++ b/x/ccv/provider/types/keys.go @@ -144,28 +144,28 @@ func InitTimeoutTimestampKey(chainID string) []byte { } // PendingCAPKey returns the key under which a pending consumer addition proposal is stored. -// The key has the following format: PendingCAPBytePrefix | timestamp | chainID +// The key has the following format: PendingCAPBytePrefix | timestamp.UnixNano() | chainID func PendingCAPKey(timestamp time.Time, chainID string) []byte { - timeBz := sdk.FormatTimeBytes(timestamp) + ts := uint64(timestamp.UTC().UnixNano()) return AppendMany( // Append the prefix []byte{PendingCAPBytePrefix}, - // Append the time bytes - timeBz, + // Append the time + sdk.Uint64ToBigEndian(ts), // Append the chainId []byte(chainID), ) } // PendingCRPKey returns the key under which pending consumer removal proposals are stored. -// The key has the following format: PendingCRPBytePrefix | timestamp | chainID +// The key has the following format: PendingCRPBytePrefix | timestamp.UnixNano() | chainID func PendingCRPKey(timestamp time.Time, chainID string) []byte { - timeBz := sdk.FormatTimeBytes(timestamp) + ts := uint64(timestamp.UTC().UnixNano()) return AppendMany( // Append the prefix []byte{PendingCRPBytePrefix}, - // Append the time bytes - timeBz, + // Append the time + sdk.Uint64ToBigEndian(ts), // Append the chainId []byte(chainID), ) From ee7040952773e8e2ec143e966b9c0c643bda4c0f Mon Sep 17 00:00:00 2001 From: Marius Poke Date: Fri, 16 Dec 2022 16:24:28 +0100 Subject: [PATCH 6/6] Use SDK utility function instead of encoding/binary (#601) use sdk func instead of binary.BigEndian. --- x/ccv/consumer/keeper/keeper.go | 25 +++++++--------------- x/ccv/consumer/keeper/relay.go | 3 +-- x/ccv/consumer/types/keys.go | 16 ++++---------- x/ccv/provider/keeper/keeper.go | 37 +++++++++++---------------------- x/ccv/provider/types/keys.go | 9 ++------ 5 files changed, 27 insertions(+), 63 deletions(-) diff --git a/x/ccv/consumer/keeper/keeper.go b/x/ccv/consumer/keeper/keeper.go index e878bf5b60..9f51545523 100644 --- a/x/ccv/consumer/keeper/keeper.go +++ b/x/ccv/consumer/keeper/keeper.go @@ -1,7 +1,6 @@ package keeper import ( - "encoding/binary" "fmt" "github.com/cosmos/cosmos-sdk/codec" @@ -214,10 +213,8 @@ func (k Keeper) IteratePacketMaturityTime(ctx sdk.Context, cb func(vscId, timeNs defer iterator.Close() for ; iterator.Valid(); iterator.Next() { // Extract bytes following the 1 byte prefix - seqBytes := iterator.Key()[1:] - seq := binary.BigEndian.Uint64(seqBytes) - - timeNs := binary.BigEndian.Uint64(iterator.Value()) + seq := sdk.BigEndianToUint64(iterator.Key()[1:]) + timeNs := sdk.BigEndianToUint64(iterator.Value()) stop := cb(seq, timeNs) if stop { @@ -229,9 +226,7 @@ func (k Keeper) IteratePacketMaturityTime(ctx sdk.Context, cb func(vscId, timeNs // SetPacketMaturityTime sets the maturity time for a given received VSC packet id func (k Keeper) SetPacketMaturityTime(ctx sdk.Context, vscId, maturityTime uint64) { store := ctx.KVStore(k.storeKey) - timeBytes := make([]byte, 8) - binary.BigEndian.PutUint64(timeBytes, maturityTime) - store.Set(types.PacketMaturityTimeKey(vscId), timeBytes) + store.Set(types.PacketMaturityTimeKey(vscId), sdk.Uint64ToBigEndian(maturityTime)) } // GetPacketMaturityTime gets the maturity time for a given received VSC packet id @@ -241,7 +236,7 @@ func (k Keeper) GetPacketMaturityTime(ctx sdk.Context, vscId uint64) uint64 { if bz == nil { return 0 } - return binary.BigEndian.Uint64(bz) + return sdk.BigEndianToUint64(bz) } // DeletePacketMaturityTimes deletes the packet maturity time for given received VSC packet ids @@ -278,9 +273,7 @@ func (k Keeper) VerifyProviderChain(ctx sdk.Context, connectionHops []string) er // SetHeightValsetUpdateID sets the vscID for a given block height func (k Keeper) SetHeightValsetUpdateID(ctx sdk.Context, height, vscID uint64) { store := ctx.KVStore(k.storeKey) - valBytes := make([]byte, 8) - binary.BigEndian.PutUint64(valBytes, vscID) - store.Set(types.HeightValsetUpdateIDKey(height), valBytes) + store.Set(types.HeightValsetUpdateIDKey(height), sdk.Uint64ToBigEndian(vscID)) } // GetHeightValsetUpdateID gets the vscID recorded for a given block height @@ -290,7 +283,7 @@ func (k Keeper) GetHeightValsetUpdateID(ctx sdk.Context, height uint64) uint64 { if bz == nil { return 0 } - return binary.BigEndian.Uint64(bz) + return sdk.BigEndianToUint64(bz) } // DeleteHeightValsetUpdateID deletes the vscID for a given block height @@ -310,10 +303,8 @@ func (k Keeper) IterateHeightToValsetUpdateID(ctx sdk.Context, cb func(height, v defer iterator.Close() for ; iterator.Valid(); iterator.Next() { - heightBytes := iterator.Key()[1:] - height := binary.BigEndian.Uint64(heightBytes) - - vscID := binary.BigEndian.Uint64(iterator.Value()) + height := sdk.BigEndianToUint64(iterator.Key()[1:]) + vscID := sdk.BigEndianToUint64(iterator.Value()) stop := cb(height, vscID) if stop { diff --git a/x/ccv/consumer/keeper/relay.go b/x/ccv/consumer/keeper/relay.go index 05b48085f7..f7822e1dc4 100644 --- a/x/ccv/consumer/keeper/relay.go +++ b/x/ccv/consumer/keeper/relay.go @@ -1,7 +1,6 @@ package keeper import ( - "encoding/binary" "fmt" "strconv" @@ -95,7 +94,7 @@ func (k Keeper) QueueVSCMaturedPackets(ctx sdk.Context) { maturedVscIds := []uint64{} for maturityIterator.Valid() { vscId := types.IdFromPacketMaturityTimeKey(maturityIterator.Key()) - if currentTime >= binary.BigEndian.Uint64(maturityIterator.Value()) { + if currentTime >= sdk.BigEndianToUint64(maturityIterator.Value()) { // construct validator set change packet data vscPacket := ccv.NewVSCMaturedPacketData(vscId) diff --git a/x/ccv/consumer/types/keys.go b/x/ccv/consumer/types/keys.go index 46fb5e9b8a..b4385eb6f3 100644 --- a/x/ccv/consumer/types/keys.go +++ b/x/ccv/consumer/types/keys.go @@ -1,8 +1,6 @@ package types import ( - "encoding/binary" - sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -101,22 +99,18 @@ func PendingChangesKey() []byte { // PacketMaturityTimeKey returns the key for storing maturity time for a given received VSC packet id func PacketMaturityTimeKey(id uint64) []byte { - seqBytes := make([]byte, 8) - binary.BigEndian.PutUint64(seqBytes, id) - return append([]byte{PacketMaturityTimeBytePrefix}, seqBytes...) + return append([]byte{PacketMaturityTimeBytePrefix}, sdk.Uint64ToBigEndian(id)...) } // IdFromPacketMaturityTimeKey returns the packet id corresponding to a maturity time full key (including prefix) func IdFromPacketMaturityTimeKey(key []byte) uint64 { // Bytes after single byte prefix are converted to uin64 - return binary.BigEndian.Uint64(key[1:]) + return sdk.BigEndianToUint64(key[1:]) } // HeightValsetUpdateIDKey returns the key to 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([]byte{HeightValsetUpdateIDBytePrefix}, sdk.Uint64ToBigEndian(height)...) } // OutstandingDowntimeKey returns the key to a validators' outstanding downtime by consensus address @@ -131,7 +125,5 @@ func CrossChainValidatorKey(addr []byte) []byte { // HistoricalInfoKey returns the key to historical info to a given block height func HistoricalInfoKey(height int64) []byte { - hBytes := make([]byte, 8) - binary.BigEndian.PutUint64(hBytes, uint64(height)) - return append([]byte{HistoricalInfoBytePrefix}, hBytes...) + return append([]byte{HistoricalInfoBytePrefix}, sdk.Uint64ToBigEndian(uint64(height))...) } diff --git a/x/ccv/provider/keeper/keeper.go b/x/ccv/provider/keeper/keeper.go index 8eaf8ff324..7a07e5e851 100644 --- a/x/ccv/provider/keeper/keeper.go +++ b/x/ccv/provider/keeper/keeper.go @@ -1,7 +1,6 @@ package keeper import ( - "encoding/binary" "fmt" "time" @@ -344,7 +343,7 @@ func (k Keeper) IterateUnbondingOps(ctx sdk.Context, cb func(id uint64, ubdOp cc defer iterator.Close() for ; iterator.Valid(); iterator.Next() { - id := binary.BigEndian.Uint64(iterator.Key()[1:]) + id := sdk.BigEndianToUint64(iterator.Key()[1:]) bz := iterator.Value() if bz == nil { panic(fmt.Errorf("unbonding operation is nil for id %d", id)) @@ -544,12 +543,7 @@ func (k Keeper) IncrementValidatorSetUpdateId(ctx sdk.Context) { func (k Keeper) SetValidatorSetUpdateId(ctx sdk.Context, vscID uint64) { store := ctx.KVStore(k.storeKey) - - // Convert back into bytes for storage - bz := make([]byte, 8) - binary.BigEndian.PutUint64(bz, vscID) - - store.Set(types.ValidatorSetUpdateIdKey(), bz) + store.Set(types.ValidatorSetUpdateIdKey(), sdk.Uint64ToBigEndian(vscID)) } func (k Keeper) GetValidatorSetUpdateId(ctx sdk.Context) (vscID uint64) { @@ -560,7 +554,7 @@ func (k Keeper) GetValidatorSetUpdateId(ctx sdk.Context) (vscID uint64) { vscID = 0 } else { // Unmarshal - vscID = binary.BigEndian.Uint64(bz) + vscID = sdk.BigEndianToUint64(bz) } return vscID @@ -569,9 +563,7 @@ func (k Keeper) GetValidatorSetUpdateId(ctx sdk.Context) (vscID uint64) { // SetValsetUpdateBlockHeight sets the block height for a given vscID func (k Keeper) SetValsetUpdateBlockHeight(ctx sdk.Context, vscID, blockHeight uint64) { store := ctx.KVStore(k.storeKey) - heightBytes := make([]byte, 8) - binary.BigEndian.PutUint64(heightBytes, blockHeight) - store.Set(types.ValsetUpdateBlockHeightKey(vscID), heightBytes) + store.Set(types.ValsetUpdateBlockHeightKey(vscID), sdk.Uint64ToBigEndian(blockHeight)) } // GetValsetUpdateBlockHeight gets the block height for a given vscID @@ -581,7 +573,7 @@ func (k Keeper) GetValsetUpdateBlockHeight(ctx sdk.Context, vscID uint64) (uint6 if bz == nil { return 0, false } - return binary.BigEndian.Uint64(bz), true + return sdk.BigEndianToUint64(bz), true } // IterateValsetUpdateBlockHeight iterates through the mapping from vscIDs to block heights. @@ -595,8 +587,8 @@ func (k Keeper) IterateValsetUpdateBlockHeight(ctx sdk.Context, cb func(vscID, h defer iterator.Close() for ; iterator.Valid(); iterator.Next() { - vscID := binary.BigEndian.Uint64(iterator.Key()[1:]) - height := binary.BigEndian.Uint64(iterator.Value()) + vscID := sdk.BigEndianToUint64(iterator.Key()[1:]) + height := sdk.BigEndianToUint64(iterator.Value()) stop := cb(vscID, height) if stop { @@ -690,10 +682,7 @@ func (k Keeper) AppendSlashAck(ctx sdk.Context, chainID, ack string) { // SetInitChainHeight sets the provider block height when the given consumer chain was initiated func (k Keeper) SetInitChainHeight(ctx sdk.Context, chainID string, height uint64) { store := ctx.KVStore(k.storeKey) - heightBytes := make([]byte, 8) - binary.BigEndian.PutUint64(heightBytes, height) - - store.Set(types.InitChainHeightKey(chainID), heightBytes) + store.Set(types.InitChainHeightKey(chainID), sdk.Uint64ToBigEndian(height)) } // GetInitChainHeight returns the provider block height when the given consumer chain was initiated @@ -704,7 +693,7 @@ func (k Keeper) GetInitChainHeight(ctx sdk.Context, chainID string) (uint64, boo return 0, false } - return binary.BigEndian.Uint64(bz), true + return sdk.BigEndianToUint64(bz), true } // DeleteInitChainHeight deletes the block height value for which the given consumer chain's channel was established @@ -773,9 +762,7 @@ func (k Keeper) DeleteConsumerClientId(ctx sdk.Context, chainID string) { // SetInitTimeoutTimestamp sets the init timeout timestamp for the given chain ID func (k Keeper) SetInitTimeoutTimestamp(ctx sdk.Context, chainID string, ts uint64) { store := ctx.KVStore(k.storeKey) - tsBytes := make([]byte, 8) - binary.BigEndian.PutUint64(tsBytes, ts) - store.Set(types.InitTimeoutTimestampKey(chainID), tsBytes) + store.Set(types.InitTimeoutTimestampKey(chainID), sdk.Uint64ToBigEndian(ts)) } // GetInitTimeoutTimestamp returns the init timeout timestamp for the given chain ID. @@ -786,7 +773,7 @@ func (k Keeper) GetInitTimeoutTimestamp(ctx sdk.Context, chainID string) (uint64 if bz == nil { return 0, false } - return binary.BigEndian.Uint64(bz), true + return sdk.BigEndianToUint64(bz), true } // DeleteInitTimeoutTimestamp removes from the store the init timeout timestamp for the given chainID. @@ -807,7 +794,7 @@ func (k Keeper) IterateInitTimeoutTimestamp(ctx sdk.Context, cb func(chainID str defer iterator.Close() for ; iterator.Valid(); iterator.Next() { chainID := string(iterator.Key()[1:]) - ts := binary.BigEndian.Uint64(iterator.Value()) + ts := sdk.BigEndianToUint64(iterator.Value()) stop := cb(chainID, ts) if stop { diff --git a/x/ccv/provider/types/keys.go b/x/ccv/provider/types/keys.go index 5465cb3ac7..9bd6a030b2 100644 --- a/x/ccv/provider/types/keys.go +++ b/x/ccv/provider/types/keys.go @@ -2,7 +2,6 @@ package types import ( "bytes" - "encoding/binary" "fmt" "time" @@ -186,16 +185,12 @@ func ParseUnbondingOpIndexKey(key []byte) (string, uint64, error) { // UnbondingOpKey returns the key that stores a record of all the ids of consumer chains that // need to unbond before a given unbonding operation can complete func UnbondingOpKey(id uint64) []byte { - bz := make([]byte, 8) - binary.BigEndian.PutUint64(bz, id) - return append([]byte{UnbondingOpBytePrefix}, bz...) + return append([]byte{UnbondingOpBytePrefix}, sdk.Uint64ToBigEndian(id)...) } // ValsetUpdateBlockHeightKey returns the key that storing the mapping from valset update ID to block height func ValsetUpdateBlockHeightKey(valsetUpdateId uint64) []byte { - vuidBytes := make([]byte, 8) - binary.BigEndian.PutUint64(vuidBytes, valsetUpdateId) - return append([]byte{ValsetUpdateBlockHeightBytePrefix}, vuidBytes...) + return append([]byte{ValsetUpdateBlockHeightBytePrefix}, sdk.Uint64ToBigEndian(valsetUpdateId)...) } // ConsumerGenesisKey returns the key corresponding to consumer genesis state material