Skip to content

Commit 7b08254

Browse files
authored
chore: recover haqq_11235-1, migaloo-1, omniflixhub-1 light clients (#621)
1 parent 7202ca2 commit 7b08254

5 files changed

Lines changed: 27 additions & 266 deletions

File tree

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
- Recover expired IBC light clients for `haqq_11235-1`, `migaloo-1`, and `omniflixhub-1` ([#621](https://github.com/noble-assets/noble/pull/621))

app.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -476,8 +476,7 @@ func (app *App) RegisterUpgradeHandler() error {
476476
app.ModuleManager,
477477
app.Configurator(),
478478
app.Logger(),
479-
&app.AccountKeeper,
480-
app.OrbiterKeeper,
479+
app.IBCKeeper.ClientKeeper,
481480
),
482481
)
483482

e2e/upgrade_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,12 @@ func TestChainUpgrade(t *testing.T) {
2727
t.Skip("skipping test in short mode.")
2828
}
2929

30-
genesisVersion := "v11.0.1"
30+
genesisVersion := "v11.1.0"
3131

3232
upgrades := []e2e.ChainUpgrade{
3333
{
3434
Image: e2e.LocalImages[0],
35-
UpgradeName: "pulse",
35+
UpgradeName: "v11.2.0",
3636
},
3737
}
3838

upgrade/constants.go

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,7 @@
1717
package upgrade
1818

1919
// UpgradeName is the name of this specific software upgrade used on-chain.
20-
const UpgradeName = "pulse"
21-
22-
// UpgradeASCII is the ASCII art shown to node operators upon successful upgrade.
23-
const UpgradeASCII = `
24-
25-
██████╗ ██╗ ██╗██╗ ███████╗███████╗
26-
██╔══██╗██║ ██║██║ ██╔════╝██╔════╝
27-
██████╔╝██║ ██║██║ ███████╗█████╗
28-
██╔═══╝ ██║ ██║██║ ╚════██║██╔══╝
29-
██║ ╚██████╔╝███████╗███████║███████╗
30-
╚═╝ ╚═════╝ ╚══════╝╚══════╝╚══════╝
31-
32-
`
20+
const UpgradeName = "v11.2.0"
3321

3422
// TestnetChainID is the Chain ID of the Noble testnet.
3523
const TestnetChainID = "grand-1"

upgrade/upgrade.go

Lines changed: 22 additions & 249 deletions
Original file line numberDiff line numberDiff line change
@@ -18,26 +18,19 @@ package upgrade
1818

1919
import (
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

3529
func 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

Comments
 (0)