@@ -18,26 +18,19 @@ package upgrade
1818
1919import (
2020 "context"
21- "errors"
22- "fmt"
2321
2422 "cosmossdk.io/log"
2523 upgradetypes "cosmossdk.io/x/upgrade/types"
2624 sdk "github.com/cosmos/cosmos-sdk/types"
2725 "github.com/cosmos/cosmos-sdk/types/module"
28- authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper"
29- authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
30- orbiterkeeper "github.com/noble-assets/orbiter/v2/keeper"
31- dispatchercomp "github.com/noble-assets/orbiter/v2/keeper/component/dispatcher"
32- orbitercore "github.com/noble-assets/orbiter/v2/types/core"
26+ clientkeeper "github.com/cosmos/ibc-go/v8/modules/core/02-client/keeper"
3327)
3428
3529func CreateUpgradeHandler (
3630 mm * module.Manager ,
3731 cfg module.Configurator ,
3832 logger log.Logger ,
39- accountKeeper * authkeeper.AccountKeeper ,
40- orbiterKeeper * orbiterkeeper.Keeper ,
33+ clientKeeper clientkeeper.Keeper ,
4134) upgradetypes.UpgradeHandler {
4235 return func (ctx context.Context , _ upgradetypes.Plan , vm module.VersionMap ) (module.VersionMap , error ) {
4336 vm , err := mm .RunMigrations (ctx , cfg , vm )
@@ -46,249 +39,29 @@ func CreateUpgradeHandler(
4639 }
4740
4841 sdkCtx := sdk .UnwrapSDKContext (ctx )
49- cachedCtx , writeCache := sdkCtx .CacheContext ()
50- err = updateOrbiterStats (cachedCtx , logger , orbiterKeeper )
51- if err != nil {
52- logger .Error ("failed to updated Orbiter stats" , "error" , err )
53- } else {
54- writeCache ()
55- }
56-
57- cachedCtx , writeCache = sdkCtx .CacheContext ()
58- err = updateOrbiterModuleAccounts (cachedCtx , logger , * accountKeeper )
59- if err != nil {
60- logger .Error ("failed to update Orbiter module accounts" , "error" , err )
61- } else {
62- writeCache ()
63- }
64-
65- logger .Info (UpgradeASCII )
66-
67- return vm , nil
68- }
69- }
7042
71- // The Orbiter module and dust collector accounts should be module accounts. If they received funds
72- // before the v11 upgrade, they remained stored as base account. This causes the query to the module
73- // account to fail. This handler migrates them to module accounts.
74- func updateOrbiterModuleAccounts (ctx sdk.Context , logger log.Logger , accountKeeper authkeeper.AccountKeeper ) error {
75- for _ , name := range []string {orbitercore .ModuleName , orbitercore .DustCollectorName } {
76- addr , perms := accountKeeper .GetModuleAddressAndPermissions (name )
77- if addr == nil {
78- return fmt .Errorf ("failed to get module address and permissions for %s" , name )
79- }
80-
81- // Module account registration is lazy. When we run this function without real mainnet or
82- // testnet data, the query will return `nil` because the modules are not registered yet.
83- // We should skip this situation to perform e2e upgrade tests.
84- acc := accountKeeper .GetAccount (ctx , addr )
85- if acc == nil {
86- continue
87- }
88-
89- baseAcc , ok := (acc ).(* authtypes.BaseAccount )
90- if ! ok {
91- // We should skip the case in which the address is already associated with a module
92- // address.
93- _ , ok := (acc ).(* authtypes.ModuleAccount )
94- if ok {
95- logger .Info (fmt .Sprintf ("skipped migration of %s, already a module account" , name ), "address" , addr .String ())
96- continue
43+ // In IBC-Go v8.7.0, the MsgRecoverClient message does not support the
44+ // LegacyAminoJSON signing mode, preventing recovery via the Noble
45+ // Maintenance Multisig. As a result, expired clients on mainnet must
46+ // be manually recovered as part of a software upgrade.
47+ if sdkCtx .ChainID () == MainnetChainID {
48+ // Substitute the IBC light client for the haqq_11235-1 chain.
49+ err = clientKeeper .RecoverClient (sdkCtx , "07-tendermint-58" , "07-tendermint-194" )
50+ if err != nil {
51+ logger .Error ("failed to recover haqq_11235-1 client" , "error" , err )
52+ }
53+ // Substitute the IBC light client for the migaloo-1 chain.
54+ err = clientKeeper .RecoverClient (sdkCtx , "07-tendermint-19" , "07-tendermint-201" )
55+ if err != nil {
56+ logger .Error ("failed to recover migaloo-1 client" , "error" , err )
57+ }
58+ // Substitute the IBC light client for the omniflixhub-1 chain.
59+ err = clientKeeper .RecoverClient (sdkCtx , "07-tendermint-68" , "07-tendermint-198" )
60+ if err != nil {
61+ logger .Error ("failed to recover omniflixhub-1 client" , "error" , err )
9762 }
98- // If we are very unlucky...
99- return fmt .Errorf ("error creating the base account for %s: %T" , name , acc )
100- }
101-
102- macc := authtypes .NewModuleAccount (baseAcc , name , perms ... )
103- accountKeeper .SetModuleAccount (ctx , macc )
104- logger .Info (fmt .Sprintf ("migrated %s to a module account" , name ), "address" , addr .String ())
105- }
106-
107- return nil
108- }
109-
110- // updateOrbiterStats updates the statistics of the Orbiter module to improve readability of the
111- // denom and fix the used couterparty id. We have two cases to fix here:
112- // 1. Update entries that use the countertparty channel id OF Noble with the counterparty channel
113- // id ON Noble (this error applies only for testnet since it is caused by the beta release)
114- // 2. Use the denom representation on Noble and not the IBC one. This means converting this
115- // transfer/channel-4280/uusdc into uusdc
116- func updateOrbiterStats (ctx sdk.Context , logger log.Logger , orbiterKeeper * orbiterkeeper.Keeper ) error {
117- expectedDenom := "uusdc"
118- channelsToCorrect := make (map [string ]string )
119- switch ctx .ChainID () {
120- case MainnetChainID :
121- // No-op for mainnet since channels are correct there.
122- case TestnetChainID :
123- // Counterparty channel to Noble -> Noble to counterparty channel.
124- channelsToCorrect = map [string ]string {
125- "channel-4280" : "channel-22" , // osmosis
126- "channel-27" : "channel-639" , // namada
127- "channel-3" : "channel-333" , // xion
128- "channel-496" : "channel-43" , // neutron
129- }
130- default :
131- return nil
132- }
133-
134- dispatcher := orbiterKeeper .Dispatcher ()
135- if dispatcher == nil {
136- return errors .New ("received nil orbiter dispatcher component" )
137- }
138-
139- err := updateDispatchedAmounts (ctx , logger , expectedDenom , channelsToCorrect , dispatcher )
140- if err != nil {
141- return fmt .Errorf ("failed to update dispatcher amounts: %w" , err )
142- }
143-
144- err = updateDispatchedCounts (ctx , logger , channelsToCorrect , dispatcher )
145- if err != nil {
146- return fmt .Errorf ("failed to update dispatcher counts: %w" , err )
147- }
148-
149- return nil
150- }
151-
152- func updateDispatchedAmounts (
153- ctx context.Context ,
154- logger log.Logger ,
155- expectedDenom string ,
156- channelsToCorrect map [string ]string ,
157- dispatcher * dispatchercomp.Dispatcher ,
158- ) error {
159- amounts := dispatcher .GetAllDispatchedAmounts (ctx )
160-
161- var numDenomUpdated , numChannelUpdated int
162- for _ , entry := range amounts {
163- correctChannel , isWrongChannelID := channelsToCorrect [entry .SourceId .GetCounterpartyId ()]
164-
165- // The only available route so far is IBC to CCTP. Since CCTP supports only USDC, we have
166- // to update all the denoms to USDC.
167- isWrongDenom := entry .Denom != expectedDenom
168-
169- // If the channel is not in the wrong channel list, or the denom is USDC, then the entry is
170- // correct. We basically skip all the entries with 0 incoming dispatched amount but a non
171- // zero outgoing amount.
172- if ! isWrongChannelID && ! isWrongDenom {
173- // Source protocol is always IBC and destination is always CCTP, no need to log them.
174- logger .Debug ("skipping dispatched amounts entry" ,
175- "src_counterparty_id" , entry .SourceId .GetCounterpartyId (),
176- "dst_countertparty_id" , entry .DestinationId .GetCounterpartyId (),
177- "denom" , entry .Denom ,
178- "amount_incoming" , entry .AmountDispatched .Incoming .String (),
179- "amount_outgoing" , entry .AmountDispatched .Outgoing .String (),
180- )
181- continue
182- }
183-
184- // One of the situations to fix, is with a correct channel ID but a wrong denom. Since the
185- // correct channel ID is not in the map, we have to use the values of the entry, otherwise
186- // it will use the empty string from the miss in the map.
187- if ! isWrongChannelID {
188- correctChannel = entry .SourceId .GetCounterpartyId ()
189- numDenomUpdated += 1
190- } else {
191- numChannelUpdated += 1
192- }
193-
194- logger .Debug ("handling dispatched amounts entry" ,
195- "src_counterparty_id" , entry .SourceId .GetCounterpartyId (),
196- "dst_countertparty_id" , entry .DestinationId .GetCounterpartyId (),
197- "denom" , entry .Denom ,
198- "amount_incoming" , entry .AmountDispatched .Incoming .String (),
199- "amount_outgoing" , entry .AmountDispatched .Outgoing .String (),
200- )
201-
202- // We remove from the store the wrong entry.
203- err := dispatcher .RemoveDispatchedAmount (ctx , entry .SourceId , entry .DestinationId , entry .Denom )
204- if err != nil {
205- return fmt .Errorf ("failed to remove dispatched amount from state: %w" , err )
206- }
207-
208- correctSourceID , err := orbitercore .NewCrossChainID (entry .SourceId .GetProtocolId (), correctChannel )
209- if err != nil {
210- return fmt .Errorf ("failed to create cross chain ID: %w" , err )
211- }
212-
213- // Get the entry with the correct outgoing value from the state. Returns zero amounts if
214- // not present.
215- oldValue := dispatcher .GetDispatchedAmount (ctx , & correctSourceID , entry .DestinationId , expectedDenom )
216-
217- dispatchedAmount := oldValue .AmountDispatched
218- // Add the entry incoming amount as the incoming amount of the correct entry.
219- if entry .AmountDispatched .Incoming .IsPositive () {
220- dispatchedAmount .Incoming = dispatchedAmount .Incoming .Add (entry .AmountDispatched .Incoming )
221- }
222- if entry .AmountDispatched .Outgoing .IsPositive () {
223- dispatchedAmount .Outgoing = dispatchedAmount .Outgoing .Add (entry .AmountDispatched .Outgoing )
224- }
225-
226- // Update the entry in the store
227- err = dispatcher .SetDispatchedAmount (ctx , & correctSourceID , entry .DestinationId , expectedDenom , dispatchedAmount )
228- if err != nil {
229- return fmt .Errorf ("failed to update the dispatched amount in state: %w" , err )
230- }
231- }
232-
233- logger .Info ("completed orbiter stats denom update" , "updated_entries" , numDenomUpdated )
234- logger .Info ("completed orbiter stats channel update" , "updated_entries" , numChannelUpdated )
235- return nil
236- }
237-
238- func updateDispatchedCounts (
239- ctx context.Context ,
240- logger log.Logger ,
241- channelsToCorrect map [string ]string ,
242- dispatcher * dispatchercomp.Dispatcher ,
243- ) error {
244- counts := dispatcher .GetAllDispatchedCounts (ctx )
245-
246- var numCountsUpdated int
247- for _ , entry := range counts {
248- correctChannel , isWrongChannelID := channelsToCorrect [entry .SourceId .GetCounterpartyId ()]
249-
250- // If the channel is not wrong, then we don't have to do anything.
251- if ! isWrongChannelID {
252- // Source protocol is always IBC and destination is always CCTP
253- logger .Debug ("skipping dispatched counts entry" ,
254- "src_counterparty_id" , entry .SourceId .GetCounterpartyId (),
255- "dst_countertparty_id" , entry .DestinationId .GetCounterpartyId (),
256- "count" , entry .Count ,
257- )
258- continue
259- }
260-
261- logger .Debug ("handling dispatched counts entry" ,
262- "src_counterparty_id" , entry .SourceId .GetCounterpartyId (),
263- "dst_countertparty_id" , entry .DestinationId .GetCounterpartyId (),
264- "count" , entry .Count ,
265- )
266-
267- // We remove from the store the wrong entry.
268- err := dispatcher .RemoveDispatchedCounts (ctx , entry .SourceId , entry .DestinationId )
269- if err != nil {
270- return fmt .Errorf ("failed to remove dispatched counts from state: %w" , err )
271- }
272-
273- correctSourceID , err := orbitercore .NewCrossChainID (entry .SourceId .GetProtocolId (), correctChannel )
274- if err != nil {
275- return fmt .Errorf ("failed to create cross chain ID: %w" , err )
276- }
277-
278- // Get the entry with the correct counts value from the state. Returns zero if not present.
279- oldValue := dispatcher .GetDispatchedCounts (ctx , & correctSourceID , entry .DestinationId )
280-
281- counts := oldValue .Count + entry .Count
282-
283- // Update the entry in the store
284- err = dispatcher .SetDispatchedCounts (ctx , & correctSourceID , entry .DestinationId , counts )
285- if err != nil {
286- return fmt .Errorf ("failed to update the dispatched counts in state: %w" , err )
28763 }
28864
289- numCountsUpdated += 1
65+ return vm , nil
29066 }
291- logger .Info ("completed orbiter stats counts update" , "updated_entries" , numCountsUpdated )
292-
293- return nil
29467}
0 commit comments