From 9fd52c76cb2fc08878963820e6d668332ee22967 Mon Sep 17 00:00:00 2001 From: Jiri Date: Wed, 24 Jul 2024 13:29:14 +0200 Subject: [PATCH 1/9] Cudos upgrade handler --- app/app.go | 66 ++++++++++++++++++++++------------- app/cudos_merge.go | 24 +++++++++++++ cmd/fetchd/cmd/cudos_merge.go | 12 +++++++ cmd/fetchd/cmd/root.go | 9 +++++ 4 files changed, 87 insertions(+), 24 deletions(-) create mode 100644 app/cudos_merge.go create mode 100644 cmd/fetchd/cmd/cudos_merge.go diff --git a/app/app.go b/app/app.go index 2154bcc0e..6fad3ba98 100644 --- a/app/app.go +++ b/app/app.go @@ -216,6 +216,7 @@ type App struct { interfaceRegistry types.InterfaceRegistry invCheckPeriod uint + cudosPath string // keys to access the substores keys map[string]*sdk.KVStoreKey @@ -256,7 +257,7 @@ type App struct { // NewSimApp returns a reference to an initialized SimApp. func New( logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest bool, - skipUpgradeHeights map[int64]bool, homePath string, invCheckPeriod uint, encodingConfig appparams.EncodingConfig, enabledProposals []wasm.ProposalType, + skipUpgradeHeights map[int64]bool, homePath string, invCheckPeriod uint, cudosPath string, encodingConfig appparams.EncodingConfig, enabledProposals []wasm.ProposalType, appOpts servertypes.AppOptions, wasmOpts []wasm.Option, baseAppOptions ...func(*baseapp.BaseApp), ) *App { @@ -285,6 +286,7 @@ func New( appCodec: appCodec, interfaceRegistry: interfaceRegistry, invCheckPeriod: invCheckPeriod, + cudosPath: cudosPath, keys: keys, tkeys: tkeys, memKeys: memKeys, @@ -369,7 +371,7 @@ func New( app.GovKeeper = *govKeeper.SetHooks( govtypes.NewMultiGovHooks( - // register the governance hooks + // register the governance hooks ), ) @@ -718,41 +720,57 @@ func (app *App) GetSubspace(moduleName string) paramstypes.Subspace { return subspace } +func (app *App) PerformASIUpgradeTasks(ctx sdk.Context, networkInfo *NetworkConfig, manifest *UpgradeManifest) error { + err := app.DeleteContractStates(ctx, networkInfo, manifest) + if err != nil { + return err + } + + // Call the separate function to handle the admin upgrade + err = app.UpgradeContractAdmins(ctx, networkInfo, manifest) + if err != nil { + return err + } + + err = app.ProcessReconciliation(ctx, networkInfo, manifest) + if err != nil { + return err + } + + err = app.ChangeContractLabels(ctx, networkInfo, manifest) + if err != nil { + return err + } + + err = app.ChangeContractVersions(ctx, networkInfo, manifest) + if err != nil { + return err + } + + return nil +} + func (app *App) RegisterUpgradeHandlers(cfg module.Configurator) { app.UpgradeKeeper.SetUpgradeHandler("v0.11.3", func(ctx sdk.Context, plan upgradetypes.Plan, fromVM module.VersionMap) (module.VersionMap, error) { return app.mm.RunMigrations(ctx, cfg, fromVM) }) app.UpgradeKeeper.SetUpgradeHandler("v0.11.4", func(ctx sdk.Context, plan upgradetypes.Plan, fromVM module.VersionMap) (module.VersionMap, error) { - manifest := NewUpgradeManifest() - - networkInfo, ok := NetworkInfos[ctx.ChainID()] - if !ok { - panic("Network info not found for chain id: " + ctx.ChainID()) - } - - err := app.DeleteContractStates(ctx, &networkInfo, manifest) - if err != nil { - return nil, err - } - - // Call the separate function to handle the admin upgrade - err = app.UpgradeContractAdmins(ctx, &networkInfo, manifest) + genesis, err := ReadGenesisFile(app.cudosPath) if err != nil { return nil, err } + print(genesis) - err = app.ProcessReconciliation(ctx, &networkInfo, manifest) - if err != nil { - return nil, err - } + manifest := NewUpgradeManifest() - err = app.ChangeContractLabels(ctx, &networkInfo, manifest) - if err != nil { - return nil, err + networkInfo, ok := NetworkInfos[ctx.ChainID()] + if !ok { + panic("Network info not found for chain id: " + ctx.ChainID()) } - err = app.ChangeContractVersions(ctx, &networkInfo, manifest) + // Perform ASI upgrade tasks + err = app.PerformASIUpgradeTasks(ctx, &networkInfo, manifest) if err != nil { return nil, err } diff --git a/app/cudos_merge.go b/app/cudos_merge.go new file mode 100644 index 000000000..ef1ac42c0 --- /dev/null +++ b/app/cudos_merge.go @@ -0,0 +1,24 @@ +package app + +import ( + "encoding/json" + "fmt" + "os" +) + +func ReadGenesisFile(filePath string) (*GenesisState, error) { + // Read the file into a byte slice + fileData, err := os.ReadFile(filePath) + if err != nil { + return nil, fmt.Errorf("error reading genesis file: %v", err) + } + + // Parse the JSON data + var genesisState GenesisState + err = json.Unmarshal(fileData, &genesisState) + if err != nil { + return nil, fmt.Errorf("error unmarshalling genesis file: %v", err) + } + + return &genesisState, nil +} diff --git a/cmd/fetchd/cmd/cudos_merge.go b/cmd/fetchd/cmd/cudos_merge.go new file mode 100644 index 000000000..c8aad42e5 --- /dev/null +++ b/cmd/fetchd/cmd/cudos_merge.go @@ -0,0 +1,12 @@ +package cmd + +import "github.com/spf13/cobra" + +// Module init related flags +const ( + FlagCudosPath = "cudos-path" +) + +func AddCudosFlags(startCmd *cobra.Command) { + startCmd.Flags().String(FlagCudosPath, "", "Cudos genesis path") +} diff --git a/cmd/fetchd/cmd/root.go b/cmd/fetchd/cmd/root.go index 4aab53882..3a63db8bf 100644 --- a/cmd/fetchd/cmd/root.go +++ b/cmd/fetchd/cmd/root.go @@ -153,6 +153,7 @@ func initRootCmd(rootCmd *cobra.Command, encodingConfig params.EncodingConfig) { func addModuleInitFlags(startCmd *cobra.Command) { crisis.AddModuleInitFlags(startCmd) wasm.AddModuleInitFlags(startCmd) + AddCudosFlags(startCmd) } func queryCommand() *cobra.Command { @@ -247,6 +248,7 @@ func (a appCreator) newApp(logger log.Logger, db dbm.DB, traceStore io.Writer, a logger, db, traceStore, true, skipUpgradeHeights, cast.ToString(appOpts.Get(flags.FlagHome)), cast.ToUint(appOpts.Get(server.FlagInvCheckPeriod)), + cast.ToString(appOpts.Get(FlagCudosPath)), a.encCfg, app.GetEnabledProposals(), appOpts, @@ -277,6 +279,11 @@ func (a appCreator) appExport( return servertypes.ExportedApp{}, errors.New("application home not set") } + cudosPath, ok := appOpts.Get(FlagCudosPath).(string) + if !ok || cudosPath == "" { + return servertypes.ExportedApp{}, errors.New("cudos path not set") + } + var emptyWasmOpts []wasm.Option if height != -1 { anApp = app.New( @@ -287,6 +294,7 @@ func (a appCreator) appExport( map[int64]bool{}, homePath, uint(1), + cudosPath, a.encCfg, app.GetEnabledProposals(), appOpts, @@ -305,6 +313,7 @@ func (a appCreator) appExport( map[int64]bool{}, homePath, uint(1), + cudosPath, a.encCfg, app.GetEnabledProposals(), appOpts, From c2aec5543f3a534e52915274205ae2c91e2daf92 Mon Sep 17 00:00:00 2001 From: Jiri Date: Wed, 24 Jul 2024 16:16:32 +0200 Subject: [PATCH 2/9] balances --- app/app.go | 13 ++++++- app/cudos_merge.go | 85 ++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 87 insertions(+), 11 deletions(-) diff --git a/app/app.go b/app/app.go index 6fad3ba98..98fcdae06 100644 --- a/app/app.go +++ b/app/app.go @@ -756,11 +756,22 @@ func (app *App) RegisterUpgradeHandlers(cfg module.Configurator) { }) app.UpgradeKeeper.SetUpgradeHandler("v0.11.4", func(ctx sdk.Context, plan upgradetypes.Plan, fromVM module.VersionMap) (module.VersionMap, error) { + // "fetchd-v0.11.3-8-g929563a" genesis, err := ReadGenesisFile(app.cudosPath) if err != nil { return nil, err } - print(genesis) + + // Create the map to store balances by address + balanceMap, _ := GetBankBalances(genesis) + + // Print the balance map + for addr, coins := range balanceMap { + fmt.Printf("Address: %s\n", addr) + for _, coin := range coins { + fmt.Printf(" Coin: %s, Amount: %s\n", coin.Denom, coin.Amount.String()) + } + } manifest := NewUpgradeManifest() diff --git a/app/cudos_merge.go b/app/cudos_merge.go index ef1ac42c0..b5d074cbc 100644 --- a/app/cudos_merge.go +++ b/app/cudos_merge.go @@ -2,23 +2,88 @@ package app import ( "encoding/json" - "fmt" + sdk "github.com/cosmos/cosmos-sdk/types" + "io/ioutil" + "log" "os" ) -func ReadGenesisFile(filePath string) (*GenesisState, error) { - // Read the file into a byte slice - fileData, err := os.ReadFile(filePath) +// ParseJSON recursively parses JSON data into a map. +func ParseJSON(data []byte) (map[string]interface{}, error) { + var result map[string]interface{} + err := json.Unmarshal(data, &result) if err != nil { - return nil, fmt.Errorf("error reading genesis file: %v", err) + return nil, err + } + return result, nil +} + +// ReadJSONFile reads a JSON file and returns its content as a byte slice. +func ReadJSONFile(filePath string) ([]byte, error) { + file, err := os.Open(filePath) + if err != nil { + return nil, err + } + defer file.Close() + + byteValue, err := ioutil.ReadAll(file) + if err != nil { + return nil, err + } + + return byteValue, nil +} + +func ReadGenesisFile(filePath string) (map[string]interface{}, error) { + jsonData, err := ReadJSONFile(filePath) + if err != nil { + log.Fatalf("Failed to read JSON file: %v", err) } - // Parse the JSON data - var genesisState GenesisState - err = json.Unmarshal(fileData, &genesisState) + parsedData, err := ParseJSON(jsonData) if err != nil { - return nil, fmt.Errorf("error unmarshalling genesis file: %v", err) + log.Fatalf("Failed to parse JSON data: %v", err) + } + + return parsedData, nil +} + +func GetBankBalances(genesis map[string]interface{}) (map[string][]sdk.Coin, error) { + + // Create the map to store balances by address + balanceMap := make(map[string][]sdk.Coin) + + // Unsafe way to access and iterate over app_state -> bank -> balances + appState := genesis["app_state"].(map[string]interface{}) + bank := appState["bank"].(map[string]interface{}) + balances := bank["balances"].([]interface{}) + + for _, res := range balances { + address := res.(map[string]interface{})["address"].(string) + balance := res.(map[string]interface{})["coins"] + + // Create a list to store coins for this address + var coins []sdk.Coin + + for _, coinsRes := range balance.([]interface{}) { + denom := coinsRes.(map[string]interface{})["denom"].(string) + amount := coinsRes.(map[string]interface{})["amount"].(string) + // Convert amount to sdk.Int + amountInt, ok := sdk.NewIntFromString(amount) + if !ok { + panic("Failed to convert amount to sdk.Int: %s" + amount) + } + // Append coin to the list + coin := sdk.Coin{ + Denom: denom, + Amount: amountInt, + } + coins = append(coins, coin) + } + + // Store the list of coins in the map + balanceMap[address] = coins } - return &genesisState, nil + return balanceMap, nil } From 84852d03614617fda720ab95c2dc81dbbd5c9900 Mon Sep 17 00:00:00 2001 From: Jiri Date: Thu, 25 Jul 2024 10:51:18 +0200 Subject: [PATCH 3/9] accounts processing --- app/app.go | 22 +++++------- app/cudos_merge.go | 85 +++++----------------------------------------- 2 files changed, 17 insertions(+), 90 deletions(-) diff --git a/app/app.go b/app/app.go index 98fcdae06..390883c84 100644 --- a/app/app.go +++ b/app/app.go @@ -86,7 +86,7 @@ import ( icahostkeeper "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/host/keeper" icahosttypes "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/host/types" icatypes "github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/types" - transfer "github.com/cosmos/ibc-go/v3/modules/apps/transfer" + "github.com/cosmos/ibc-go/v3/modules/apps/transfer" ibctransferkeeper "github.com/cosmos/ibc-go/v3/modules/apps/transfer/keeper" ibctransfertypes "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types" ibc "github.com/cosmos/ibc-go/v3/modules/core" @@ -755,22 +755,16 @@ func (app *App) RegisterUpgradeHandlers(cfg module.Configurator) { return app.mm.RunMigrations(ctx, cfg, fromVM) }) - app.UpgradeKeeper.SetUpgradeHandler("v0.11.4", func(ctx sdk.Context, plan upgradetypes.Plan, fromVM module.VersionMap) (module.VersionMap, error) { - // "fetchd-v0.11.3-8-g929563a" - genesis, err := ReadGenesisFile(app.cudosPath) + app.UpgradeKeeper.SetUpgradeHandler("fetchd-v0.11.3-8-g929563a", func(ctx sdk.Context, plan upgradetypes.Plan, fromVM module.VersionMap) (module.VersionMap, error) { + + genesisState, _, err := genutiltypes.GenesisStateFromGenFile(app.cudosPath) if err != nil { - return nil, err + panic(fmt.Sprintf("failed to unmarshal genesis state: %w", err)) } - // Create the map to store balances by address - balanceMap, _ := GetBankBalances(genesis) - - // Print the balance map - for addr, coins := range balanceMap { - fmt.Printf("Address: %s\n", addr) - for _, coin := range coins { - fmt.Printf(" Coin: %s, Amount: %s\n", coin.Denom, coin.Amount.String()) - } + err = ProcessAccounts(app, genesisState) + if err != nil { + panic(fmt.Sprintf("failed process accounts: %w", err)) } manifest := NewUpgradeManifest() diff --git a/app/cudos_merge.go b/app/cudos_merge.go index b5d074cbc..1a70a41e4 100644 --- a/app/cudos_merge.go +++ b/app/cudos_merge.go @@ -2,88 +2,21 @@ package app import ( "encoding/json" - sdk "github.com/cosmos/cosmos-sdk/types" - "io/ioutil" - "log" - "os" + "fmt" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" ) -// ParseJSON recursively parses JSON data into a map. -func ParseJSON(data []byte) (map[string]interface{}, error) { - var result map[string]interface{} - err := json.Unmarshal(data, &result) - if err != nil { - return nil, err - } - return result, nil -} - -// ReadJSONFile reads a JSON file and returns its content as a byte slice. -func ReadJSONFile(filePath string) ([]byte, error) { - file, err := os.Open(filePath) - if err != nil { - return nil, err - } - defer file.Close() - - byteValue, err := ioutil.ReadAll(file) - if err != nil { - return nil, err - } - - return byteValue, nil -} +func ProcessAccounts(app *App, genesisState map[string]json.RawMessage) error { -func ReadGenesisFile(filePath string) (map[string]interface{}, error) { - jsonData, err := ReadJSONFile(filePath) - if err != nil { - log.Fatalf("Failed to read JSON file: %v", err) - } + authGenState := authtypes.GetGenesisStateFromAppState(app.appCodec, genesisState) - parsedData, err := ParseJSON(jsonData) + accs, err := authtypes.UnpackAccounts(authGenState.Accounts) if err != nil { - log.Fatalf("Failed to parse JSON data: %v", err) + panic(fmt.Sprintf("failed to get accounts from any: %w", err)) } - return parsedData, nil -} - -func GetBankBalances(genesis map[string]interface{}) (map[string][]sdk.Coin, error) { - - // Create the map to store balances by address - balanceMap := make(map[string][]sdk.Coin) - - // Unsafe way to access and iterate over app_state -> bank -> balances - appState := genesis["app_state"].(map[string]interface{}) - bank := appState["bank"].(map[string]interface{}) - balances := bank["balances"].([]interface{}) - - for _, res := range balances { - address := res.(map[string]interface{})["address"].(string) - balance := res.(map[string]interface{})["coins"] - - // Create a list to store coins for this address - var coins []sdk.Coin - - for _, coinsRes := range balance.([]interface{}) { - denom := coinsRes.(map[string]interface{})["denom"].(string) - amount := coinsRes.(map[string]interface{})["amount"].(string) - // Convert amount to sdk.Int - amountInt, ok := sdk.NewIntFromString(amount) - if !ok { - panic("Failed to convert amount to sdk.Int: %s" + amount) - } - // Append coin to the list - coin := sdk.Coin{ - Denom: denom, - Amount: amountInt, - } - coins = append(coins, coin) - } - - // Store the list of coins in the map - balanceMap[address] = coins - } + // Handle accounts - return balanceMap, nil + print(accs) + return nil } From 0e3125e35beaadfd67f199a73d22ab1d40404bc7 Mon Sep 17 00:00:00 2001 From: Jiri Date: Thu, 25 Jul 2024 14:04:00 +0200 Subject: [PATCH 4/9] auth processing --- app/app.go | 25 +- app/cudos_merge.go | 386 ++++++++++++++++++++++++++- app/upgrade_v_11_4_manifest.go | 14 + app/upgrade_v_11_4_network_config.go | 3 + go.mod | 2 + go.sum | 1 + 6 files changed, 416 insertions(+), 15 deletions(-) diff --git a/app/app.go b/app/app.go index 390883c84..2203ce534 100644 --- a/app/app.go +++ b/app/app.go @@ -1,6 +1,7 @@ package app import ( + "encoding/json" "fmt" "io" "net/http" @@ -757,21 +758,27 @@ func (app *App) RegisterUpgradeHandlers(cfg module.Configurator) { app.UpgradeKeeper.SetUpgradeHandler("fetchd-v0.11.3-8-g929563a", func(ctx sdk.Context, plan upgradetypes.Plan, fromVM module.VersionMap) (module.VersionMap, error) { - genesisState, _, err := genutiltypes.GenesisStateFromGenFile(app.cudosPath) - if err != nil { - panic(fmt.Sprintf("failed to unmarshal genesis state: %w", err)) + manifest := NewUpgradeManifest() + + networkInfo, ok := NetworkInfos[ctx.ChainID()] + if !ok { + panic("Network info not found for chain id: " + ctx.ChainID()) } - err = ProcessAccounts(app, genesisState) + _, genDoc, err := genutiltypes.GenesisStateFromGenFile(app.cudosPath) if err != nil { - panic(fmt.Sprintf("failed process accounts: %w", err)) + panic(fmt.Sprintf("failed to unmarshal genesis state: %w", err)) } - manifest := NewUpgradeManifest() + // unmarshal the app state + var jsonData map[string]interface{} + if err = json.Unmarshal(genDoc.AppState, &jsonData); err != nil { + panic(fmt.Sprintf("failed to unmarshal app state: %w", err)) + } - networkInfo, ok := NetworkInfos[ctx.ChainID()] - if !ok { - panic("Network info not found for chain id: " + ctx.ChainID()) + err = ProcessAccounts(ctx, app, jsonData, networkInfo, manifest) + if err != nil { + panic(fmt.Sprintf("failed process accounts: %w", err)) } // Perform ASI upgrade tasks diff --git a/app/cudos_merge.go b/app/cudos_merge.go index 1a70a41e4..311a027d8 100644 --- a/app/cudos_merge.go +++ b/app/cudos_merge.go @@ -1,22 +1,396 @@ package app import ( - "encoding/json" + "encoding/base64" "fmt" + "github.com/btcsuite/btcutil/bech32" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + ibctransfertypes "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types" + ibccore "github.com/cosmos/ibc-go/v3/modules/core/24-host" + "strconv" ) -func ProcessAccounts(app *App, genesisState map[string]json.RawMessage) error { +const ( + Bech32Chars = "qpzry9x8gf2tvdw0s3jn54khce6mua7l" + AddrDataLength = 32 + WasmAddrDataLength = 52 + MaxAddrDataLength = 100 + AddrChecksumLength = 6 - authGenState := authtypes.GetGenesisStateFromAppState(app.appCodec, genesisState) + AccAddressPrefix = "" + ValAddressPrefix = "valoper" + ConsAddressPrefix = "valcons" - accs, err := authtypes.UnpackAccounts(authGenState.Accounts) + NewAddrPrefix = "fetch" + OldAddrPrefix = "cudos" + + FlagGenesisTime = "genesis-time" + + ModuleAccount = "/cosmos.auth.v1beta1.ModuleAccount" + BaseAccount = "/cosmos.auth.v1beta1.BaseAccount" +) + +func convertAddressToFetch(addr string, addressPrefix string) (string, error) { + _, decodedAddrData, err := bech32.Decode(addr) + if err != nil { + return "", err + } + + newAddress, err := bech32.Encode(NewAddrPrefix+addressPrefix, decodedAddrData) + if err != nil { + return "", err + } + + err = sdk.VerifyAddressFormat(decodedAddrData) + if err != nil { + return "", err + } + + return newAddress, nil +} + +func convertAddressToRaw(addr string) (sdk.AccAddress, error) { + _, decodedAddrData, err := bech32.Decode(addr) if err != nil { - panic(fmt.Sprintf("failed to get accounts from any: %w", err)) + return nil, err + } + + return decodedAddrData, nil +} + +func getGenesisAccountSequenceMap(accounts []interface{}) *map[string]int { + accountMap := make(map[string]int) + + for _, acc := range accounts { + accMap := acc.(map[string]interface{}) + accType := accMap["@type"] + + accData := acc + if accType == ModuleAccount { + accData = accMap["base_account"] + } + + accDataMap := accData.(map[string]interface{}) + addr := accDataMap["address"].(string) + sequence := accDataMap["sequence"].(string) + + sequenceInt, ok := strconv.Atoi(sequence) + if ok != nil { + panic("getGenesisAccountSequenceMap: failed to convert sequence to int") + } + accountMap[addr] = sequenceInt + } + + return &accountMap +} + +func getGenesisBalancesMap(balances []interface{}) *map[string]int { + balanceMap := make(map[string]int) + + for i, balance := range balances { + addr := balance.(map[string]interface{})["address"] + if addr == nil { + fmt.Println(balance) + } + addrStr := addr.(string) + balanceMap[addrStr] = i + } + + return &balanceMap +} + +func getCoinsFromInterfaceSlice(data interface{}) sdk.Coins { + balance := data.(map[string]interface{})["coins"] + var balanceCoins sdk.Coins + + for _, coin := range balance.([]interface{}) { + coinData := coin.(map[string]interface{}) + coinDenom := coinData["denom"].(string) + coinAmount, ok := sdk.NewIntFromString(coinData["amount"].(string)) + if !ok { + panic("ibc withdraw: failed to convert coin amount to int") + } + balanceCoins = append(balanceCoins, sdk.NewCoin(coinDenom, coinAmount)) + } + + balanceCoins = sdk.NewCoins(balanceCoins...) + return balanceCoins +} + +func getInterfaceSliceFromCoins(coins sdk.Coins) []interface{} { + var balance []interface{} + for _, coin := range coins { + balance = append(balance, map[string]interface{}{ + "denom": coin.Denom, + "amount": coin.Amount.String(), + }) + } + return balance +} + +func GenesisUpgradeWithdrawIBCChannelsBalances(jsonData map[string]interface{}, networkInfo NetworkConfig, manifest *UpgradeManifest) { + if networkInfo.IbcTargetAddr == "" { + return + } + + bank := jsonData[banktypes.ModuleName].(map[string]interface{}) + balances := bank["balances"].([]interface{}) + balanceMap := getGenesisBalancesMap(balances) + ibcWithdrawalAddress := networkInfo.IbcTargetAddr + + manifest.IBC = &UpgradeIBCTransfers{ + To: ibcWithdrawalAddress, + } + withdrawalBalanceIdx, ok := (*balanceMap)[ibcWithdrawalAddress] + if !ok { + panic("failed to find ibc withdrawal address in genesis balances") + } + + ibc := jsonData[ibccore.ModuleName].(map[string]interface{}) + channelGenesis := ibc["channel_genesis"].(map[string]interface{}) + ibcChannels := channelGenesis["channels"].([]interface{}) + + for _, channel := range ibcChannels { + channelMap := channel.(map[string]interface{}) + channelId := channelMap["channel_id"].(string) + portId := channelMap["port_id"].(string) + + // close channel + channelMap["state"] = "STATE_CLOSED" + + rawAddr := ibctransfertypes.GetEscrowAddress(portId, channelId) + channelAddr, err := sdk.Bech32ifyAddressBytes(OldAddrPrefix+AccAddressPrefix, rawAddr) + if err != nil { + panic(err) + } + + balanceIdx, ok := (*balanceMap)[channelAddr] + if !ok { + // channel address not found in genesis balances + continue + } + + channelBalanceCoins := getCoinsFromInterfaceSlice(balances[balanceIdx]) + withdrawalBalanceCoins := getCoinsFromInterfaceSlice(balances[withdrawalBalanceIdx]) + + // add channel balance to withdrawal balance + newWithdrawalBalanceCoins := withdrawalBalanceCoins.Add(channelBalanceCoins...) + balances[withdrawalBalanceIdx].(map[string]interface{})["coins"] = getInterfaceSliceFromCoins(newWithdrawalBalanceCoins) + + // zero out the channel balance + balances[balanceIdx].(map[string]interface{})["coins"] = []interface{}{} + + manifest.IBC.Transfers = append(manifest.IBC.Transfers, UpgradeIBCTransfer{From: channelAddr, ChannelID: fmt.Sprintf("%s/%s", portId, channelId), Amount: channelBalanceCoins}) + manifest.IBC.AggregatedTransferredAmount = manifest.IBC.AggregatedTransferredAmount.Add(channelBalanceCoins...) + manifest.IBC.NumberOfTransfers += 1 + } +} + +func GetIBCAccountAddresses(jsonData map[string]interface{}) (map[string]bool, error) { + ibcAccountSet := make(map[string]bool) + + ibc, ok := jsonData[ibccore.ModuleName].(map[string]interface{}) + if !ok { + return nil, fmt.Errorf("IBC module data not found in genesis") + } + + channelGenesis, ok := ibc["channel_genesis"].(map[string]interface{}) + if !ok { + return nil, fmt.Errorf("channel genesis data not found in IBC module") + } + + ibcChannels, ok := channelGenesis["channels"].([]interface{}) + if !ok { + return nil, fmt.Errorf("channels data not found in channel genesis") + } + + for _, channel := range ibcChannels { + channelMap, ok := channel.(map[string]interface{}) + if !ok { + return nil, fmt.Errorf("invalid channel format in genesis") + } + + channelId, ok := channelMap["channel_id"].(string) + if !ok { + return nil, fmt.Errorf("channel_id not found or invalid in channel") + } + + portId, ok := channelMap["port_id"].(string) + if !ok { + return nil, fmt.Errorf("port_id not found or invalid in channel") + } + + rawAddr := ibctransfertypes.GetEscrowAddress(portId, channelId) + channelAddr, err := sdk.Bech32ifyAddressBytes(OldAddrPrefix+AccAddressPrefix, rawAddr) + if err != nil { + return nil, err + } + + ibcAccountSet[channelAddr] = true + } + + return ibcAccountSet, nil +} + +func GetWasmContractAccounts(jsonData map[string]interface{}) (map[string]bool, error) { + contractAccountSet := make(map[string]bool) + + // Navigate to the "wasm" module + wasm, ok := jsonData["wasm"].(map[string]interface{}) + if !ok { + return nil, fmt.Errorf("wasm module data not found in genesis") + } + + // Navigate to the "contracts" section + contracts, ok := wasm["contracts"].([]interface{}) + if !ok { + return nil, fmt.Errorf("contracts data not found in wasm module") + } + + // Iterate over each contract to get the "contract_address" + for _, contract := range contracts { + contractMap, ok := contract.(map[string]interface{}) + if !ok { + return nil, fmt.Errorf("invalid contract format in genesis") + } + + contractAddr, ok := contractMap["contract_address"].(string) + if !ok { + return nil, fmt.Errorf("contract_address not found or invalid in contract") + } + + contractAccountSet[contractAddr] = true + } + + return contractAccountSet, nil +} + +func decodePubKeyFromMap(pubKeyMap map[string]interface{}) (cryptotypes.PubKey, error) { + keyType, ok := pubKeyMap["@type"].(string) + if !ok { + return nil, fmt.Errorf("@type field not found or is not a string in pubKeyMap") + } + + keyStr, ok := pubKeyMap["key"].(string) + if !ok { + return nil, fmt.Errorf("key field not found or is not a string in pubKeyMap") + } + + keyBytes, err := base64.StdEncoding.DecodeString(keyStr) + if err != nil { + return nil, fmt.Errorf("failed to decode base64 key: %w", err) + } + + switch keyType { + case "/cosmos.crypto.secp256k1.PubKey": + // Ensure the byte slice is the correct length for a secp256k1 public key + if len(keyBytes) != secp256k1.PubKeySize { + return nil, fmt.Errorf("invalid pubkey length: got %d, expected %d", len(keyBytes), secp256k1.PubKeySize) + } + + pubKey := secp256k1.PubKey{ + Key: keyBytes, + } + return &pubKey, nil + default: + return nil, fmt.Errorf("unsupported key type: %s", keyType) + } +} + +func createNewAccount(ctx sdk.Context, app *App, accDataMap map[string]interface{}) error { + // Get raw address + addr := accDataMap["address"].(string) + accRawAddr, err := convertAddressToRaw(addr) + if err != nil { + return err + } + + // Create new account + newAcc := app.AccountKeeper.NewAccountWithAddress(ctx, accRawAddr) + + // Set pubkey if present + if pk, ok := accDataMap["pub_key"]; ok { + decodedPk, err := decodePubKeyFromMap(pk.(map[string]interface{})) + if err != nil { + return err + } + newAcc.SetPubKey(decodedPk) + } + + app.AccountKeeper.SetAccount(ctx, newAcc) + return nil +} + +func ProcessAccounts(ctx sdk.Context, app *App, jsonData map[string]interface{}, networkInfo NetworkConfig, manifest *UpgradeManifest) error { + + auth := jsonData[authtypes.ModuleName].(map[string]interface{}) + accounts := auth["accounts"].([]interface{}) + + //accountSequenceMap := getGenesisAccountSequenceMap(accounts) + + ibcAccountsSet, err := GetIBCAccountAddresses(jsonData) + if err != nil { + return err + } + + contractAccountsSet, err := GetWasmContractAccounts(jsonData) + if err != nil { + return err } // Handle accounts + for _, acc := range accounts { + accMap := acc.(map[string]interface{}) + accType := accMap["@type"] + + accData := acc + if accType == ModuleAccount { + accData = accMap["base_account"] + } + + accDataMap := accData.(map[string]interface{}) + addr := accDataMap["address"].(string) + + if addr == "cudos1a53udazy8ayufvy0s434pfwjcedzqv34hat64m" { + print(addr, accType) + } + + if ibcAccountsSet[addr] { + // Handle IBC account + continue + } + + if contractAccountsSet[addr] { + // Skip contract account + continue + } + + if accType == BaseAccount { + accRawAddr, err := convertAddressToRaw(addr) + if err != nil { + return err + } + + // Check for collision + existingAccount := app.AccountKeeper.GetAccount(ctx, accRawAddr) + if existingAccount != nil { + // Handle collision + return fmt.Errorf("account already exists: %s", addr) + } + + // Handle regular migration + createNewAccount(ctx, app, accDataMap) + + } else if accType == ModuleAccount { + // Skip module accounts + continue + } + + } - print(accs) return nil } diff --git a/app/upgrade_v_11_4_manifest.go b/app/upgrade_v_11_4_manifest.go index b8060d6ee..b700b63c3 100644 --- a/app/upgrade_v_11_4_manifest.go +++ b/app/upgrade_v_11_4_manifest.go @@ -13,6 +13,7 @@ const manifestFilenameBase = "upgrade_manifest.json" type UpgradeManifest struct { Reconciliation *UpgradeReconciliation `json:"reconciliation,omitempty"` Contracts *Contracts `json:"contracts,omitempty"` + IBC *UpgradeIBCTransfers `json:"ibc,omitempty"` } func NewUpgradeManifest() *UpgradeManifest { @@ -45,6 +46,19 @@ type ValueUpdate struct { To string `json:"to"` } +type UpgradeIBCTransfer struct { + From string `json:"from"` + ChannelID string `json:"channel_id"` + Amount types.Coins `json:"amount"` +} + +type UpgradeIBCTransfers struct { + Transfers []UpgradeIBCTransfer `json:"transfer"` + To string `json:"to"` + AggregatedTransferredAmount types.Coins `json:"aggregated_transferred_amount"` + NumberOfTransfers int `json:"number_of_transfers"` +} + type UpgradeReconciliation struct { Transfers *UpgradeReconciliationTransfers `json:"transfers,omitempty"` ContractState *UpgradeReconciliationContractState `json:"contract_state,omitempty"` diff --git a/app/upgrade_v_11_4_network_config.go b/app/upgrade_v_11_4_network_config.go index 9cac49521..a0c0cb869 100644 --- a/app/upgrade_v_11_4_network_config.go +++ b/app/upgrade_v_11_4_network_config.go @@ -2,6 +2,7 @@ package app var NetworkInfos = map[string]NetworkConfig{ "fetchhub-4": { + IbcTargetAddr: "fetch1zydegef0z6lz4gamamzlnu52ethe8xnm0xe5fkyrgwumsh9pplus5he63f", ReconciliationInfo: &ReconciliationInfo{ TargetAddress: "fetch1tynmzk68pq6kzawqffrqdhquq475gw9ccmlf9gk24mxjjy6ugl3q70aeyd", InputCSVRecords: readInputReconciliationData(reconciliationData), @@ -30,6 +31,7 @@ var NetworkInfos = map[string]NetworkConfig{ }, "dorado-1": { + IbcTargetAddr: "fetch18rlg4hs2p03yuvvdu389pe65qa789asmyqsfftdxsh2qjfwmt94qmrf7g0", ReconciliationInfo: &ReconciliationInfo{ TargetAddress: "fetch1g5ur2wc5xnlc7sw9wd895lw7mmxz04r5syj3s6ew8md6pvwuweqqavkgt0", InputCSVRecords: readInputReconciliationData(reconciliationDataTestnet), @@ -57,6 +59,7 @@ var NetworkInfos = map[string]NetworkConfig{ type NetworkConfig struct { ReconciliationInfo *ReconciliationInfo Contracts *ContractSet + IbcTargetAddr string } type ReconciliationInfo struct { diff --git a/go.mod b/go.mod index 497696111..4871c7b2f 100644 --- a/go.mod +++ b/go.mod @@ -16,6 +16,8 @@ require ( github.com/tendermint/tm-db v0.6.7 ) +require github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce + require ( filippo.io/edwards25519 v1.0.0-beta.2 // indirect github.com/99designs/keyring v1.1.6 // indirect diff --git a/go.sum b/go.sum index ecf9d03f9..e057dea6b 100644 --- a/go.sum +++ b/go.sum @@ -122,6 +122,7 @@ github.com/btcsuite/btcutil v0.0.0-20190207003914-4c204d697803/go.mod h1:+5NJ2+q github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= github.com/btcsuite/btcutil v1.0.2/go.mod h1:j9HUFwoQRsZL3V4n+qG+CUnEGHOarIxfC3Le2Yhbcts= github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce h1:YtWJF7RHm2pYCvA5t0RPmAaLUhREsKuKd+SLhxFbFeQ= +github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce/go.mod h1:0DVlHczLPewLcPGEIeUEzfOJhqGPQ0mJJRDBtD307+o= github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I= From 51ff17a970d55bb0b947a93b1a21fce54e38cef0 Mon Sep 17 00:00:00 2001 From: Jiri Date: Thu, 25 Jul 2024 14:36:21 +0200 Subject: [PATCH 5/9] Removed redundand line --- app/cudos_merge.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/app/cudos_merge.go b/app/cudos_merge.go index 311a027d8..7805e3c05 100644 --- a/app/cudos_merge.go +++ b/app/cudos_merge.go @@ -355,10 +355,6 @@ func ProcessAccounts(ctx sdk.Context, app *App, jsonData map[string]interface{}, accDataMap := accData.(map[string]interface{}) addr := accDataMap["address"].(string) - if addr == "cudos1a53udazy8ayufvy0s434pfwjcedzqv34hat64m" { - print(addr, accType) - } - if ibcAccountsSet[addr] { // Handle IBC account continue From bed9f4bc25d592ab10b7bbffb10f1033c66c7784 Mon Sep 17 00:00:00 2001 From: Jiri Date: Mon, 29 Jul 2024 14:56:21 +0200 Subject: [PATCH 6/9] Balances conversion --- app/app.go | 7 +++- app/cudos_merge.go | 102 ++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 97 insertions(+), 12 deletions(-) diff --git a/app/app.go b/app/app.go index 2203ce534..eb15b66b7 100644 --- a/app/app.go +++ b/app/app.go @@ -776,7 +776,12 @@ func (app *App) RegisterUpgradeHandlers(cfg module.Configurator) { panic(fmt.Sprintf("failed to unmarshal app state: %w", err)) } - err = ProcessAccounts(ctx, app, jsonData, networkInfo, manifest) + bank := jsonData[banktypes.ModuleName].(map[string]interface{}) + balances := bank["balances"].([]interface{}) + + convertedBalancesMap := getConvertedGenesisBalancesMap(balances) + + err = ProcessAccounts(ctx, app, jsonData, networkInfo, manifest, convertedBalancesMap) if err != nil { panic(fmt.Sprintf("failed process accounts: %w", err)) } diff --git a/app/cudos_merge.go b/app/cudos_merge.go index 7805e3c05..86899d294 100644 --- a/app/cudos_merge.go +++ b/app/cudos_merge.go @@ -8,9 +8,11 @@ import ( cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + authvesting "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" ibctransfertypes "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types" ibccore "github.com/cosmos/ibc-go/v3/modules/core/24-host" + "math/big" "strconv" ) @@ -28,12 +30,21 @@ const ( NewAddrPrefix = "fetch" OldAddrPrefix = "cudos" + ConvertedDenom = "afet" + + MergeTime = 123456 // Epoch time of merge + VestingPeriod = 3 * 30 * 24 * 60 * 60 // 3 months period + FlagGenesisTime = "genesis-time" ModuleAccount = "/cosmos.auth.v1beta1.ModuleAccount" BaseAccount = "/cosmos.auth.v1beta1.BaseAccount" ) +var BalanceDivisionConstants = map[string]int{ + "acudos": 11, +} + func convertAddressToFetch(addr string, addressPrefix string) (string, error) { _, decodedAddrData, err := bech32.Decode(addr) if err != nil { @@ -103,6 +114,63 @@ func getGenesisBalancesMap(balances []interface{}) *map[string]int { return &balanceMap } +func getConvertedGenesisBalancesMap(balances []interface{}) map[string]sdk.Coins { + balanceMap := make(map[string]sdk.Coins) + + for _, balance := range balances { + + addr := balance.(map[string]interface{})["address"] + if addr == nil { + fmt.Println(balance) + } + addrStr := addr.(string) + + var resBalance sdk.Coins + + coins := balance.(map[string]interface{})["coins"] + for _, coin := range coins.([]interface{}) { + + amount := coin.(map[string]interface{})["amount"].(string) + + // Convert amount to big.Int + amountInt := new(big.Int) + _, ok := amountInt.SetString(amount, 10) + if !ok { + panic("Failed to convert amount to big.Int") + } + + denom := coin.(map[string]interface{})["denom"].(string) + + if divisionConst, ok := BalanceDivisionConstants[denom]; ok { + divisionConstBigInt := big.NewInt(int64(divisionConst)) + newAmount := new(big.Int).Div(amountInt, divisionConstBigInt) + + sdkCoin := sdk.NewCoin(ConvertedDenom, sdk.NewIntFromBigInt(newAmount)) + resBalance = resBalance.Add(sdkCoin) + + } else { + print("Unknown denom", denom) + // Just add without conversion + + newAmount, ok := sdk.NewIntFromString(amount) + if !ok { + panic("Failed to convert amount to big.Int") + } + + sdkCoin := sdk.NewCoin(denom, newAmount) + + resBalance = resBalance.Add(sdkCoin) + + } + + } + + balanceMap[addrStr] = resBalance + } + + return balanceMap +} + func getCoinsFromInterfaceSlice(data interface{}) sdk.Coins { balance := data.(map[string]interface{})["coins"] var balanceCoins sdk.Coins @@ -301,7 +369,7 @@ func decodePubKeyFromMap(pubKeyMap map[string]interface{}) (cryptotypes.PubKey, } } -func createNewAccount(ctx sdk.Context, app *App, accDataMap map[string]interface{}) error { +func createNewVestingAccount(ctx sdk.Context, app *App, accDataMap map[string]interface{}, vestedCoins sdk.Coins, startTime int64, endTime int64) error { // Get raw address addr := accDataMap["address"].(string) accRawAddr, err := convertAddressToRaw(addr) @@ -309,23 +377,32 @@ func createNewAccount(ctx sdk.Context, app *App, accDataMap map[string]interface return err } - // Create new account - newAcc := app.AccountKeeper.NewAccountWithAddress(ctx, accRawAddr) - // Set pubkey if present + var pubKey cryptotypes.PubKey if pk, ok := accDataMap["pub_key"]; ok { - decodedPk, err := decodePubKeyFromMap(pk.(map[string]interface{})) - if err != nil { - return err + if pk != nil { + pubKey, err = decodePubKeyFromMap(pk.(map[string]interface{})) + if err != nil { + return err + } } - newAcc.SetPubKey(decodedPk) } - app.AccountKeeper.SetAccount(ctx, newAcc) + // Create new account + + newAccNumber := app.AccountKeeper.GetNextAccountNumber(ctx) + newBaseAccount := authtypes.NewBaseAccount(accRawAddr, pubKey, newAccNumber, 0) + + // TODO: Fill balances + newBaseVestingAcc := authvesting.NewBaseVestingAccount(newBaseAccount, vestedCoins, endTime) + newContinuousVestingAcc := authvesting.NewContinuousVestingAccountRaw(newBaseVestingAcc, startTime) + + app.AccountKeeper.SetAccount(ctx, newContinuousVestingAcc) + return nil } -func ProcessAccounts(ctx sdk.Context, app *App, jsonData map[string]interface{}, networkInfo NetworkConfig, manifest *UpgradeManifest) error { +func ProcessAccounts(ctx sdk.Context, app *App, jsonData map[string]interface{}, networkInfo NetworkConfig, manifest *UpgradeManifest, convertedBalancesMap map[string]sdk.Coins) error { auth := jsonData[authtypes.ModuleName].(map[string]interface{}) accounts := auth["accounts"].([]interface{}) @@ -379,7 +456,10 @@ func ProcessAccounts(ctx sdk.Context, app *App, jsonData map[string]interface{}, } // Handle regular migration - createNewAccount(ctx, app, accDataMap) + + // Create vesting account + newBalance := convertedBalancesMap[addr] + createNewVestingAccount(ctx, app, accDataMap, newBalance, MergeTime, MergeTime+VestingPeriod) } else if accType == ModuleAccount { // Skip module accounts From 488cbc7e200a230c0c97e6b27119e15ee4f6edf9 Mon Sep 17 00:00:00 2001 From: Jiri Date: Tue, 30 Jul 2024 13:38:47 +0200 Subject: [PATCH 7/9] Minting --- app/app.go | 2 ++ app/cudos_merge.go | 21 +++++++++++++++++++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/app/app.go b/app/app.go index eb15b66b7..0eed9b0f2 100644 --- a/app/app.go +++ b/app/app.go @@ -786,6 +786,8 @@ func (app *App) RegisterUpgradeHandlers(cfg module.Configurator) { panic(fmt.Sprintf("failed process accounts: %w", err)) } + panic("Debug interruption") + // Perform ASI upgrade tasks err = app.PerformASIUpgradeTasks(ctx, &networkInfo, manifest) if err != nil { diff --git a/app/cudos_merge.go b/app/cudos_merge.go index 86899d294..562adfd6b 100644 --- a/app/cudos_merge.go +++ b/app/cudos_merge.go @@ -10,6 +10,7 @@ import ( authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" authvesting "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" ibctransfertypes "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types" ibccore "github.com/cosmos/ibc-go/v3/modules/core/24-host" "math/big" @@ -149,7 +150,7 @@ func getConvertedGenesisBalancesMap(balances []interface{}) map[string]sdk.Coins resBalance = resBalance.Add(sdkCoin) } else { - print("Unknown denom", denom) + println("Unknown denom: ", denom) // Just add without conversion newAmount, ok := sdk.NewIntFromString(amount) @@ -393,7 +394,6 @@ func createNewVestingAccount(ctx sdk.Context, app *App, accDataMap map[string]in newAccNumber := app.AccountKeeper.GetNextAccountNumber(ctx) newBaseAccount := authtypes.NewBaseAccount(accRawAddr, pubKey, newAccNumber, 0) - // TODO: Fill balances newBaseVestingAcc := authvesting.NewBaseVestingAccount(newBaseAccount, vestedCoins, endTime) newContinuousVestingAcc := authvesting.NewContinuousVestingAccountRaw(newBaseVestingAcc, startTime) @@ -402,6 +402,14 @@ func createNewVestingAccount(ctx sdk.Context, app *App, accDataMap map[string]in return nil } +func mintToAccount(ctx sdk.Context, app *App, address sdk.AccAddress, newCoins sdk.Coins) error { + + app.MintKeeper.MintCoins(ctx, newCoins) + app.BankKeeper.SendCoinsFromModuleToAccount(ctx, minttypes.ModuleName, address, newCoins) + + return nil +} + func ProcessAccounts(ctx sdk.Context, app *App, jsonData map[string]interface{}, networkInfo NetworkConfig, manifest *UpgradeManifest, convertedBalancesMap map[string]sdk.Coins) error { auth := jsonData[authtypes.ModuleName].(map[string]interface{}) @@ -460,8 +468,17 @@ func ProcessAccounts(ctx sdk.Context, app *App, jsonData map[string]interface{}, // Create vesting account newBalance := convertedBalancesMap[addr] createNewVestingAccount(ctx, app, accDataMap, newBalance, MergeTime, MergeTime+VestingPeriod) + err = mintToAccount(ctx, app, accRawAddr, newBalance) + if err != nil { + return err + } } else if accType == ModuleAccount { + + newBalance := convertedBalancesMap[addr] + + println(acc.(map[string]interface{})["name"].(string), newBalance.String()) + // Skip module accounts continue } From b70ca3b41babc73bcc390c4d4adfea6f4a3f9e28 Mon Sep 17 00:00:00 2001 From: Jiri Date: Wed, 31 Jul 2024 11:46:35 +0200 Subject: [PATCH 8/9] Account handling improvements --- app/cudos_merge.go | 137 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 116 insertions(+), 21 deletions(-) diff --git a/app/cudos_merge.go b/app/cudos_merge.go index 562adfd6b..86fa4e291 100644 --- a/app/cudos_merge.go +++ b/app/cudos_merge.go @@ -3,10 +3,12 @@ package app import ( "encoding/base64" "fmt" - "github.com/btcsuite/btcutil/bech32" + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + "github.com/cosmos/cosmos-sdk/crypto/keys/multisig" "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/bech32" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" authvesting "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" @@ -47,12 +49,12 @@ var BalanceDivisionConstants = map[string]int{ } func convertAddressToFetch(addr string, addressPrefix string) (string, error) { - _, decodedAddrData, err := bech32.Decode(addr) + _, decodedAddrData, err := bech32.DecodeAndConvert(addr) if err != nil { return "", err } - newAddress, err := bech32.Encode(NewAddrPrefix+addressPrefix, decodedAddrData) + newAddress, err := bech32.ConvertAndEncode(NewAddrPrefix+addressPrefix, decodedAddrData) if err != nil { return "", err } @@ -66,7 +68,12 @@ func convertAddressToFetch(addr string, addressPrefix string) (string, error) { } func convertAddressToRaw(addr string) (sdk.AccAddress, error) { - _, decodedAddrData, err := bech32.Decode(addr) + prefix, decodedAddrData, err := bech32.DecodeAndConvert(addr) + + if prefix != OldAddrPrefix { + println("Unknown prefix: ", prefix) + } + if err != nil { return nil, err } @@ -344,18 +351,18 @@ func decodePubKeyFromMap(pubKeyMap map[string]interface{}) (cryptotypes.PubKey, return nil, fmt.Errorf("@type field not found or is not a string in pubKeyMap") } - keyStr, ok := pubKeyMap["key"].(string) - if !ok { - return nil, fmt.Errorf("key field not found or is not a string in pubKeyMap") - } - - keyBytes, err := base64.StdEncoding.DecodeString(keyStr) - if err != nil { - return nil, fmt.Errorf("failed to decode base64 key: %w", err) - } - switch keyType { case "/cosmos.crypto.secp256k1.PubKey": + keyStr, ok := pubKeyMap["key"].(string) + if !ok { + return nil, fmt.Errorf("key field not found or is not a string in pubKeyMap") + } + + keyBytes, err := base64.StdEncoding.DecodeString(keyStr) + if err != nil { + return nil, fmt.Errorf("failed to decode base64 key: %w", err) + } + // Ensure the byte slice is the correct length for a secp256k1 public key if len(keyBytes) != secp256k1.PubKeySize { return nil, fmt.Errorf("invalid pubkey length: got %d, expected %d", len(keyBytes), secp256k1.PubKeySize) @@ -365,17 +372,68 @@ func decodePubKeyFromMap(pubKeyMap map[string]interface{}) (cryptotypes.PubKey, Key: keyBytes, } return &pubKey, nil + + case "/cosmos.crypto.ed25519.PubKey": + keyStr, ok := pubKeyMap["key"].(string) + if !ok { + return nil, fmt.Errorf("key field not found or is not a string in pubKeyMap") + } + + keyBytes, err := base64.StdEncoding.DecodeString(keyStr) + if err != nil { + return nil, fmt.Errorf("failed to decode base64 key: %w", err) + } + + // Ensure the byte slice is the correct length for an ed25519 public key + if len(keyBytes) != ed25519.PubKeySize { + return nil, fmt.Errorf("invalid pubkey length: got %d, expected %d", len(keyBytes), ed25519.PubKeySize) + } + + pubKey := ed25519.PubKey{ + Key: keyBytes, + } + return &pubKey, nil + + case "/cosmos.crypto.multisig.LegacyAminoPubKey": + threshold, ok := pubKeyMap["threshold"].(float64) // JSON numbers are float64 + if !ok { + return nil, fmt.Errorf("threshold field not found or is not a number in pubKeyMap") + } + + pubKeysInterface, ok := pubKeyMap["public_keys"].([]interface{}) + if !ok { + return nil, fmt.Errorf("public_keys field not found or is not an array in pubKeyMap") + } + + var pubKeys []cryptotypes.PubKey + for _, pubKeyInterface := range pubKeysInterface { + pubKeyMap, ok := pubKeyInterface.(map[string]interface{}) + if !ok { + return nil, fmt.Errorf("public key entry is not a valid map") + } + + pubKey, err := decodePubKeyFromMap(pubKeyMap) + if err != nil { + return nil, fmt.Errorf("failed to decode public key: %w", err) + } + + pubKeys = append(pubKeys, pubKey) + } + + legacyAminoPubKey := multisig.NewLegacyAminoPubKey(int(threshold), pubKeys) + return legacyAminoPubKey, nil + default: return nil, fmt.Errorf("unsupported key type: %s", keyType) } } -func createNewVestingAccount(ctx sdk.Context, app *App, accDataMap map[string]interface{}, vestedCoins sdk.Coins, startTime int64, endTime int64) error { +func getNewBaseAccount(ctx sdk.Context, app *App, accDataMap map[string]interface{}) (*authtypes.BaseAccount, error) { // Get raw address addr := accDataMap["address"].(string) accRawAddr, err := convertAddressToRaw(addr) if err != nil { - return err + return nil, err } // Set pubkey if present @@ -384,7 +442,7 @@ func createNewVestingAccount(ctx sdk.Context, app *App, accDataMap map[string]in if pk != nil { pubKey, err = decodePubKeyFromMap(pk.(map[string]interface{})) if err != nil { - return err + return nil, err } } } @@ -393,8 +451,11 @@ func createNewVestingAccount(ctx sdk.Context, app *App, accDataMap map[string]in newAccNumber := app.AccountKeeper.GetNextAccountNumber(ctx) newBaseAccount := authtypes.NewBaseAccount(accRawAddr, pubKey, newAccNumber, 0) + return newBaseAccount, nil +} - newBaseVestingAcc := authvesting.NewBaseVestingAccount(newBaseAccount, vestedCoins, endTime) +func createNewVestingAccountFromBaseAccount(ctx sdk.Context, app *App, account *authtypes.BaseAccount, vestedCoins sdk.Coins, startTime int64, endTime int64) error { + newBaseVestingAcc := authvesting.NewBaseVestingAccount(account, vestedCoins, endTime) newContinuousVestingAcc := authvesting.NewContinuousVestingAccountRaw(newBaseVestingAcc, startTime) app.AccountKeeper.SetAccount(ctx, newContinuousVestingAcc) @@ -456,22 +517,56 @@ func ProcessAccounts(ctx sdk.Context, app *App, jsonData map[string]interface{}, return err } + newBalance := convertedBalancesMap[addr] + // Check for collision existingAccount := app.AccountKeeper.GetAccount(ctx, accRawAddr) if existingAccount != nil { // Handle collision - return fmt.Errorf("account already exists: %s", addr) + + // Check that public keys are the same + var newAccPubKey cryptotypes.PubKey + if pk, ok := accDataMap["pub_key"]; ok { + if pk != nil { + newAccPubKey, err = decodePubKeyFromMap(pk.(map[string]interface{})) + if err != nil { + return err + } + } + } + existingAccountPubkey := existingAccount.GetPubKey() + + // Set pubkey from newAcc if is not in existingAccount + if existingAccountPubkey == nil && newAccPubKey != nil { + existingAccount.SetPubKey(newAccPubKey) + } + + if newAccPubKey != nil && existingAccountPubkey != nil && !existingAccountPubkey.Equals(newAccPubKey) { + return fmt.Errorf("account already exists with different pubkey: %s", addr) + } + + newBaseAccount := authtypes.NewBaseAccount(accRawAddr, existingAccount.GetPubKey(), existingAccount.GetAccountNumber(), existingAccount.GetSequence()) + createNewVestingAccountFromBaseAccount(ctx, app, newBaseAccount, newBalance, MergeTime, MergeTime+VestingPeriod) + + // Existing account is the same + continue + } // Handle regular migration // Create vesting account - newBalance := convertedBalancesMap[addr] - createNewVestingAccount(ctx, app, accDataMap, newBalance, MergeTime, MergeTime+VestingPeriod) + newBaseAccount, err := getNewBaseAccount(ctx, app, accDataMap) + if err != nil { + return err + } + + createNewVestingAccountFromBaseAccount(ctx, app, newBaseAccount, newBalance, MergeTime, MergeTime+VestingPeriod) err = mintToAccount(ctx, app, accRawAddr, newBalance) if err != nil { return err } + continue } else if accType == ModuleAccount { From 460ad48665e3f9ff5a9296b289b3353f2584c574 Mon Sep 17 00:00:00 2001 From: Jiri Date: Wed, 31 Jul 2024 17:29:31 +0200 Subject: [PATCH 9/9] Staking --- app/app.go | 10 +++-- app/cudos_merge.go | 109 +++++++++++++++++++++++++++++++++++++++------ 2 files changed, 102 insertions(+), 17 deletions(-) diff --git a/app/app.go b/app/app.go index 0eed9b0f2..df8f8124a 100644 --- a/app/app.go +++ b/app/app.go @@ -776,12 +776,14 @@ func (app *App) RegisterUpgradeHandlers(cfg module.Configurator) { panic(fmt.Sprintf("failed to unmarshal app state: %w", err)) } - bank := jsonData[banktypes.ModuleName].(map[string]interface{}) - balances := bank["balances"].([]interface{}) + convertedBalancesMap := getConvertedGenesisBalancesMap(jsonData) - convertedBalancesMap := getConvertedGenesisBalancesMap(balances) + err = withdrawGenesisStakingRewards(jsonData, convertedBalancesMap) + if err != nil { + panic(fmt.Sprintf("failed to withdraw genesis staking rewards: %w", err)) + } - err = ProcessAccounts(ctx, app, jsonData, networkInfo, manifest, convertedBalancesMap) + err = ProcessAccountsAndBalances(ctx, app, jsonData, networkInfo, manifest, convertedBalancesMap) if err != nil { panic(fmt.Sprintf("failed process accounts: %w", err)) } diff --git a/app/cudos_merge.go b/app/cudos_merge.go index 86fa4e291..cf8d6556c 100644 --- a/app/cudos_merge.go +++ b/app/cudos_merge.go @@ -13,6 +13,7 @@ import ( authvesting "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ibctransfertypes "github.com/cosmos/ibc-go/v3/modules/apps/transfer/types" ibccore "github.com/cosmos/ibc-go/v3/modules/core/24-host" "math/big" @@ -40,8 +41,10 @@ const ( FlagGenesisTime = "genesis-time" - ModuleAccount = "/cosmos.auth.v1beta1.ModuleAccount" - BaseAccount = "/cosmos.auth.v1beta1.BaseAccount" + ModuleAccount = "/cosmos.auth.v1beta1.ModuleAccount" + BaseAccount = "/cosmos.auth.v1beta1.BaseAccount" + UnbondedStatus = "BOND_STATUS_UNBONDED" + BondedStatus = "BOND_STATUS_BONDED" ) var BalanceDivisionConstants = map[string]int{ @@ -71,7 +74,7 @@ func convertAddressToRaw(addr string) (sdk.AccAddress, error) { prefix, decodedAddrData, err := bech32.DecodeAndConvert(addr) if prefix != OldAddrPrefix { - println("Unknown prefix: ", prefix) + return nil, fmt.Errorf("Unknown prefix: %s", prefix) } if err != nil { @@ -122,14 +125,92 @@ func getGenesisBalancesMap(balances []interface{}) *map[string]int { return &balanceMap } -func getConvertedGenesisBalancesMap(balances []interface{}) map[string]sdk.Coins { +func getConsAddressFromValidator(validatorData map[string]interface{}) (sdk.ConsAddress, error) { + consensusPubkey := validatorData["consensus_pubkey"].(map[string]interface{}) + decodedConsensusPubkey, err := decodePubKeyFromMap(consensusPubkey) + if err != nil { + return nil, err + } + return sdk.ConsAddress(decodedConsensusPubkey.Address()), nil +} + +func withdrawGenesisStakingRewards(jsonData map[string]interface{}, convertedBalances map[string]sdk.Coins) error { + + // Validator pubkey hex -> tokens int amount + validatorStakeMap := make(map[string]sdk.Int) + + // Operator address -> Validator pubkey hex + validatorOperatorMap := make(map[string]string) + + staking := jsonData[stakingtypes.ModuleName].(map[string]interface{}) + delegations := staking["delegations"].([]interface{}) + validators := staking["validators"].([]interface{}) + + // Prepare maps and total tokens amount + totalStake := sdk.NewInt(0) + for _, validator := range validators { + + tokens := validator.(map[string]interface{})["tokens"].(string) + operatorAddress := validator.(map[string]interface{})["operator_address"].(string) + + consensusPubkey := validator.(map[string]interface{})["consensus_pubkey"].(map[string]interface{}) + decodedConsensusPubkey, err := decodePubKeyFromMap(consensusPubkey) + if err != nil { + return err + } + + // Convert amount to big.Int + tokensInt, ok := sdk.NewIntFromString(tokens) + if !ok { + panic("Failed to convert validator tokens to big.Int") + } + totalStake = totalStake.Add(tokensInt) + + validatorStakeMap[decodedConsensusPubkey.String()] = tokensInt + validatorOperatorMap[operatorAddress] = decodedConsensusPubkey.String() + + } + + println(totalStake.String()) + + for _, delegation := range delegations { + delegationMap := delegation.(map[string]interface{}) + delegatorAddress := delegationMap["delegator_address"].(string) + validatorOperatorAddress := delegationMap["validator_address"].(string) + shares := delegationMap["shares"].(string) + + sharesDec, err := sdk.NewDecFromStr(shares) + if err != nil { + return err + } + + validatorAddress := validatorOperatorMap[validatorOperatorAddress] + validatorTokens := validatorStakeMap[validatorAddress] + + println(delegatorAddress, validatorOperatorAddress, sharesDec.String(), validatorAddress, validatorTokens.String()) + } + + // Dummy modification + for key := range convertedBalances { + newAmount := big.NewInt(int64(123)) + sdkCoin := sdk.NewCoin(ConvertedDenom, sdk.NewIntFromBigInt(newAmount)) + convertedBalances[key] = convertedBalances[key].Add(sdkCoin) + } + + return nil +} + +func getConvertedGenesisBalancesMap(jsonData map[string]interface{}) map[string]sdk.Coins { + bank := jsonData[banktypes.ModuleName].(map[string]interface{}) + balances := bank["balances"].([]interface{}) + balanceMap := make(map[string]sdk.Coins) for _, balance := range balances { addr := balance.(map[string]interface{})["address"] if addr == nil { - fmt.Println(balance) + panic("Failed to get address") } addrStr := addr.(string) @@ -471,7 +552,7 @@ func mintToAccount(ctx sdk.Context, app *App, address sdk.AccAddress, newCoins s return nil } -func ProcessAccounts(ctx sdk.Context, app *App, jsonData map[string]interface{}, networkInfo NetworkConfig, manifest *UpgradeManifest, convertedBalancesMap map[string]sdk.Coins) error { +func ProcessAccountsAndBalances(ctx sdk.Context, app *App, jsonData map[string]interface{}, networkInfo NetworkConfig, manifest *UpgradeManifest, convertedBalancesMap map[string]sdk.Coins) error { auth := jsonData[authtypes.ModuleName].(map[string]interface{}) accounts := auth["accounts"].([]interface{}) @@ -551,17 +632,19 @@ func ProcessAccounts(ctx sdk.Context, app *App, jsonData map[string]interface{}, // Existing account is the same continue - } + } else { - // Handle regular migration + // Handle regular migration - // Create vesting account - newBaseAccount, err := getNewBaseAccount(ctx, app, accDataMap) - if err != nil { - return err + // Create vesting account + newBaseAccount, err := getNewBaseAccount(ctx, app, accDataMap) + if err != nil { + return err + } + + createNewVestingAccountFromBaseAccount(ctx, app, newBaseAccount, newBalance, MergeTime, MergeTime+VestingPeriod) } - createNewVestingAccountFromBaseAccount(ctx, app, newBaseAccount, newBalance, MergeTime, MergeTime+VestingPeriod) err = mintToAccount(ctx, app, accRawAddr, newBalance) if err != nil { return err