From bf140aa60045ba92b83d0cb7f3bc47a2661a4e7e Mon Sep 17 00:00:00 2001 From: Andrea Di Michele Date: Tue, 9 Jul 2024 18:26:36 +0200 Subject: [PATCH] upgrade handler for v23 (#1032) * basic upgrade handler for v23 * remove icq from store upgrades * migrate icq params in upgrade handler * builder module account conversion * port back tf patch from mainnet with a better implementation * lint * refuse in tf update * core1 vesting migration * lint * lint 2 * remove uneccessary consensus params migration * ci: allow flaky interchaintest to rerun. (#1034) * ci * test * test2 * test3 --------- Co-authored-by: Tuan Tran --- .github/workflows/interchaintest-E2E.yml | 15 ++ app/app.go | 4 + .../testnet/v23.0.0-alpha.1/constants.go | 52 +++++ .../testnet/v23.0.0-alpha.1/upgrade_test.go | 40 ++++ app/upgrades/v23/constants.go | 16 ++ app/upgrades/v23/upgrade_test.go | 39 ++++ app/upgrades/v23/upgrades.go | 120 ++++++++++++ app/upgrades/v23/vesting.go | 181 ++++++++++++++++++ interchaintest/chain_upgrade_test.go | 4 +- x/tokenfactory/keeper/bankactions.go | 47 ++--- x/tokenfactory/keeper/keeper.go | 13 +- x/tokenfactory/types/errors.go | 1 + 12 files changed, 493 insertions(+), 39 deletions(-) create mode 100644 app/upgrades/testnet/v23.0.0-alpha.1/constants.go create mode 100644 app/upgrades/testnet/v23.0.0-alpha.1/upgrade_test.go create mode 100644 app/upgrades/v23/constants.go create mode 100644 app/upgrades/v23/upgrade_test.go create mode 100644 app/upgrades/v23/upgrades.go create mode 100644 app/upgrades/v23/vesting.go diff --git a/.github/workflows/interchaintest-E2E.yml b/.github/workflows/interchaintest-E2E.yml index 81452ea5a..417550215 100644 --- a/.github/workflows/interchaintest-E2E.yml +++ b/.github/workflows/interchaintest-E2E.yml @@ -99,4 +99,19 @@ jobs: docker image ls -a - name: Run Test + id: run_test + continue-on-error: true run: make ${{ matrix.test }} + + - name: Retry Failed Test + if: steps.run_test.outcome == 'failure' + run: | + for i in 1 2; do + echo "Retry attempt $i" + if make ${{ matrix.test }}; then + echo "Test passed on retry" + exit 0 + fi + done + echo "Test failed after retries" + exit 1 diff --git a/app/app.go b/app/app.go index ec28ea9c8..149c2f24b 100644 --- a/app/app.go +++ b/app/app.go @@ -73,6 +73,7 @@ import ( testnetV19alpha3 "github.com/CosmosContracts/juno/v23/app/upgrades/testnet/v19.0.0-alpha.3" testnetV21alpha1 "github.com/CosmosContracts/juno/v23/app/upgrades/testnet/v21.0.0-alpha.1" testnetV22alpha1 "github.com/CosmosContracts/juno/v23/app/upgrades/testnet/v22.0.0-alpha.1" + testnetV23alpha1 "github.com/CosmosContracts/juno/v23/app/upgrades/testnet/v23.0.0-alpha.1" v10 "github.com/CosmosContracts/juno/v23/app/upgrades/v10" v11 "github.com/CosmosContracts/juno/v23/app/upgrades/v11" v12 "github.com/CosmosContracts/juno/v23/app/upgrades/v12" @@ -85,6 +86,7 @@ import ( v19 "github.com/CosmosContracts/juno/v23/app/upgrades/v19" v21 "github.com/CosmosContracts/juno/v23/app/upgrades/v21" v22 "github.com/CosmosContracts/juno/v23/app/upgrades/v22" + v23 "github.com/CosmosContracts/juno/v23/app/upgrades/v23" "github.com/CosmosContracts/juno/v23/docs" ) @@ -114,6 +116,7 @@ var ( testnetV19alpha3.Upgrade, testnetV21alpha1.Upgrade, testnetV22alpha1.Upgrade, + testnetV23alpha1.Upgrade, v10.Upgrade, v11.Upgrade, @@ -127,6 +130,7 @@ var ( v19.Upgrade, v21.Upgrade, v22.Upgrade, + v23.Upgrade, } ) diff --git a/app/upgrades/testnet/v23.0.0-alpha.1/constants.go b/app/upgrades/testnet/v23.0.0-alpha.1/constants.go new file mode 100644 index 000000000..955dac81f --- /dev/null +++ b/app/upgrades/testnet/v23.0.0-alpha.1/constants.go @@ -0,0 +1,52 @@ +package v23 + +import ( + "fmt" + + icqtypes "github.com/cosmos/ibc-apps/modules/async-icq/v7/types" + + store "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" + + "github.com/CosmosContracts/juno/v23/app/keepers" + "github.com/CosmosContracts/juno/v23/app/upgrades" +) + +// UpgradeName defines the on-chain upgrade name for the upgrade. +const UpgradeName = "v2300alpha1" + +var Upgrade = upgrades.Upgrade{ + UpgradeName: UpgradeName, + CreateUpgradeHandler: v2300Alpha1UpgradeHandler, + StoreUpgrades: store.StoreUpgrades{ + Added: []string{ + // updated modules + icqtypes.ModuleName, + }, + }, +} + +func v2300Alpha1UpgradeHandler( + mm *module.Manager, + cfg module.Configurator, + _ *keepers.AppKeepers, +) upgradetypes.UpgradeHandler { + return func(ctx sdk.Context, _ upgradetypes.Plan, vm module.VersionMap) (module.VersionMap, error) { + logger := ctx.Logger().With("upgrade", UpgradeName) + + nativeDenom := upgrades.GetChainsDenomToken(ctx.ChainID()) + logger.Info(fmt.Sprintf("With native denom %s", nativeDenom)) + + // Run migrations + logger.Info(fmt.Sprintf("pre migrate version map: %v", vm)) + versionMap, err := mm.RunMigrations(ctx, cfg, vm) + if err != nil { + return nil, err + } + logger.Info(fmt.Sprintf("post migrate version map: %v", versionMap)) + + return versionMap, err + } +} diff --git a/app/upgrades/testnet/v23.0.0-alpha.1/upgrade_test.go b/app/upgrades/testnet/v23.0.0-alpha.1/upgrade_test.go new file mode 100644 index 000000000..d16e773b6 --- /dev/null +++ b/app/upgrades/testnet/v23.0.0-alpha.1/upgrade_test.go @@ -0,0 +1,40 @@ +package v23_test + +import ( + "testing" + + "github.com/stretchr/testify/suite" + + "github.com/CosmosContracts/juno/v23/app/apptesting" + v23alpha1 "github.com/CosmosContracts/juno/v23/app/upgrades/testnet/v22.0.0-alpha.1" +) + +type UpgradeTestSuite struct { + apptesting.KeeperTestHelper +} + +func (s *UpgradeTestSuite) SetupTest() { + s.Setup() +} + +func TestKeeperTestSuite(t *testing.T) { + suite.Run(t, new(UpgradeTestSuite)) +} + +// Ensures the test does not error out. +func (s *UpgradeTestSuite) TestUpgrade() { + s.Setup() + + preUpgradeChecks(s) + + upgradeHeight := int64(5) + s.ConfirmUpgradeSucceeded(v23alpha1.UpgradeName, upgradeHeight) + + postUpgradeChecks(s) +} + +func preUpgradeChecks(_ *UpgradeTestSuite) { +} + +func postUpgradeChecks(_ *UpgradeTestSuite) { +} diff --git a/app/upgrades/v23/constants.go b/app/upgrades/v23/constants.go new file mode 100644 index 000000000..58f0ea08c --- /dev/null +++ b/app/upgrades/v23/constants.go @@ -0,0 +1,16 @@ +package v23 + +import ( + store "github.com/cosmos/cosmos-sdk/store/types" + + "github.com/CosmosContracts/juno/v23/app/upgrades" +) + +// UpgradeName defines the on-chain upgrade name for the upgrade. +const UpgradeName = "v23" + +var Upgrade = upgrades.Upgrade{ + UpgradeName: UpgradeName, + CreateUpgradeHandler: CreateV23UpgradeHandler, + StoreUpgrades: store.StoreUpgrades{}, +} diff --git a/app/upgrades/v23/upgrade_test.go b/app/upgrades/v23/upgrade_test.go new file mode 100644 index 000000000..5117f212f --- /dev/null +++ b/app/upgrades/v23/upgrade_test.go @@ -0,0 +1,39 @@ +package v23_test + +import ( + "testing" + + "github.com/stretchr/testify/suite" + + "github.com/CosmosContracts/juno/v23/app/apptesting" + v23 "github.com/CosmosContracts/juno/v23/app/upgrades/v23" +) + +type UpgradeTestSuite struct { + apptesting.KeeperTestHelper +} + +func (s *UpgradeTestSuite) SetupTest() { + s.Setup() +} + +func TestKeeperTestSuite(t *testing.T) { + suite.Run(t, new(UpgradeTestSuite)) +} + +// Ensures the test does not error out. +func (s *UpgradeTestSuite) TestUpgrade() { + s.Setup() + preUpgradeChecks(s) + + upgradeHeight := int64(5) + s.ConfirmUpgradeSucceeded(v23.UpgradeName, upgradeHeight) + + postUpgradeChecks(s) +} + +func preUpgradeChecks(_ *UpgradeTestSuite) { +} + +func postUpgradeChecks(_ *UpgradeTestSuite) { +} diff --git a/app/upgrades/v23/upgrades.go b/app/upgrades/v23/upgrades.go new file mode 100644 index 000000000..344b9a5c6 --- /dev/null +++ b/app/upgrades/v23/upgrades.go @@ -0,0 +1,120 @@ +package v23 + +import ( + "fmt" + + icqtypes "github.com/cosmos/ibc-apps/modules/async-icq/v7/types" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + paramstypes "github.com/cosmos/cosmos-sdk/x/params/types" + upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" + + "github.com/CosmosContracts/juno/v23/app/keepers" + "github.com/CosmosContracts/juno/v23/app/upgrades" +) + +type IndividualAccount struct { + Owner string + Address string +} + +// Core1VestingAccounts https://daodao.zone/dao/juno1j6glql3xmrcnga0gytecsucq3kd88jexxamxg3yn2xnqhunyvflqr7lxx3/members +// we are including only lobo, dimi and jake because the other ones do not agree on giving up their vesting +var Core1VestingAccounts = []IndividualAccount{ + { + Owner: "dimi", + Address: "juno1s33zct2zhhaf60x4a90cpe9yquw99jj0zen8pt", + }, + { + Owner: "jake", + Address: "juno18qw9ydpewh405w4lvmuhlg9gtaep79vy2gmtr2", + }, + { + Owner: "wolf", + Address: "juno1a8u47ggy964tv9trjxfjcldutau5ls705djqyu", + }, +} + +func CreateV23UpgradeHandler( + mm *module.Manager, + cfg module.Configurator, + keepers *keepers.AppKeepers, +) upgradetypes.UpgradeHandler { + return func(ctx sdk.Context, _ upgradetypes.Plan, vm module.VersionMap) (module.VersionMap, error) { + logger := ctx.Logger().With("upgrade", UpgradeName) + + nativeDenom := upgrades.GetChainsDenomToken(ctx.ChainID()) + logger.Info(fmt.Sprintf("With native denom %s", nativeDenom)) + + // migrate ICQ params + for _, subspace := range keepers.ParamsKeeper.GetSubspaces() { + subspace := subspace + + var keyTable paramstypes.KeyTable + if subspace.Name() == icqtypes.ModuleName { + keyTable = icqtypes.ParamKeyTable() + } else { + continue + } + + if !subspace.HasKeyTable() { + subspace.WithKeyTable(keyTable) + } + } + + // Run migrations + logger.Info(fmt.Sprintf("pre migrate version map: %v", vm)) + versionMap, err := mm.RunMigrations(ctx, cfg, vm) + if err != nil { + return nil, err + } + + logger.Info(fmt.Sprintf("post migrate version map: %v", versionMap)) + + // convert pob builder account to an actual module account + // during upgrade from v15 to v16 it wasn't correctly created, and since it received tokens on mainnet is now a base account + // it's like this on both mainnet and uni + if ctx.ChainID() == "juno-1" || ctx.ChainID() == "uni-6" { + logger.Info("converting x/pob builder module account") + + address := sdk.MustAccAddressFromBech32("juno1ma4sw9m2nvtucny6lsjhh4qywvh86zdh5dlkd4") + + acc := keepers.AccountKeeper.NewAccount( + ctx, + authtypes.NewModuleAccount( + authtypes.NewBaseAccountWithAddress(address), + "builder", + ), + ) + keepers.AccountKeeper.SetAccount(ctx, acc) + + logger.Info("x/pob builder module address is now a module account") + } + + // only on mainnet and uni, migrate core1 vesting accounts + if ctx.ChainID() == "juno-1" || ctx.ChainID() == "uni-6" { + if err := migrateCore1VestingAccounts(ctx, keepers, nativeDenom); err != nil { + return nil, err + } + } + + return versionMap, err + } +} + +// Migrate balances from the Core-1 vesting accounts to the Council SubDAO. +func migrateCore1VestingAccounts(ctx sdk.Context, keepers *keepers.AppKeepers, bondDenom string) error { + for _, account := range Core1VestingAccounts { + if err := MoveVestingCoinFromVestingAccount(ctx, + keepers, + bondDenom, + account.Owner, + sdk.MustAccAddressFromBech32(account.Address), + ); err != nil { + return err + } + } + return nil +} diff --git a/app/upgrades/v23/vesting.go b/app/upgrades/v23/vesting.go new file mode 100644 index 000000000..18b794f84 --- /dev/null +++ b/app/upgrades/v23/vesting.go @@ -0,0 +1,181 @@ +package v23 + +import ( + "fmt" + "time" + + "cosmossdk.io/math" + + sdk "github.com/cosmos/cosmos-sdk/types" + authvestingtypes "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" + + "github.com/CosmosContracts/juno/v23/app/keepers" +) + +// Stops a vesting account and returns all tokens back to the community pool +func MoveVestingCoinFromVestingAccount(ctx sdk.Context, keepers *keepers.AppKeepers, bondDenom string, owner string, accAddr sdk.AccAddress) error { + now := ctx.BlockHeader().Time + + stdAcc := keepers.AccountKeeper.GetAccount(ctx, accAddr) + vacc, ok := stdAcc.(*authvestingtypes.PeriodicVestingAccount) + if !ok { + // For e2e testing + fmt.Printf("account " + accAddr.String() + " is not a vesting account.\n") + return nil + } + + fmt.Printf("\n\n== Vesting Account Address: %s (%s) ==\n", vacc.GetAddress().String(), owner) + + // Gets vesting coins (These get returned back to community pool) + // we should filter vesting coins to only include the bondDenom + vestingCoins := vacc.GetVestingCoins(now) + fmt.Printf("All Vesting Coins: %v\n", vestingCoins) + vestingJuno := vestingCoins.AmountOf(bondDenom) + fmt.Printf("Vesting Junos: %v\n", vestingJuno) + + // Display locked & spendable funds + lockedCoins := keepers.BankKeeper.LockedCoins(ctx, accAddr) + fmt.Printf("Locked Coins: %v\n", lockedCoins) + spendableCoins := keepers.BankKeeper.SpendableCoins(ctx, accAddr) + fmt.Printf("Spendable Coins: %v\n", spendableCoins) + + // Instantly complete any re-deleations. + amt, err := completeAllRedelegations(ctx, now, keepers, accAddr) + if err != nil { + return err + } + fmt.Println("Redelegated Amount: ", amt) + + // Instantly unbond all delegations. + amt, err = unbondAllAndFinish(ctx, now, keepers, accAddr) + if err != nil { + return err + } + fmt.Println("Unbonded Amount: ", amt) + + // Community pool balance before transfer + cpoolBeforeBal := keepers.DistrKeeper.GetFeePool(ctx).CommunityPool + + // Set the vesting account to a base account + keepers.AccountKeeper.SetAccount(ctx, vacc.BaseAccount) + + // Moves vesting tokens to the council. + if err := transferUnvestedTokensToCommunityPool(ctx, keepers, accAddr, sdk.NewCoin(bondDenom, vestingJuno)); err != nil { + return err + } + + // Log new council balance + cpoolAfterBal := keepers.DistrKeeper.GetFeePool(ctx).CommunityPool + + fmt.Printf("Community Pool Balance Before: %v\n", cpoolBeforeBal) + fmt.Printf("Community Pool Balance After: %v\n", cpoolAfterBal) + + // Ensure the post validation checks are met. + err = postValidation(ctx, keepers, bondDenom, accAddr, vestingCoins, cpoolBeforeBal) + return err +} + +func postValidation(ctx sdk.Context, keepers *keepers.AppKeepers, bondDenom string, accAddr sdk.AccAddress, vestingCoins sdk.Coins, cpoolBeforeBal sdk.DecCoins) error { + // Community pool juno balance should only increase by exactly the vestedCoins + cpoolAfterBal := keepers.DistrKeeper.GetFeePool(ctx).CommunityPool + + // only count vesting junos + vestingJuno := vestingCoins.AmountOf(bondDenom) + + if !cpoolBeforeBal.AmountOf(bondDenom).Add(vestingJuno.ToLegacyDec()).Equal(cpoolAfterBal.AmountOf(bondDenom)) { + return fmt.Errorf("ERROR: community pool balance before (%v) + unvested juno (%v) from unvestedCoins (%v) != core1BalAfter (%v)", cpoolBeforeBal, vestingJuno, vestingCoins, cpoolAfterBal) + } + + // vesting account should have no future vesting periods + newVacc := keepers.AccountKeeper.GetAccount(ctx, accAddr) + if _, ok := newVacc.(*authvestingtypes.PeriodicVestingAccount); ok { + return fmt.Errorf("ERROR: account %s still is a vesting account", accAddr.String()) + } + + // ensure the account has 0 delegations, redelegations, or unbonding delegations, + delegations := keepers.StakingKeeper.GetAllDelegatorDelegations(ctx, accAddr) + if !(len(delegations) == 0) { + return fmt.Errorf("ERROR: account %s still has delegations", accAddr.String()) + } + + redelegations := keepers.StakingKeeper.GetRedelegations(ctx, accAddr, 65535) + if len(redelegations) != 0 { + return fmt.Errorf("ERROR: account %s still has redelegations", accAddr.String()) + } + + unbondingDelegations := keepers.StakingKeeper.GetAllUnbondingDelegations(ctx, accAddr) + if len(unbondingDelegations) != 0 { + return fmt.Errorf("ERROR: account %s still has unbonding delegations", accAddr.String()) + } + + return nil +} + +// Transfer funds from the vesting account to the Council SubDAO. +func transferUnvestedTokensToCommunityPool(ctx sdk.Context, keepers *keepers.AppKeepers, accAddr sdk.AccAddress, vestingJuno sdk.Coin) error { + fmt.Printf("Sending Vesting Juno to Community pool: %v\n", vestingJuno) + err := keepers.DistrKeeper.FundCommunityPool(ctx, sdk.NewCoins(vestingJuno), accAddr) + return err +} + +// Completes all re-delegations and returns the amount of tokens which were re-delegated. +func completeAllRedelegations(ctx sdk.Context, now time.Time, keepers *keepers.AppKeepers, accAddr sdk.AccAddress) (math.Int, error) { + redelegatedAmt := math.ZeroInt() + + for _, activeRedelegation := range keepers.StakingKeeper.GetRedelegations(ctx, accAddr, 65535) { + redelegationSrc, _ := sdk.ValAddressFromBech32(activeRedelegation.ValidatorSrcAddress) + redelegationDst, _ := sdk.ValAddressFromBech32(activeRedelegation.ValidatorDstAddress) + + // set all entry completionTime to now so we can complete re-delegation + for i := range activeRedelegation.Entries { + activeRedelegation.Entries[i].CompletionTime = now + redelegatedAmt = redelegatedAmt.Add(math.Int(activeRedelegation.Entries[i].SharesDst)) + } + + keepers.StakingKeeper.SetRedelegation(ctx, activeRedelegation) + _, err := keepers.StakingKeeper.CompleteRedelegation(ctx, accAddr, redelegationSrc, redelegationDst) + if err != nil { + return redelegatedAmt, err + } + } + + return redelegatedAmt, nil +} + +// Returns the amount of tokens which were unbonded (not rewards) +func unbondAllAndFinish(ctx sdk.Context, now time.Time, keepers *keepers.AppKeepers, accAddr sdk.AccAddress) (math.Int, error) { + unbondedAmt := math.ZeroInt() + + // Unbond all delegations from the account + for _, delegation := range keepers.StakingKeeper.GetAllDelegatorDelegations(ctx, accAddr) { + validatorValAddr := delegation.GetValidatorAddr() + if _, found := keepers.StakingKeeper.GetValidator(ctx, validatorValAddr); !found { + continue + } + + _, err := keepers.StakingKeeper.Undelegate(ctx, accAddr, validatorValAddr, delegation.GetShares()) + if err != nil { + return math.ZeroInt(), err + } + } + + // Take all unbonding and complete them. + for _, unbondingDelegation := range keepers.StakingKeeper.GetAllUnbondingDelegations(ctx, accAddr) { + validatorStringAddr := unbondingDelegation.ValidatorAddress + validatorValAddr, _ := sdk.ValAddressFromBech32(validatorStringAddr) + + // Complete unbonding delegation + for i := range unbondingDelegation.Entries { + unbondingDelegation.Entries[i].CompletionTime = now + unbondedAmt = unbondedAmt.Add(unbondingDelegation.Entries[i].Balance) + } + + keepers.StakingKeeper.SetUnbondingDelegation(ctx, unbondingDelegation) + _, err := keepers.StakingKeeper.CompleteUnbonding(ctx, accAddr, validatorValAddr) + if err != nil { + return math.ZeroInt(), err + } + } + + return unbondedAmt, nil +} diff --git a/interchaintest/chain_upgrade_test.go b/interchaintest/chain_upgrade_test.go index 8d4616e8f..ac674bcb0 100644 --- a/interchaintest/chain_upgrade_test.go +++ b/interchaintest/chain_upgrade_test.go @@ -24,7 +24,7 @@ import ( const ( chainName = "juno" - upgradeName = "v21" + upgradeName = "v23" haltHeightDelta = int64(9) // will propose upgrade this many blocks in the future blocksAfterUpgrade = int64(7) @@ -34,7 +34,7 @@ var ( // baseChain is the current version of the chain that will be upgraded from baseChain = ibc.DockerImage{ Repository: JunoMainRepo, - Version: "v20.0.0", + Version: "v22.0.1", UidGid: "1025:1025", } ) diff --git a/x/tokenfactory/keeper/bankactions.go b/x/tokenfactory/keeper/bankactions.go index 92b34f165..636d979b8 100644 --- a/x/tokenfactory/keeper/bankactions.go +++ b/x/tokenfactory/keeper/bankactions.go @@ -1,12 +1,6 @@ package keeper import ( - "fmt" - "sort" - - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/CosmosContracts/juno/v23/x/tokenfactory/types" @@ -29,8 +23,8 @@ func (k Keeper) mintTo(ctx sdk.Context, amount sdk.Coin, mintTo string) error { return err } - if k.bankKeeper.BlockedAddr(addr) { - return fmt.Errorf("failed to mint to blocked address: %s", addr) + if k.IsModuleAcc(ctx, addr) { + return types.ErrModuleAccount } return k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, @@ -50,8 +44,8 @@ func (k Keeper) burnFrom(ctx sdk.Context, amount sdk.Coin, burnFrom string) erro return err } - if k.bankKeeper.BlockedAddr(addr) { - return fmt.Errorf("failed to burn from blocked address: %s", addr) + if k.IsModuleAcc(ctx, addr) { + return types.ErrModuleAccount } err = k.bankKeeper.SendCoinsFromAccountToModule(ctx, @@ -77,36 +71,23 @@ func (k Keeper) forceTransfer(ctx sdk.Context, amount sdk.Coin, fromAddr string, return err } - sortedPermAddrs := make([]string, 0, len(k.permAddrs)) - for moduleName := range k.permAddrs { - sortedPermAddrs = append(sortedPermAddrs, moduleName) - } - sort.Strings(sortedPermAddrs) - - for _, moduleName := range sortedPermAddrs { - account := k.accountKeeper.GetModuleAccount(ctx, moduleName) - if account == nil { - return status.Errorf(codes.NotFound, "account %s not found", moduleName) - } - - if account.GetAddress().Equals(fromAcc) { - return status.Errorf(codes.Internal, "send from module acc not available") - } + if k.IsModuleAcc(ctx, fromAcc) { + return types.ErrModuleAccount } - fromSdkAddr, err := sdk.AccAddressFromBech32(fromAddr) + toAcc, err := sdk.AccAddressFromBech32(toAddr) if err != nil { return err } - toSdkAddr, err := sdk.AccAddressFromBech32(toAddr) - if err != nil { - return err + if k.IsModuleAcc(ctx, toAcc) { + return types.ErrModuleAccount } - if k.bankKeeper.BlockedAddr(toSdkAddr) { - return fmt.Errorf("failed to force transfer to blocked address: %s", toSdkAddr) - } + return k.bankKeeper.SendCoins(ctx, fromAcc, toAcc, sdk.NewCoins(amount)) +} - return k.bankKeeper.SendCoins(ctx, fromSdkAddr, toSdkAddr, sdk.NewCoins(amount)) +// IsModuleAcc checks if a given address is restricted +func (k Keeper) IsModuleAcc(_ sdk.Context, addr sdk.AccAddress) bool { + return k.permAddrMap[addr.String()] } diff --git a/x/tokenfactory/keeper/keeper.go b/x/tokenfactory/keeper/keeper.go index 7ee3bcc1e..30d2fb053 100644 --- a/x/tokenfactory/keeper/keeper.go +++ b/x/tokenfactory/keeper/keeper.go @@ -16,9 +16,10 @@ import ( type ( Keeper struct { - cdc codec.BinaryCodec - storeKey storetypes.StoreKey - permAddrs map[string]authtypes.PermissionsForAddress + cdc codec.BinaryCodec + storeKey storetypes.StoreKey + permAddrs map[string]authtypes.PermissionsForAddress + permAddrMap map[string]bool accountKeeper types.AccountKeeper bankKeeper types.BankKeeper @@ -44,8 +45,11 @@ func NewKeeper( authority string, ) Keeper { permAddrs := make(map[string]authtypes.PermissionsForAddress) + permAddrMap := make(map[string]bool) for name, perms := range maccPerms { - permAddrs[name] = authtypes.NewPermissionsForAddress(name, perms) + permsForAddr := authtypes.NewPermissionsForAddress(name, perms) + permAddrs[name] = permsForAddr + permAddrMap[permsForAddr.GetAddress().String()] = true } return Keeper{ @@ -53,6 +57,7 @@ func NewKeeper( storeKey: storeKey, permAddrs: permAddrs, + permAddrMap: permAddrMap, accountKeeper: accountKeeper, bankKeeper: bankKeeper, communityPoolKeeper: communityPoolKeeper, diff --git a/x/tokenfactory/types/errors.go b/x/tokenfactory/types/errors.go index 5a3c587d0..c04cc008c 100644 --- a/x/tokenfactory/types/errors.go +++ b/x/tokenfactory/types/errors.go @@ -20,4 +20,5 @@ var ( ErrCreatorTooLong = errorsmod.Register(ModuleName, 9, fmt.Sprintf("creator too long, max length is %d bytes", MaxCreatorLength)) ErrDenomDoesNotExist = errorsmod.Register(ModuleName, 10, "denom does not exist") ErrCapabilityNotEnabled = errorsmod.Register(ModuleName, 11, "this capability is not enabled on chain") + ErrModuleAccount = errorsmod.Register(ModuleName, 12, "interacting with module accounts not allowed") )