@@ -2,6 +2,7 @@ package keeper
22
33import (
44 "fmt"
5+ "strings"
56 "time"
67
78 stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
@@ -72,20 +73,137 @@ func (k Keeper) HandleConsumerRewardDenomProposal(ctx sdk.Context, proposal *typ
7273 return k .HandleLegacyConsumerRewardDenomProposal (ctx , & p )
7374}
7475
75- // HandleConsumerModificationProposal modifies a running consumer chain
76+ // HandleConsumerModificationProposal modifies a consumer chain.
77+ // If proposal.NewChainId is present and different, we validate it and (only if pre-launch)
78+ // rename the chain-id by migrating all chain-scoped keys. Then we hand off to the legacy path.
7679func (k Keeper ) HandleConsumerModificationProposal (ctx sdk.Context , proposal * types.MsgConsumerModification ) error {
77- p := types.ConsumerModificationProposal {
80+ chainID := proposal .ChainId
81+
82+ // Optional pre-launch rename
83+ if s := strings .TrimSpace (proposal .NewChainId ); s != "" && s != chainID {
84+ if err := types .ValidateChainId ("NewChainId" , s ); err != nil {
85+ return errorsmod .Wrapf (types .ErrInvalidConsumerModificationProposal ,
86+ "invalid new chain id: %s" , err .Error ())
87+ }
88+
89+ // launched == has client-id
90+ if _ , launched := k .GetConsumerClientId (ctx , chainID ); launched {
91+ return errorsmod .Wrapf (types .ErrInvalidConsumerModificationProposal ,
92+ "cannot update chain id of a launched chain: %s" , chainID )
93+ }
94+
95+ // ensure target not taken (taken == has client-id)
96+ if _ , taken := k .GetConsumerClientId (ctx , s ); taken {
97+ return errorsmod .Wrapf (types .ErrInvalidConsumerModificationProposal ,
98+ "target chain id already exists: %s" , s )
99+ }
100+
101+ // Move any chain-scoped state you’re tracking prelaunch
102+ if err := k .renameConsumerChain (ctx , chainID , s ); err != nil {
103+ return err
104+ }
105+
106+ ctx .EventManager ().EmitEvent (
107+ sdk .NewEvent ("consumer_chain_renamed" ,
108+ sdk .NewAttribute ("old_chain_id" , chainID ),
109+ sdk .NewAttribute ("new_chain_id" , s ),
110+ ),
111+ )
112+
113+ chainID = s
114+ }
115+
116+ // Only call legacy path if the chain is actually running (has client-id).
117+ if _ , running := k .GetConsumerClientId (ctx , chainID ); ! running {
118+ return nil
119+ }
120+
121+ legacy := types.ConsumerModificationProposal {
78122 Title : proposal .Title ,
79123 Description : proposal .Description ,
80- ChainId : proposal . ChainId ,
124+ ChainId : chainID ,
81125 Top_N : proposal .Top_N ,
82126 ValidatorsPowerCap : proposal .ValidatorsPowerCap ,
83127 ValidatorSetCap : proposal .ValidatorSetCap ,
84128 Allowlist : proposal .Allowlist ,
85129 Denylist : proposal .Denylist ,
86130 }
131+ return k .HandleLegacyConsumerModificationProposal (ctx , & legacy )
132+ }
133+
134+ // renameConsumerChain moves all chain-scoped state from oldID -> newID.
135+ // It handles exact keys (singletons) and prefixed collections that include the chain-id in the key.
136+ func (k Keeper ) renameConsumerChain (ctx sdk.Context , oldID , newID string ) error {
137+ kv := ctx .KVStore (k .storeKey )
138+
139+ // --- basic helpers ---
140+
141+ // copy+delete for a single exact key
142+ moveIf := func (oldKey , newKey []byte ) {
143+ if bz := kv .Get (oldKey ); bz != nil {
144+ kv .Set (newKey , bz )
145+ kv .Delete (oldKey )
146+ }
147+ }
87148
88- return k .HandleLegacyConsumerModificationProposal (ctx , & p )
149+ // copy+delete for all entries under prefix(oldID)+suffix -> prefix(newID)+suffix
150+ migratePrefix := func (prefixFor func (string ) []byte ) {
151+ oldPref := prefixFor (oldID )
152+ it := storetypes .KVStorePrefixIterator (kv , oldPref )
153+ defer it .Close ()
154+ for ; it .Valid (); it .Next () {
155+ key := it .Key ()
156+ val := it .Value ()
157+ suffix := key [len (oldPref ):]
158+ newKey := append (prefixFor (newID ), suffix ... )
159+ kv .Set (newKey , val )
160+ kv .Delete (key )
161+ }
162+ }
163+
164+ // convenience for prefixes created via ChainIdWithLenKey(prefixByte, chainID)
165+ migrateByPrefixByte := func (b byte ) {
166+ migratePrefix (func (id string ) []byte { return types .ChainIdWithLenKey (b , id ) })
167+ }
168+
169+ // --- singletons keyed by chain-id ---
170+ moveIf (types .ChainToClientKey (oldID ), types .ChainToClientKey (newID ))
171+ if ch := kv .Get (types .ChainToChannelKey (oldID )); ch != nil {
172+ // forward map: chainID -> channelID
173+ moveIf (types .ChainToChannelKey (oldID ), types .ChainToChannelKey (newID ))
174+ // reverse map: channelID -> chainID (rewrite the VALUE to newID)
175+ kv .Set (types .ChannelToChainKey (string (ch )), []byte (newID ))
176+ }
177+ moveIf (types .ConsumerGenesisKey (oldID ), types .ConsumerGenesisKey (newID ))
178+ moveIf (types .SlashAcksKey (oldID ), types .SlashAcksKey (newID ))
179+ moveIf (types .InitChainHeightKey (oldID ), types .InitChainHeightKey (newID ))
180+ moveIf (types .PendingVSCsKey (oldID ), types .PendingVSCsKey (newID ))
181+ moveIf (types .ConsumerRewardsAllocationKey (oldID ), types .ConsumerRewardsAllocationKey (newID ))
182+ moveIf (types .TopNKey (oldID ), types .TopNKey (newID ))
183+ moveIf (types .MinimumPowerInTopNKey (oldID ), types .MinimumPowerInTopNKey (newID ))
184+ moveIf (types .ValidatorSetCapKey (oldID ), types .ValidatorSetCapKey (newID ))
185+ moveIf (types .ValidatorsPowerCapKey (oldID ), types .ValidatorsPowerCapKey (newID ))
186+
187+ // --- collections prefixed by (prefixByte + chain-id + suffix) ---
188+ migrateByPrefixByte (types .ConsumerValidatorBytePrefix )
189+ migrateByPrefixByte (types .ConsumerValidatorsBytePrefix )
190+ migrateByPrefixByte (types .ValidatorsByConsumerAddrBytePrefix )
191+ migrateByPrefixByte (types .OptedInBytePrefix )
192+ migrateByPrefixByte (types .AllowlistPrefix )
193+ migrateByPrefixByte (types .DenylistPrefix )
194+ migrateByPrefixByte (types .ConsumerAddrsToPruneV2BytePrefix )
195+ migrateByPrefixByte (types .ThrottledPacketDataBytePrefix )
196+
197+ // --- proposal side-table where VALUE == chain-id (rewrite values) ---
198+ it := storetypes .KVStorePrefixIterator (kv , []byte {types .ProposedConsumerChainByteKey })
199+ defer it .Close ()
200+ for ; it .Valid (); it .Next () {
201+ if string (it .Value ()) == oldID {
202+ kv .Set (it .Key (), []byte (newID ))
203+ }
204+ }
205+
206+ return nil
89207}
90208
91209// CreateConsumerClient will create the CCV client for the given consumer chain. The CCV channel must be built
0 commit comments