diff --git a/app/app.go b/app/app.go index 648b35b1..6ea835c1 100644 --- a/app/app.go +++ b/app/app.go @@ -727,7 +727,7 @@ func (app *App) GetSubspace(moduleName string) paramstypes.Subspace { return subspace } -func getNetworkInfo(app *App, ctx *sdk.Context, manifest *UpgradeManifest, expectedChainIdOfMergeSourceGenesis string) (*NetworkConfig, error) { +func getNetworkInfo(app *App, ctx sdk.Context, manifest *UpgradeManifest, expectedChainIdOfMergeSourceGenesis string) (*NetworkConfig, error) { // Load network config from file if given var networkInfo *NetworkConfig var err error @@ -761,7 +761,7 @@ func getNetworkInfo(app *App, ctx *sdk.Context, manifest *UpgradeManifest, expec return networkInfo, nil } -func LoadAndParseMergeSourceInputFiles(app *App, ctx *sdk.Context, manifest *UpgradeManifest) (*GenesisData, *NetworkConfig, error) { +func LoadAndParseMergeSourceInputFiles(app *App, ctx sdk.Context, manifest *UpgradeManifest) (*GenesisData, *NetworkConfig, error) { cudosJsonData, cudosGenDoc, err := LoadCudosGenesis(app, manifest) @@ -776,7 +776,7 @@ func LoadAndParseMergeSourceInputFiles(app *App, ctx *sdk.Context, manifest *Upg cudosConfig := NewCudosMergeConfig(networkInfo.CudosMerge) - genesisData, err := parseGenesisData(*cudosJsonData, cudosGenDoc, cudosConfig, manifest) + genesisData, err := parseGenesisData(app, ctx, *cudosJsonData, cudosGenDoc, cudosConfig, manifest) if err != nil { return nil, nil, fmt.Errorf("failed to parse genesis data: %w", err) } @@ -793,7 +793,7 @@ func (app *App) RegisterUpgradeHandlers(cfg module.Configurator) { manifest := NewUpgradeManifest() - cudosGenesisData, networkInfo, err := LoadAndParseMergeSourceInputFiles(app, &ctx, manifest) + cudosGenesisData, networkInfo, err := LoadAndParseMergeSourceInputFiles(app, ctx, manifest) if err != nil { return nil, fmt.Errorf("cudos merge: %w", err) } diff --git a/app/ordered_map.go b/app/ordered_map.go index ad997dc1..230d1772 100644 --- a/app/ordered_map.go +++ b/app/ordered_map.go @@ -82,6 +82,27 @@ func (om *OrderedMap[K, V]) MustGet(key K) V { return value } +func (om *OrderedMap[K, V]) GetOrSetDefault(key K, defaultValue V) (V, bool) { + if value, exists := om.Get(key); exists { + return value, false + } else { + om.Set(key, defaultValue) + return om.MustGet(key), true + } +} + +// Iterate returns a channel that yields key-value pairs in insertion order +func (om *OrderedMap[K, V]) Iterate() <-chan Pair[K, V] { + ch := make(chan Pair[K, V]) + go func() { + for _, key := range om.keys { + ch <- Pair[K, V]{Key: key, Value: om.MustGet(key)} + } + close(ch) + }() + return ch +} + func (om *OrderedMap[K, V]) Has(key K) bool { _, exists := om.Get(key) return exists diff --git a/app/upgrade_cudos.go b/app/upgrade_cudos.go index 52d8c6ce..12f53d74 100644 --- a/app/upgrade_cudos.go +++ b/app/upgrade_cudos.go @@ -108,11 +108,12 @@ type GenesisData struct { blockHeight int64 chainId string prefix string + bondDenom string accounts *OrderedMap[string, *AccountInfo] contracts *OrderedMap[string, *ContractInfo] ibcAccounts *OrderedMap[string, *IBCInfo] - delegations *OrderedMap[string, *OrderedMap[string, sdk.Coins]] + delegations *OrderedMap[string, *OrderedMap[string, sdk.Int]] validators *OrderedMap[string, *ValidatorInfo] bondedPoolAddress string @@ -153,15 +154,6 @@ func LoadCudosGenesis(app *App, manifest *UpgradeManifest) (*map[string]interfac return nil, nil, fmt.Errorf("cudos merge: failed to unmarshal app state: %w", err) } - /* - genesisData, err := parseGenesisData(jsonData, cudosCfg, manifest) - if err != nil { - return fmt.Errorf("cudos merge: failed to parse genesis data: %w", err) - } - */ - - //genDoc.AppState = nil - return &jsonData, genDoc, nil } @@ -215,7 +207,7 @@ func CudosMergeUpgradeHandler(app *App, ctx sdk.Context, cudosCfg *CudosMergeCon return fmt.Errorf("cudos merge: failed process delegations: %w", err) } - err = verifySupply(genesisData, cudosCfg, manifest) + err = verifySupply(app, ctx, cudosCfg, manifest) if err != nil { return fmt.Errorf("cudos merge: failed to verify supply: %w", err) } @@ -257,7 +249,26 @@ func getAccPrefix(jsonData map[string]interface{}) (string, error) { return "", fmt.Errorf("failed to get prefix: %w", lastErr) } -func parseGenesisData(jsonData map[string]interface{}, genDoc *tmtypes.GenesisDoc, cudosCfg *CudosMergeConfig, manifest *UpgradeManifest) (*GenesisData, error) { +func getBondDenom(jsonData map[string]interface{}) (string, error) { + staking, ok := jsonData["staking"].(map[string]interface{}) + if !ok { + return "", fmt.Errorf("staking module data not found in genesis") + } + + stakingParams, ok := staking["params"].(map[string]interface{}) + if !ok { + return "", fmt.Errorf("staking params not found in genesis") + } + + bondDenom, ok := stakingParams["bond_denom"].(string) + if !ok { + return "", fmt.Errorf("staking params bond denom value not found in genesis") + } + + return bondDenom, nil +} + +func parseGenesisData(app *App, ctx sdk.Context, jsonData map[string]interface{}, genDoc *tmtypes.GenesisDoc, cudosCfg *CudosMergeConfig, manifest *UpgradeManifest) (*GenesisData, error) { genesisData := GenesisData{} var err error @@ -268,11 +279,17 @@ func parseGenesisData(jsonData map[string]interface{}, genDoc *tmtypes.GenesisDo genesisData.totalSupply = totalSupply genesisData.blockHeight = genDoc.InitialHeight genesisData.chainId = genDoc.ChainID + genesisData.prefix, err = getAccPrefix(jsonData) if err != nil { return nil, fmt.Errorf("failed to get prefix: %w", err) } + genesisData.bondDenom, err = getBondDenom(jsonData) + if err != nil { + return nil, fmt.Errorf("failed to get staking denom: %w", err) + } + genesisData.contracts, err = parseGenesisWasmContracts(jsonData) if err != nil { return nil, fmt.Errorf("failed to get contracts: %w", err) @@ -284,7 +301,7 @@ func parseGenesisData(jsonData map[string]interface{}, genDoc *tmtypes.GenesisDo } // Get all accounts and balances into map - genesisData.accounts, err = parseGenesisAccounts(jsonData, genesisData.contracts, genesisData.ibcAccounts, cudosCfg, manifest) + genesisData.accounts, err = parseGenesisAccounts(app, ctx, jsonData, genesisData.contracts, genesisData.ibcAccounts, cudosCfg, manifest) if err != nil { return nil, fmt.Errorf("failed to get accounts map: %w", err) } @@ -306,7 +323,7 @@ func parseGenesisData(jsonData map[string]interface{}, genDoc *tmtypes.GenesisDo return nil, fmt.Errorf("failed to get validators map: %w", err) } - genesisData.delegations, err = parseGenesisDelegations(genesisData.validators, genesisData.contracts, cudosCfg) + genesisData.delegations, err = parseGenesisDelegations(genesisData.bondDenom, genesisData.validators, genesisData.contracts, cudosCfg) if err != nil { return nil, fmt.Errorf("failed to get delegations map: %w", err) } @@ -520,7 +537,7 @@ func parseGenesisAccount(accMap map[string]interface{}) (*AccountInfo, error) { return &accountInfo, nil } -func parseGenesisAccounts(jsonData map[string]interface{}, contractAccountMap *OrderedMap[string, *ContractInfo], IBCAccountsMap *OrderedMap[string, *IBCInfo], cudosCfg *CudosMergeConfig, manifest *UpgradeManifest) (*OrderedMap[string, *AccountInfo], error) { +func parseGenesisAccounts(app *App, ctx sdk.Context, jsonData map[string]interface{}, contractAccountMap *OrderedMap[string, *ContractInfo], IBCAccountsMap *OrderedMap[string, *IBCInfo], cudosCfg *CudosMergeConfig, manifest *UpgradeManifest) (*OrderedMap[string, *AccountInfo], error) { var err error // Map to verify that account exists in auth module @@ -546,7 +563,7 @@ func parseGenesisAccounts(jsonData map[string]interface{}, contractAccountMap *O } // Add balances to accounts map - err = fillGenesisBalancesToAccountsMap(jsonData, accountMap, cudosCfg, manifest) + err = fillGenesisBalancesToAccountsMap(app, ctx, jsonData, accountMap, cudosCfg, manifest) if err != nil { return nil, err } @@ -554,13 +571,15 @@ func parseGenesisAccounts(jsonData map[string]interface{}, contractAccountMap *O return accountMap, nil } -func parseGenesisDelegations(validators *OrderedMap[string, *ValidatorInfo], contracts *OrderedMap[string, *ContractInfo], cudosCfg *CudosMergeConfig) (*OrderedMap[string, *OrderedMap[string, sdk.Coins]], error) { +func parseGenesisDelegations(sourceBondDenom string, validators *OrderedMap[string, *ValidatorInfo], contracts *OrderedMap[string, *ContractInfo], cudosCfg *CudosMergeConfig) (*OrderedMap[string, *OrderedMap[string, sdk.Int]], error) { // Handle delegations - delegatedBalanceMap := NewOrderedMap[string, *OrderedMap[string, sdk.Coins]]() - for _, validatorOperatorAddress := range validators.Keys() { - validator := validators.MustGet(validatorOperatorAddress) - for _, delegatorAddress := range validator.delegations.Keys() { - delegation := validator.delegations.MustGet(delegatorAddress) + delegatedBalanceMap := NewOrderedMap[string, *OrderedMap[string, sdk.Int]]() + for i := range validators.Iterate() { + validatorOperatorAddress, validator := i.Key, i.Value + + for j := range validator.delegations.Iterate() { + delegatorAddress, delegation := j.Key, j.Value + resolvedDelegatorAddress, err := resolveIfContractAddressWithFallback(delegatorAddress, contracts, cudosCfg) if err != nil { return nil, err @@ -569,9 +588,6 @@ func parseGenesisDelegations(validators *OrderedMap[string, *ValidatorInfo], con currentValidatorInfo := validators.MustGet(validatorOperatorAddress) delegatorTokens := currentValidatorInfo.TokensFromShares(delegation.shares).TruncateInt() - // Move balance to delegator address - delegatorBalance := sdk.NewCoins(sdk.NewCoin(cudosCfg.config.OriginalDenom, delegatorTokens)) - if delegatorTokens.IsZero() { // This happens when number of shares is less than 1 continue @@ -581,19 +597,9 @@ func parseGenesisDelegations(validators *OrderedMap[string, *ValidatorInfo], con if currentValidatorInfo.status == BondedStatus { // Store delegation to delegated map - if _, exists := delegatedBalanceMap.Get(resolvedDelegatorAddress); !exists { - delegatedBalanceMap.Set(resolvedDelegatorAddress, NewOrderedMap[string, sdk.Coins]()) - } - - resolvedDelegatorMap := delegatedBalanceMap.MustGet(resolvedDelegatorAddress) - - if _, exists := resolvedDelegatorMap.Get(validatorOperatorAddress); !exists { - resolvedDelegatorMap.Set(validatorOperatorAddress, sdk.NewCoins()) - } - resolvedDelegator := resolvedDelegatorMap.MustGet(validatorOperatorAddress) - - resolvedDelegatorMap.Set(validatorOperatorAddress, resolvedDelegator.Add(delegatorBalance...)) - + resolvedDelegatorMap, _ := delegatedBalanceMap.GetOrSetDefault(resolvedDelegatorAddress, NewOrderedMap[string, sdk.Int]()) + resolvedDelegator, _ := resolvedDelegatorMap.GetOrSetDefault(validatorOperatorAddress, sdk.NewInt(0)) + resolvedDelegatorMap.Set(validatorOperatorAddress, resolvedDelegator.Add(delegatorTokens)) delegatedBalanceMap.Set(resolvedDelegatorAddress, resolvedDelegatorMap) } } @@ -732,10 +738,12 @@ func parseGenesisValidators(jsonData map[string]interface{}) (*OrderedMap[string func withdrawGenesisStakingDelegations(app *App, genesisData *GenesisData, cudosCfg *CudosMergeConfig, manifest *UpgradeManifest) error { // Handle delegations - for _, validatorOperatorAddress := range genesisData.validators.Keys() { - validator := genesisData.validators.MustGet(validatorOperatorAddress) - for _, delegatorAddress := range validator.delegations.Keys() { - delegation := validator.delegations.MustGet(delegatorAddress) + for i := range genesisData.validators.Iterate() { + validatorOperatorAddress, validator := i.Key, i.Value + + for j := range validator.delegations.Iterate() { + delegatorAddress, delegation := j.Key, j.Value + resolvedDelegatorAddress, err := resolveIfContractAddressWithFallback(delegatorAddress, genesisData.contracts, cudosCfg) if err != nil { return err @@ -745,7 +753,7 @@ func withdrawGenesisStakingDelegations(app *App, genesisData *GenesisData, cudos delegatorTokens := currentValidatorInfo.TokensFromShares(delegation.shares).TruncateInt() // Move balance to delegator address - delegatorBalance := sdk.NewCoins(sdk.NewCoin(cudosCfg.config.OriginalDenom, delegatorTokens)) + delegatorBalance := sdk.NewCoins(sdk.NewCoin(genesisData.bondDenom, delegatorTokens)) if delegatorTokens.IsZero() { // This happens when number of shares is less than 1 @@ -773,15 +781,16 @@ func withdrawGenesisStakingDelegations(app *App, genesisData *GenesisData, cudos } // Handle unbonding delegations - for _, delegatorAddress := range validator.unbondingDelegations.Keys() { - unbondingDelegation := validator.unbondingDelegations.MustGet(delegatorAddress) + for j := range validator.unbondingDelegations.Iterate() { + delegatorAddress, unbondingDelegation := j.Key, j.Value + resolvedDelegatorAddress, err := resolveIfContractAddressWithFallback(delegatorAddress, genesisData.contracts, cudosCfg) if err != nil { return err } for _, entry := range unbondingDelegation.entries { - unbondingDelegationBalance := sdk.NewCoins(sdk.NewCoin(cudosCfg.config.OriginalDenom, entry.balance)) + unbondingDelegationBalance := sdk.NewCoins(sdk.NewCoin(genesisData.bondDenom, entry.balance)) // Move unbonding balance from not-bonded pool to delegator address err := moveGenesisBalance(genesisData, genesisData.notBondedPoolAddress, resolvedDelegatorAddress, unbondingDelegationBalance, "unbonding_delegation", manifest, cudosCfg) @@ -867,7 +876,7 @@ func getIntAmountFromCoins(balance sdk.Coins, expectedDenom string) (*sdk.Int, e return &coin, nil } -func createDelegation(ctx sdk.Context, app *App, originalValidator string, newDelegatorRawAddr sdk.AccAddress, validator stakingtypes.Validator, originalBalance sdk.Coins, tokensToDelegate sdk.Int, manifest *UpgradeManifest) error { +func createDelegation(ctx sdk.Context, app *App, originalValidator string, newDelegatorRawAddr sdk.AccAddress, validator stakingtypes.Validator, originalTokens sdk.Int, tokensToDelegate sdk.Int, manifest *UpgradeManifest) error { newShares, err := app.StakingKeeper.Delegate(ctx, newDelegatorRawAddr, tokensToDelegate, stakingtypes.Unbonded, validator, true) if err != nil { @@ -881,7 +890,7 @@ func createDelegation(ctx sdk.Context, app *App, originalValidator string, newDe delegation := UpgradeDelegation{ NewDelegator: newDelegatorRawAddr.String(), NewValidator: validator.OperatorAddress, - OriginalTokens: originalBalance, + OriginalTokens: originalTokens, NewTokens: tokensToDelegate, NewShares: newShares, OriginalValidator: originalValidator, @@ -904,7 +913,7 @@ func handleCommunityPoolBalance(ctx sdk.Context, app *App, genesisData *GenesisD // Get addresses and amounts RemainingDistributionBalanceAccount := genesisData.accounts.MustGet(cudosCfg.config.RemainingDistributionBalanceAddr) communityPoolBalance, _ := genesisData.distributionInfo.feePool.communityPool.TruncateDecimal() - convertedCommunityPoolBalance, err := convertBalance(communityPoolBalance, cudosCfg) + convertedCommunityPoolBalance, err := convertBalance(app, ctx, communityPoolBalance, cudosCfg) if err != nil { return err } @@ -953,7 +962,7 @@ func createGenesisDelegations(ctx sdk.Context, app *App, genesisData *GenesisDat } for _, validatorOperatorStringAddr := range delegatorAddrMap.Keys() { - delegatedBalance := delegatorAddrMap.MustGet(validatorOperatorStringAddr) + delegatedAmount := delegatorAddrMap.MustGet(validatorOperatorStringAddr) destValidator, err := resolveDestinationValidator(ctx, app, validatorOperatorStringAddr, cudosCfg) if err != nil { @@ -961,17 +970,7 @@ func createGenesisDelegations(ctx sdk.Context, app *App, genesisData *GenesisDat } // Get int amount in native tokens - convertedBalance, err := convertBalance(delegatedBalance, cudosCfg) - if err != nil { - return err - } - - if convertedBalance.Empty() { - // Very small balance gets truncated to 0 during conversion - continue - } - - tokensToDelegate, err := getIntAmountFromCoins(convertedBalance, cudosCfg.config.StakingDenom) + tokensToDelegate, err := convertAmount(app, ctx, genesisData, delegatedAmount, cudosCfg) if err != nil { return err } @@ -991,7 +990,7 @@ func createGenesisDelegations(ctx sdk.Context, app *App, genesisData *GenesisDat } } - err = createDelegation(ctx, app, validatorOperatorStringAddr, delegatorRawAddr, *destValidator, delegatedBalance, *tokensToDelegate, manifest) + err = createDelegation(ctx, app, validatorOperatorStringAddr, delegatorRawAddr, *destValidator, delegatedAmount, tokensToDelegate, manifest) if err != nil { return err } @@ -1075,13 +1074,23 @@ func withdrawGenesisContractBalances(genesisData *GenesisData, manifest *Upgrade return nil } -func convertBalance(balance sdk.Coins, cudosCfg *CudosMergeConfig) (sdk.Coins, error) { +func convertAmount(app *App, ctx sdk.Context, genesisData *GenesisData, amount sdk.Int, cudosCfg *CudosMergeConfig) (sdk.Int, error) { + balance := sdk.NewCoins(sdk.NewCoin(genesisData.bondDenom, amount)) + convertedBalance, err := convertBalance(app, ctx, balance, cudosCfg) + if err != nil { + return sdk.ZeroInt(), err + } + return convertedBalance.AmountOf(app.StakingKeeper.BondDenom(ctx)), nil + +} + +func convertBalance(app *App, ctx sdk.Context, balance sdk.Coins, cudosCfg *CudosMergeConfig) (sdk.Coins, error) { var resBalance sdk.Coins for _, coin := range balance { if conversionConstant, exists := cudosCfg.balanceConversionConstants.Get(coin.Denom); exists { newAmount := coin.Amount.ToDec().Quo(conversionConstant).TruncateInt() - sdkCoin := sdk.NewCoin(cudosCfg.config.ConvertedDenom, newAmount) + sdkCoin := sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), newAmount) resBalance = resBalance.Add(sdkCoin) } // Denominations that are not in conversion constant map are ignored @@ -1119,7 +1128,7 @@ func ensureAccount(addrStr string, genesisAccountsMap *OrderedMap[string, *Accou return nil } -func fillGenesisBalancesToAccountsMap(jsonData map[string]interface{}, genesisAccountsMap *OrderedMap[string, *AccountInfo], cudosCfg *CudosMergeConfig, manifest *UpgradeManifest) error { +func fillGenesisBalancesToAccountsMap(app *App, ctx sdk.Context, jsonData map[string]interface{}, genesisAccountsMap *OrderedMap[string, *AccountInfo], cudosCfg *CudosMergeConfig, manifest *UpgradeManifest) error { bank := jsonData[banktypes.ModuleName].(map[string]interface{}) balances := bank["balances"].([]interface{}) @@ -1138,7 +1147,7 @@ func fillGenesisBalancesToAccountsMap(jsonData map[string]interface{}, genesisAc return err } - convertedBalance, err := convertBalance(sdkBalance, cudosCfg) + convertedBalance, err := convertBalance(app, ctx, sdkBalance, cudosCfg) if err != nil { return err } @@ -1493,57 +1502,78 @@ func registerBalanceMovement(fromAddress, toAddress string, sourceAmount sdk.Coi manifest.MoveMintedBalance.Movements = append(manifest.MoveMintedBalance.Movements, movement) } -func registerManifestMoveDelegations(fromAddress, toAddress string, memo string, manifest *UpgradeManifest) { +func registerManifestMoveDelegations(fromAddress, toAddress, validatorAddress string, amount sdk.Int, memo string, manifest *UpgradeManifest) { if manifest.MoveDelegations == nil { manifest.MoveDelegations = &UpgradeMoveDelegations{} } movement := UpgradeDelegationMovements{ - From: fromAddress, - To: toAddress, - Memo: memo, + From: fromAddress, + To: toAddress, + Validator: validatorAddress, + Tokens: amount, + Memo: memo, } manifest.MoveDelegations.Movements = append(manifest.MoveDelegations.Movements, movement) manifest.MoveDelegations.NumberOfMovements = len(manifest.MoveDelegations.Movements) } -func moveGenesisDelegations(genesisData *GenesisData, fromAddress, toAddress string, manifest *UpgradeManifest) error { - sourceDelegations, exists := genesisData.delegations.Get(fromAddress) +func getDelegationData(genesisData *GenesisData, DelegatorAddress string, validatorAddress string) (*OrderedMap[string, sdk.Int], *sdk.Int) { + sourceDelegations, exists := genesisData.delegations.Get(DelegatorAddress) + if !exists { + return nil, nil + } + sourceAmount, exists := sourceDelegations.Get(validatorAddress) if !exists { - registerManifestMoveDelegations(fromAddress, toAddress, "no_delegations", manifest) - // Nothing to move - return nil + return sourceDelegations, nil } - if destDelegation, destDelegationExists := genesisData.delegations.Get(toAddress); destDelegationExists { - // Add delegations - destination delegator has some delegations + return sourceDelegations, &sourceAmount +} - // List all validator addresses in source delegation - for _, validatorAddr := range sourceDelegations.Keys() { - srcCoins := sourceDelegations.MustGet(validatorAddr) +func moveGenesisDelegation(genesisData *GenesisData, fromDelegatorAddress, toDelegatorAddress string, validatorAddress string, amount sdk.Int, manifest *UpgradeManifest, memo string) error { + // Nothing to move + if fromDelegatorAddress == toDelegatorAddress { + return nil + } - // Same validator exists in destination delegation - Add coins - if destCoins, destValidatorExists := destDelegation.Get(validatorAddr); destValidatorExists { - destDelegation.Set(validatorAddr, destCoins.Add(srcCoins...)) + // Source delegation must exist + sourceDelegations, sourceAmount := getDelegationData(genesisData, fromDelegatorAddress, validatorAddress) + if sourceDelegations == nil { + return fmt.Errorf("genesis source delegations of %s not found", fromDelegatorAddress) + } + if sourceAmount == nil { + return fmt.Errorf("genesis source delegation of %s to specific validator %s not found", fromDelegatorAddress, validatorAddress) + } - } else { - // Validator doesn't exist on destination delegation, add it - destDelegation.Set(validatorAddr, srcCoins) - } + if sourceAmount.LT(amount) { + return fmt.Errorf("amount to move is greater than delegated amount") + } - } + destinationDelegations, destinationDelegatedAmount := getDelegationData(genesisData, toDelegatorAddress, validatorAddress) + if destinationDelegations == nil { + // No destination delegations + newMap := NewOrderedMap[string, sdk.Int]() + newMap.Set(toDelegatorAddress, amount) + genesisData.delegations.Set(toDelegatorAddress, newMap) + } else if destinationDelegatedAmount == nil { + // No delegations to validator + destinationDelegations.Set(validatorAddress, amount) } else { - // Destination has no delegations, just move source delegations pointer - genesisData.delegations.Set(toAddress, sourceDelegations) + // Update existing balance + destinationDelegations.Set(validatorAddress, destinationDelegatedAmount.Add(amount)) } - // Delete all source delegations - genesisData.delegations.Delete(fromAddress) - - registerManifestMoveDelegations(fromAddress, toAddress, "", manifest) + // Subtract amount from source or remove if nothing left + if amount.Equal(*sourceAmount) { + sourceDelegations.Delete(validatorAddress) + } else { + sourceDelegations.Set(validatorAddress, sourceAmount.Sub(amount)) + } + registerManifestMoveDelegations(fromDelegatorAddress, toDelegatorAddress, validatorAddress, amount, memo, manifest) return nil } @@ -1807,15 +1837,15 @@ func MigrateGenesisAccounts(genesisData *GenesisData, ctx sdk.Context, app *App, initialMintBalance := app.BankKeeper.GetAllBalances(ctx, mintModuleAddr) // Mint donor chain total supply - totalSupplyToMint := sdk.NewCoins(sdk.NewCoin(cudosCfg.config.ConvertedDenom, cudosCfg.config.TotalFetchSupplyToMint)) - totalCudosSupply := sdk.NewCoins(sdk.NewCoin(cudosCfg.config.OriginalDenom, cudosCfg.config.TotalCudosSupply)) + totalSupplyToMint := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), cudosCfg.config.TotalFetchSupplyToMint)) + totalCudosSupply := sdk.NewCoins(sdk.NewCoin(genesisData.bondDenom, cudosCfg.config.TotalCudosSupply)) err := app.MintKeeper.MintCoins(ctx, totalSupplyToMint) if err != nil { return err } - totalSupplyReducedByCommission, err := convertBalance(totalCudosSupply, cudosCfg) + totalSupplyReducedByCommission, err := convertBalance(app, ctx, totalCudosSupply, cudosCfg) if err != nil { return err } @@ -1829,13 +1859,13 @@ func MigrateGenesisAccounts(genesisData *GenesisData, ctx sdk.Context, app *App, err = migrateToAccount(ctx, app, "mint_module", commissionRawAcc, sdk.NewCoins(), totalCommission, "total_commission", manifest) - extraSupplyInCudos := cudosCfg.config.TotalCudosSupply.Sub(genesisData.totalSupply.AmountOf(cudosCfg.config.OriginalDenom)) + extraSupplyInCudos := cudosCfg.config.TotalCudosSupply.Sub(genesisData.totalSupply.AmountOf(genesisData.bondDenom)) extraSupplyCudosAddress, err := convertAddressPrefix(cudosCfg.config.ExtraSupplyFetchAddr, genesisData.prefix) if err != nil { return err } - extraSupplyInCudosCoins := sdk.NewCoins(sdk.NewCoin(cudosCfg.config.OriginalDenom, extraSupplyInCudos)) + extraSupplyInCudosCoins := sdk.NewCoins(sdk.NewCoin(genesisData.bondDenom, extraSupplyInCudos)) err = createGenesisBalance(genesisData, extraSupplyCudosAddress, extraSupplyInCudosCoins, "extra_supply", manifest) if err != nil { @@ -1895,7 +1925,7 @@ func MigrateGenesisAccounts(genesisData *GenesisData, ctx sdk.Context, app *App, } // Get balance to mint - newBalance, err := convertBalance(genesisAccount.balance, cudosCfg) + newBalance, err := convertBalance(app, ctx, genesisAccount.balance, cudosCfg) if err != nil { return err } @@ -1950,35 +1980,58 @@ func MigrateGenesisAccounts(genesisData *GenesisData, ctx sdk.Context, app *App, } func DoGenesisAccountMovements(genesisData *GenesisData, cudosCfg *CudosMergeConfig, manifest *UpgradeManifest) error { - if cudosCfg.MovedAccounts == nil { - // Nothing to move - return nil - } - for _, fromAddr := range cudosCfg.MovedAccounts.Keys() { - toAddr := cudosCfg.MovedAccounts.MustGet(fromAddr) - - fromAcc, exists := genesisData.accounts.Get(fromAddr) + for _, accountMovement := range cudosCfg.config.MovedAccounts { + fromAcc, exists := genesisData.accounts.Get(accountMovement.SourceAddress) if !exists { - registerManifestBalanceMovement(fromAddr, toAddr, nil, "non_existing_from_account", manifest) + registerManifestBalanceMovement(accountMovement.SourceAddress, accountMovement.DestinationAddress, nil, "non_existing_from_account", manifest) return nil } if fromAcc.balance.IsZero() { - registerManifestBalanceMovement(fromAddr, toAddr, nil, "nothing_to_move_err", manifest) + registerManifestBalanceMovement(accountMovement.SourceAddress, accountMovement.DestinationAddress, nil, "nothing_to_move_err", manifest) return nil } - err := moveGenesisBalance(genesisData, fromAddr, toAddr, fromAcc.balance, "balance_movement", manifest, cudosCfg) - if err != nil { - return err + fromAccTokensAmount := fromAcc.balance.AmountOfNoDenomValidation(genesisData.bondDenom) + + // Move entire balance if balance to move is 0 or greater than available balance + if accountMovement.Amount.IsZero() || fromAccTokensAmount.LT(accountMovement.Amount) { + accountMovement.Amount = fromAccTokensAmount } + balanceToMove := sdk.NewCoins(sdk.NewCoin(genesisData.bondDenom, accountMovement.Amount)) - err = moveGenesisDelegations(genesisData, fromAddr, toAddr, manifest) + // Handle balance movement + err := moveGenesisBalance(genesisData, accountMovement.SourceAddress, accountMovement.DestinationAddress, balanceToMove, "balance_movement", manifest, cudosCfg) if err != nil { return err } + + // Handle delegations movement + remainingAmountToMove := accountMovement.Amount + if sourceDelegations, exists := genesisData.delegations.Get(accountMovement.SourceAddress); exists { + for i := range sourceDelegations.Iterate() { + validatorAddr, delegatedAmount := i.Key, i.Value + + if delegatedAmount.GTE(remainingAmountToMove) { + // Split delegation + err := moveGenesisDelegation(genesisData, accountMovement.SourceAddress, accountMovement.DestinationAddress, validatorAddr, remainingAmountToMove, manifest, "") + if err != nil { + return fmt.Errorf("failed to move delegated amount %s of %s from %s to %s: %w", delegatedAmount, validatorAddr, accountMovement.SourceAddress, accountMovement.DestinationAddress, err) + } + + break + } else { + // Move entire delegation + err := moveGenesisDelegation(genesisData, accountMovement.SourceAddress, accountMovement.DestinationAddress, validatorAddr, delegatedAmount, manifest, "") + if err != nil { + return fmt.Errorf("failed to move delegated amount %s of %s from %s to %s: %w", delegatedAmount, validatorAddr, accountMovement.SourceAddress, accountMovement.DestinationAddress, err) + } + } + remainingAmountToMove = remainingAmountToMove.Sub(delegatedAmount) + } + } } return nil @@ -1996,9 +2049,9 @@ func parseGenesisTotalSupply(jsonData map[string]interface{}) (sdk.Coins, error) } -func verifySupply(genesisData *GenesisData, cudosCfg *CudosMergeConfig, manifest *UpgradeManifest) error { +func verifySupply(app *App, ctx sdk.Context, cudosCfg *CudosMergeConfig, manifest *UpgradeManifest) error { - expectedMintedSupply := sdk.NewCoins(sdk.NewCoin(cudosCfg.config.ConvertedDenom, cudosCfg.config.TotalFetchSupplyToMint)) + expectedMintedSupply := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), cudosCfg.config.TotalFetchSupplyToMint)) mintedSupply := manifest.Migration.AggregatedMigratedAmount diff --git a/app/upgrade_cudos_distribution.go b/app/upgrade_cudos_distribution.go index f29e1f71..9659b42d 100644 --- a/app/upgrade_cudos_distribution.go +++ b/app/upgrade_cudos_distribution.go @@ -73,10 +73,7 @@ func parseDelegatorStartingInfos(distribution map[string]interface{}) (*OrderedM } delegatorStartingInfo.stake = stakeDec - if _, exists := delegatorStartingInfos.Get(validatorAddress); !exists { - delegatorStartingInfos.Set(validatorAddress, NewOrderedMap[string, *DelegatorStartingInfo]()) - } - valStartingInfo := delegatorStartingInfos.MustGet(validatorAddress) + valStartingInfo, _ := delegatorStartingInfos.GetOrSetDefault(validatorAddress, NewOrderedMap[string, *DelegatorStartingInfo]()) valStartingInfo.Set(delegatorAddress, &delegatorStartingInfo) delegatorStartingInfos.Set(validatorAddress, valStartingInfo) @@ -104,10 +101,7 @@ func parseValidatorHistoricalRewards(distribution map[string]interface{}) (*Orde delegatorStartingInfo.cumulativeRewardRatio = cumulativeRewardRatio - if _, exists := validatorHistoricalRewards.Get(validatorAddress); !exists { - validatorHistoricalRewards.Set(validatorAddress, NewOrderedMap[uint64, *ValidatorHistoricalReward]()) - } - valRewards := validatorHistoricalRewards.MustGet(validatorAddress) + valRewards, _ := validatorHistoricalRewards.GetOrSetDefault(validatorAddress, NewOrderedMap[uint64, *ValidatorHistoricalReward]()) valRewards.SetNew(period, &delegatorStartingInfo) //validatorHistoricalRewards.Set(validatorAddress, *valRewards) @@ -206,10 +200,7 @@ func parseValidatorSlashEvents(distribution map[string]interface{}) (*OrderedMap delegatorStartingInfo.period = period delegatorStartingInfo.validatorPeriod = cast.ToUint64(slashEvent["validator_period"].(string)) - if _, exists := validatorSlashEvents.Get(validatorAddress); !exists { - validatorSlashEvents.Set(validatorAddress, NewOrderedMap[uint64, *ValidatorSlashEvent]()) - } - valEvents := validatorSlashEvents.MustGet(validatorAddress) + valEvents, _ := validatorSlashEvents.GetOrSetDefault(validatorAddress, NewOrderedMap[uint64, *ValidatorSlashEvent]()) // TODO(pb): Is this necessary? It basically re-validates data in genesis file, which we should treat & take // as granted to be correct by definition. @@ -303,33 +294,6 @@ func parseGenesisDistribution(jsonData map[string]interface{}, genesisAccounts * return &distributionInfo, nil } -func getMaxBlockHeight(genesisData *GenesisData) uint64 { - maxHeight := uint64(0) - - for _, validatorOperatorAddress := range genesisData.distributionInfo.delegatorStartingInfos.Keys() { - valStartingInfo := genesisData.distributionInfo.delegatorStartingInfos.MustGet(validatorOperatorAddress) - for _, DelegatorAddress := range valStartingInfo.Keys() { - stargingInfo := valStartingInfo.MustGet(DelegatorAddress) - if stargingInfo.height > maxHeight { - maxHeight = stargingInfo.height - } - } - } - - for _, validatorOperatorAddress := range genesisData.distributionInfo.validatorSlashEvents.Keys() { - slashEvents := genesisData.distributionInfo.validatorSlashEvents.MustGet(validatorOperatorAddress) - - for _, slashEventHeight := range slashEvents.Keys() { - if slashEventHeight > maxHeight { - maxHeight = slashEventHeight - } - } - - } - - return maxHeight -} - func checkTolerance(coins sdk.Coins, maxToleratedDiff sdk.Int) error { for _, coin := range coins { if coin.Amount.GT(maxToleratedDiff) { @@ -340,8 +304,9 @@ func checkTolerance(coins sdk.Coins, maxToleratedDiff sdk.Int) error { } func verifyOutstandingBalances(genesisData *GenesisData) error { - for _, validatorAddr := range genesisData.distributionInfo.outstandingRewards.Keys() { - validatorOutstandingReward := genesisData.distributionInfo.outstandingRewards.MustGet(validatorAddr) + for i := range genesisData.distributionInfo.outstandingRewards.Iterate() { + validatorAddr, validatorOutstandingReward := i.Key, i.Value + validatorAccumulatedCommission := genesisData.distributionInfo.validatorAccumulatedCommissions.MustGet(validatorAddr) diff := validatorOutstandingReward.Sub(validatorAccumulatedCommission) @@ -363,6 +328,7 @@ func withdrawGenesisDistributionRewards(app *App, genesisData *GenesisData, cudo // Withdraw all delegation rewards for _, validatorOpertorAddr := range genesisData.distributionInfo.delegatorStartingInfos.Keys() { validator := genesisData.validators.MustGet(validatorOpertorAddr) + delegatorStartInfo := genesisData.distributionInfo.delegatorStartingInfos.MustGet(validatorOpertorAddr) endingPeriod := updateValidatorData(genesisData.distributionInfo, validator) @@ -546,7 +512,7 @@ func calculateDelegationRewards(blockHeight uint64, distributionInfo *Distributi // for them for the stake sanity check below. endingHeight := blockHeight if endingHeight > startingHeight { - IterateValidatorSlashEventsBetween(distributionInfo, val.operatorAddress, startingHeight, endingHeight, + err := IterateValidatorSlashEventsBetween(distributionInfo, val.operatorAddress, startingHeight, endingHeight, func(height uint64, event *ValidatorSlashEvent) (stop bool, err error) { endingPeriod := event.validatorPeriod if endingPeriod > startingPeriod { @@ -563,6 +529,9 @@ func calculateDelegationRewards(blockHeight uint64, distributionInfo *Distributi return false, nil }, ) + if err != nil { + return nil, err + } } // A total stake sanity check; Recalculated final stake should be less than or @@ -681,10 +650,13 @@ func withdrawDelegationRewards(app *App, genesisData *GenesisData, val *Validato //k.DeleteDelegatorStartingInfo(ctx, del.GetValidatorAddr(), del.GetDelegatorAddr()) if finalRewards.IsZero() { - baseDenom, _ := sdk.GetBaseDenom() - if baseDenom == "" { - baseDenom = cudosCfg.config.OriginalDenom - } + /* + baseDenom, _ := sdk.GetBaseDenom() + if baseDenom == "" { + baseDenom = cudosCfg.config.OriginalDenom + } + */ + baseDenom := genesisData.bondDenom // Note, we do not call the NewCoins constructor as we do not want the zero // coin removed. diff --git a/app/upgrade_v_11_4_manifest.go b/app/upgrade_v_11_4_manifest.go index bd1248c5..b3cb1e32 100644 --- a/app/upgrade_v_11_4_manifest.go +++ b/app/upgrade_v_11_4_manifest.go @@ -119,9 +119,11 @@ type UpgradeMigation struct { } type UpgradeDelegationMovements struct { - From string `json:"from"` - To string `json:"to"` - Memo string `json:"memo,omitempty"` + From string `json:"from"` + To string `json:"to"` + Validator string `json:"validator"` + Tokens types.Int `json:"tokens"` + Memo string `json:"memo,omitempty"` } type UpgradeMoveGenesisBalance struct { @@ -147,12 +149,12 @@ type UpgradeMoveDelegations struct { } type UpgradeDelegation struct { - OriginalValidator string `json:"original_validator"` - NewValidator string `json:"new_validator"` - NewDelegator string `json:"new_delegator"` - OriginalTokens types.Coins `json:"original_tokens"` - NewTokens types.Int `json:"new_tokens"` - NewShares types.Dec `json:"new_shares"` + OriginalValidator string `json:"original_validator"` + NewValidator string `json:"new_validator"` + NewDelegator string `json:"new_delegator"` + OriginalTokens types.Int `json:"original_tokens"` + NewTokens types.Int `json:"new_tokens"` + NewShares types.Dec `json:"new_shares"` } type VestingCollision struct { diff --git a/app/upgrade_v_11_4_network_config.go b/app/upgrade_v_11_4_network_config.go index 2e23c477..ef89ad8b 100644 --- a/app/upgrade_v_11_4_network_config.go +++ b/app/upgrade_v_11_4_network_config.go @@ -30,6 +30,12 @@ func newDec(val string) sdk.Dec { return res } +type BalanceMovement struct { + SourceAddress string `json:"from"` + DestinationAddress string `json:"to"` + Amount sdk.Int `json:"amount"` +} + var NetworkInfos = map[string]NetworkConfig{ "fetchhub-4": { ReconciliationInfo: &ReconciliationInfo{ @@ -67,14 +73,10 @@ var NetworkInfos = map[string]NetworkConfig{ ContractDestinationFallbackAddr: "cudos1qqz5ezf9ylgft0eq97d66v5aakynux540ds9mv", // Replace!! CommissionFetchAddr: "fetch122j02czdt5ca8cf576wy2hassyxyx67wg5xmgc", // Replace!! - ExtraSupplyFetchAddr: "fetch122j02czdt5ca8cf576wy2hassyxyx67wg5xmgc", // Reokace!! + ExtraSupplyFetchAddr: "fetch122j02czdt5ca8cf576wy2hassyxyx67wg5xmgc", // Replace!! VestingCollisionDestAddr: "fetch122j02czdt5ca8cf576wy2hassyxyx67wg5xmgc", // Replace!! CommunityPoolBalanceDestAddr: "cudos1nj49l56x7sss5hqyvfmctxr3mq64whg273g3x5", - OriginalDenom: "acudos", - ConvertedDenom: "afet", - StakingDenom: "afet", - VestingPeriod: 3 * 30 * 24 * 60 * 60, // 3 months period BalanceConversionConstants: []Pair[string, sdk.Dec]{ @@ -93,9 +95,9 @@ var NetworkInfos = map[string]NetworkConfig{ "cudos1qx3yaanre054nlq84qdzufsjmrrxcqxwzdkh6c", }, - MovedAccounts: []Pair[string, string]{ - {"cudos1h6r6g0pwq7kcys5jcvfm9r7gcj3n2753hvk2ym", "cudos1w63ph9e4l07vpx7xdnje43cr2tlnr4jsfm4mvq"}, - {"cudos1jxyc7lny4q7te6sj5xyt9j86kyz82vlfdprl4a", "cudos1tfmkdzx9hm8g28vpgc3xhhxjjn460wzkwtayxr"}, + MovedAccounts: []BalanceMovement{ + BalanceMovement{"cudos1h6r6g0pwq7kcys5jcvfm9r7gcj3n2753hvk2ym", "cudos1w63ph9e4l07vpx7xdnje43cr2tlnr4jsfm4mvq", newInt("0")}, + BalanceMovement{"cudos1jxyc7lny4q7te6sj5xyt9j86kyz82vlfdprl4a", "cudos1tfmkdzx9hm8g28vpgc3xhhxjjn460wzkwtayxr", newInt("0")}, }, BackupValidators: []string{"fetchvaloper14w6a4al72uc3fpfy4lqtg0a7xtkx3w7hda0vel"}, @@ -142,10 +144,6 @@ var NetworkInfos = map[string]NetworkConfig{ VestingCollisionDestAddr: "cudos1nj49l56x7sss5hqyvfmctxr3mq64whg273g3x5", CommunityPoolBalanceDestAddr: "cudos1dslwarknhfsw3pfjzxxf5mn28q3ewfectw0gta", - OriginalDenom: "acudos", - ConvertedDenom: "atestfet", - StakingDenom: "atestfet", - VestingPeriod: 3 * 30 * 24 * 60 * 60, // 3 months period BalanceConversionConstants: []Pair[string, sdk.Dec]{ @@ -172,11 +170,10 @@ var NetworkInfos = map[string]NetworkConfig{ //cudos1dslwarknhfsw3pfjzxxf5mn28q3ewfectw0gta //cudos15jpukx39rtkt8w3u3gzwwvyptdeyejcjade6he - MovedAccounts: []Pair[string, string]{ - //{"cudos196nrmandtwz67d8h4h0ux7amlcluecglx00wlw", "cudos1nj49l56x7sss5hqyvfmctxr3mq64whg273g3x5"}, // Replace this - //{"cudos1xcwjdw09cc9dyshr4gt5520sgsh582mjj03jge", "cudos1dslwarknhfsw3pfjzxxf5mn28q3ewfectw0gta"}, // Replace this - //{"cudos1ejmf96efvjp6pmsaj8djv3gpmnsvmpnctger4v", "fetch15p3rl5aavw9rtu86tna5lgxfkz67zzr6ed4yhw"}, // Replace this - + MovedAccounts: []BalanceMovement{ + {"cudos196nrmandtwz67d8h4h0ux7amlcluecglx00wlw", "cudos1nj49l56x7sss5hqyvfmctxr3mq64whg273g3x5", newInt("10000")}, // Replace this + {"cudos1xcwjdw09cc9dyshr4gt5520sgsh582mjj03jge", "cudos1dslwarknhfsw3pfjzxxf5mn28q3ewfectw0gta", newInt("0")}, // Replace this + {"cudos1ejmf96efvjp6pmsaj8djv3gpmnsvmpnctger4v", "cudos15p3rl5aavw9rtu86tna5lgxfkz67zzr6tp4ltv", newInt("0")}, // Replace this }, BackupValidators: []string{"fetchvaloper1m9cjw6xgt04f9ddw25fff3cfe2exgwk07eu46u", "fetchvaloper122j02czdt5ca8cf576wy2hassyxyx67wdsecml"}, @@ -314,10 +311,6 @@ type CudosMergeConfigJSON struct { ExtraSupplyFetchAddr string `json:"extra_supply_fetch_addr"` // Fetch address for extra supply VestingCollisionDestAddr string `json:"vesting_collision_dest_addr"` // This gets converted to raw address, so it can be fetch or cudos address - OriginalDenom string `json:"original_denom"` - ConvertedDenom string `json:"converted_denom"` - StakingDenom string `json:"staking_denom"` - VestingPeriod int64 `json:"vesting_period"` // Vesting period BalanceConversionConstants []Pair[string, sdk.Dec] `json:"balance_conversion_constants,omitempty"` @@ -325,9 +318,9 @@ type CudosMergeConfigJSON struct { TotalCudosSupply sdk.Int `json:"total_cudos_supply"` TotalFetchSupplyToMint sdk.Int `json:"total_fetch_supply_to_mint"` - NotVestedAccounts []string `json:"not_vested_accounts,omitempty"` - NotDelegatedAccounts []string `json:"not_delegated_accounts,omitempty"` - MovedAccounts []Pair[string, string] `json:"moved_accounts,omitempty"` + NotVestedAccounts []string `json:"not_vested_accounts,omitempty"` + NotDelegatedAccounts []string `json:"not_delegated_accounts,omitempty"` + MovedAccounts []BalanceMovement `json:"moved_accounts,omitempty"` ValidatorsMap []Pair[string, string] `json:"validators_map,omitempty"` @@ -341,7 +334,6 @@ type CudosMergeConfig struct { notVestedAccounts *OrderedMap[string, bool] notDelegatedAccounts *OrderedMap[string, bool] - MovedAccounts *OrderedMap[string, string] validatorsMap *OrderedMap[string, string] } @@ -353,7 +345,7 @@ func NewCudosMergeConfig(config *CudosMergeConfigJSON) *CudosMergeConfig { retval.balanceConversionConstants = NewOrderedMapFromPairs(config.BalanceConversionConstants) retval.notVestedAccounts = NewOrderedSet(config.NotVestedAccounts) retval.notDelegatedAccounts = NewOrderedSet(config.NotDelegatedAccounts) - retval.MovedAccounts = NewOrderedMapFromPairs(config.MovedAccounts) + retval.validatorsMap = NewOrderedMapFromPairs(config.ValidatorsMap) return retval