From ed8a8a5f53ab1a08d309fc406444799aa2e93f36 Mon Sep 17 00:00:00 2001 From: sampocs Date: Wed, 27 Mar 2024 15:39:34 -0500 Subject: [PATCH] added function to unregister host zone (#1166) --- x/stakeibc/keeper/host_zone.go | 50 ++++++++++++++++++++++++++ x/stakeibc/keeper/msg_server_test.go | 54 ++++++++++++++++++++++++++++ x/stakeibc/types/expected_keepers.go | 2 ++ 3 files changed, 106 insertions(+) diff --git a/x/stakeibc/keeper/host_zone.go b/x/stakeibc/keeper/host_zone.go index 88b8c81318..d508b1dc6c 100644 --- a/x/stakeibc/keeper/host_zone.go +++ b/x/stakeibc/keeper/host_zone.go @@ -11,6 +11,7 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/Stride-Labs/stride/v20/utils" + recordstypes "github.com/Stride-Labs/stride/v20/x/records/types" "github.com/Stride-Labs/stride/v20/x/stakeibc/types" ) @@ -112,6 +113,55 @@ func (k Keeper) GetAllHostZone(ctx sdk.Context) (list []types.HostZone) { return } +// Unregisters a host zone, including removing the module accounts and records +func (k Keeper) UnregisterHostZone(ctx sdk.Context, chainId string) error { + hostZone, found := k.GetHostZone(ctx, chainId) + if !found { + return types.ErrHostZoneNotFound.Wrapf("host zone %s not found", chainId) + } + + // Remove module accounts + depositAddress := types.NewHostZoneDepositAddress(chainId) + communityPoolStakeAddress := types.NewHostZoneModuleAddress(chainId, CommunityPoolStakeHoldingAddressKey) + communityPoolRedeemAddress := types.NewHostZoneModuleAddress(chainId, CommunityPoolRedeemHoldingAddressKey) + + k.AccountKeeper.RemoveAccount(ctx, k.AccountKeeper.GetAccount(ctx, depositAddress)) + k.AccountKeeper.RemoveAccount(ctx, k.AccountKeeper.GetAccount(ctx, communityPoolStakeAddress)) + k.AccountKeeper.RemoveAccount(ctx, k.AccountKeeper.GetAccount(ctx, communityPoolRedeemAddress)) + + // Remove all deposit records for the host zone + for _, depositRecord := range k.RecordsKeeper.GetAllDepositRecord(ctx) { + if depositRecord.HostZoneId == chainId { + k.RecordsKeeper.RemoveDepositRecord(ctx, depositRecord.Id) + } + } + + // Remove all epoch unbonding records for the host zone + for _, epochUnbondingRecord := range k.RecordsKeeper.GetAllEpochUnbondingRecord(ctx) { + updatedHostZoneUnbondings := []*recordstypes.HostZoneUnbonding{} + for _, hostZoneUnbonding := range epochUnbondingRecord.HostZoneUnbondings { + if hostZoneUnbonding.HostZoneId != chainId { + updatedHostZoneUnbondings = append(updatedHostZoneUnbondings, hostZoneUnbonding) + } + } + epochUnbondingRecord.HostZoneUnbondings = updatedHostZoneUnbondings + k.RecordsKeeper.SetEpochUnbondingRecord(ctx, epochUnbondingRecord) + } + + // Remove whitelisted address pairs from rate limit module + rewardCollectorAddress := k.AccountKeeper.GetModuleAccount(ctx, types.RewardCollectorName).GetAddress() + k.RatelimitKeeper.RemoveWhitelistedAddressPair(ctx, hostZone.DepositAddress, hostZone.DelegationIcaAddress) + k.RatelimitKeeper.RemoveWhitelistedAddressPair(ctx, hostZone.FeeIcaAddress, rewardCollectorAddress.String()) + k.RatelimitKeeper.RemoveWhitelistedAddressPair(ctx, hostZone.CommunityPoolDepositIcaAddress, hostZone.CommunityPoolStakeHoldingAddress) + k.RatelimitKeeper.RemoveWhitelistedAddressPair(ctx, hostZone.CommunityPoolDepositIcaAddress, hostZone.CommunityPoolRedeemHoldingAddress) + k.RatelimitKeeper.RemoveWhitelistedAddressPair(ctx, hostZone.CommunityPoolStakeHoldingAddress, hostZone.CommunityPoolReturnIcaAddress) + + // Finally, remove the host zone struct + k.RemoveHostZone(ctx, chainId) + + return nil +} + // GetAllActiveHostZone returns all hostZones that are active (halted = false) func (k Keeper) GetAllActiveHostZone(ctx sdk.Context) (list []types.HostZone) { store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.HostZoneKey)) diff --git a/x/stakeibc/keeper/msg_server_test.go b/x/stakeibc/keeper/msg_server_test.go index 5dd2e3c8de..c5f9337fe3 100644 --- a/x/stakeibc/keeper/msg_server_test.go +++ b/x/stakeibc/keeper/msg_server_test.go @@ -185,6 +185,60 @@ func (s *KeeperTestSuite) TestRegisterHostZone_Success_SetCommunityPoolTreasuryA s.Require().Equal(ValidHostAddress, hostZone.CommunityPoolTreasuryAddress, "treasury address") } +func (s *KeeperTestSuite) TestRegisterHostZone_Success_Unregister() { + tc := s.SetupRegisterHostZone() + msg := tc.validMsg + + // Register the host zone with the valid message + _, err := s.GetMsgServer().RegisterHostZone(sdk.WrapSDKContext(s.Ctx), &msg) + s.Require().NoError(err, "no error expected when registering host") + + // Confirm accounts were created + depositAddress := types.NewHostZoneDepositAddress(chainId) + communityPoolStakeAddress := types.NewHostZoneModuleAddress(chainId, keeper.CommunityPoolStakeHoldingAddressKey) + communityPoolRedeemAddress := types.NewHostZoneModuleAddress(chainId, keeper.CommunityPoolRedeemHoldingAddressKey) + + depositAccount := s.App.AccountKeeper.GetAccount(s.Ctx, depositAddress) + communityPoolStakeAccount := s.App.AccountKeeper.GetAccount(s.Ctx, communityPoolStakeAddress) + communityPoolRedeemAccount := s.App.AccountKeeper.GetAccount(s.Ctx, communityPoolRedeemAddress) + + s.Require().NotNil(depositAccount, "deposit account should exist") + s.Require().NotNil(communityPoolStakeAccount, "community pool stake account should exist") + s.Require().NotNil(communityPoolRedeemAccount, "community pool redeem account should exist") + + // Confirm records were created + depositRecords := s.App.RecordsKeeper.GetAllDepositRecord(s.Ctx) + s.Require().Len(depositRecords, 1, "there should be one deposit record") + + epochUnbondingRecords := s.App.RecordsKeeper.GetAllEpochUnbondingRecord(s.Ctx) + s.Require().Len(epochUnbondingRecords, 1, "there should be one epoch unbonding record") + s.Require().Len(epochUnbondingRecords[0].HostZoneUnbondings, 1, "there should be one host zone unbonding record") + + // Unregister the host zone + err = s.App.StakeibcKeeper.UnregisterHostZone(s.Ctx, HostChainId) + s.Require().NoError(err, "no error expected when unregistering host zone") + + // Confirm accounts were deleted + depositAccount = s.App.AccountKeeper.GetAccount(s.Ctx, depositAddress) + communityPoolStakeAccount = s.App.AccountKeeper.GetAccount(s.Ctx, communityPoolStakeAddress) + communityPoolRedeemAccount = s.App.AccountKeeper.GetAccount(s.Ctx, communityPoolRedeemAddress) + + s.Require().Nil(depositAccount, "deposit account should have been deleted") + s.Require().Nil(communityPoolStakeAccount, "community pool stake account should have been deleted") + s.Require().Nil(communityPoolRedeemAccount, "community pool redeem account should have been deleted") + + // Confirm records were deleted + depositRecords = s.App.RecordsKeeper.GetAllDepositRecord(s.Ctx) + s.Require().Empty(depositRecords, "deposit records should have been deleted") + + epochUnbondingRecords = s.App.RecordsKeeper.GetAllEpochUnbondingRecord(s.Ctx) + s.Require().Empty(epochUnbondingRecords[0].HostZoneUnbondings, "host zone unbonding record should have been deleted") + + // Attempt to re-register, it should succeed + _, err = s.GetMsgServer().RegisterHostZone(sdk.WrapSDKContext(s.Ctx), &msg) + s.Require().NoError(err, "no error expected when re-registering host") +} + func (s *KeeperTestSuite) TestRegisterHostZone_InvalidConnectionId() { tc := s.SetupRegisterHostZone() msg := tc.validMsg diff --git a/x/stakeibc/types/expected_keepers.go b/x/stakeibc/types/expected_keepers.go index 76487ff121..95a013f7ca 100644 --- a/x/stakeibc/types/expected_keepers.go +++ b/x/stakeibc/types/expected_keepers.go @@ -17,6 +17,7 @@ type AccountKeeper interface { SetAccount(ctx sdk.Context, acc authtypes.AccountI) GetAccount(ctx sdk.Context, addr sdk.AccAddress) types.AccountI GetModuleAccount(ctx sdk.Context, moduleName string) types.ModuleAccountI + RemoveAccount(ctx sdk.Context, acc authtypes.AccountI) } // BankKeeper defines the expected interface needed to retrieve account balances. @@ -49,6 +50,7 @@ type RatelimitKeeper interface { AddDenomToBlacklist(ctx sdk.Context, denom string) RemoveDenomFromBlacklist(ctx sdk.Context, denom string) SetWhitelistedAddressPair(ctx sdk.Context, whitelist ratelimittypes.WhitelistedAddressPair) + RemoveWhitelistedAddressPair(ctx sdk.Context, sender, receiver string) } type ConsumerKeeper interface {