Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inflation Fix #951

Merged
merged 12 commits into from
Oct 14, 2023
12 changes: 12 additions & 0 deletions app/upgrades.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
v13 "github.com/Stride-Labs/stride/v15/app/upgrades/v13"
v14 "github.com/Stride-Labs/stride/v15/app/upgrades/v14"
v15 "github.com/Stride-Labs/stride/v15/app/upgrades/v15"
v16 "github.com/Stride-Labs/stride/v15/app/upgrades/v16"
v2 "github.com/Stride-Labs/stride/v15/app/upgrades/v2"
v3 "github.com/Stride-Labs/stride/v15/app/upgrades/v3"
v4 "github.com/Stride-Labs/stride/v15/app/upgrades/v4"
Expand Down Expand Up @@ -204,6 +205,17 @@ func (app *StrideApp) setupUpgradeHandlers(appOpts servertypes.AppOptions) {
),
)

// v16 upgrade handler
app.UpgradeKeeper.SetUpgradeHandler(
v16.UpgradeName,
v16.CreateUpgradeHandler(
app.mm,
app.configurator,
app.StakeibcKeeper,
app.RatelimitKeeper,
),
)

upgradeInfo, err := app.UpgradeKeeper.ReadUpgradeInfoFromDisk()
if err != nil {
panic(fmt.Errorf("Failed to read upgrade info from disk: %w", err))
Expand Down
44 changes: 44 additions & 0 deletions app/upgrades/v16/upgrades.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package v16

import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"
upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types"

ratelimitkeeper "github.com/Stride-Labs/stride/v15/x/ratelimit/keeper"
stakeibckeeper "github.com/Stride-Labs/stride/v15/x/stakeibc/keeper"
)

var (
UpgradeName = "v16"

CosmosHubChainId = "cosmoshub-4"
)

// CreateUpgradeHandler creates an SDK upgrade handler for v15
func CreateUpgradeHandler(
mm *module.Manager,
configurator module.Configurator,
stakeibcKeeper stakeibckeeper.Keeper,
ratelimitKeeper ratelimitkeeper.Keeper,
) upgradetypes.UpgradeHandler {
return func(ctx sdk.Context, _ upgradetypes.Plan, vm module.VersionMap) (module.VersionMap, error) {
ctx.Logger().Info("Starting upgrade v16...")

// unhalt Cosmos Hub host zone
ctx.Logger().Info("Unhalting Cosmos Hub...")
hostZone, found := stakeibcKeeper.GetHostZone(ctx, CosmosHubChainId)
if !found {
ctx.Logger().Error("Cosmos Hub host zone not found!")
} else {
hostZone.Halted = false
stakeibcKeeper.SetHostZone(ctx, hostZone)
}

// remove stuatom from rate limits
ctx.Logger().Info("Removing stuatom as a blacklisted asset...")
ratelimitKeeper.RemoveDenomFromBlacklist(ctx, "stuatom")

return mm.RunMigrations(ctx, configurator, vm)
}
}
73 changes: 73 additions & 0 deletions app/upgrades/v16/upgrades_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package v16_test

import (
"fmt"
"testing"

"github.com/stretchr/testify/suite"

"github.com/Stride-Labs/stride/v15/app/apptesting"
stakeibctypes "github.com/Stride-Labs/stride/v15/x/stakeibc/types"
)

type UpgradeTestSuite struct {
apptesting.AppTestHelper
}

func (s *UpgradeTestSuite) SetupTest() {
s.Setup()
}

var (
CosmosHubChainIdTest = "cosmoshub-4"
)

func TestKeeperTestSuite(t *testing.T) {
suite.Run(t, new(UpgradeTestSuite))
}

func (s *UpgradeTestSuite) TestUpgrade() {
dummyUpgradeHeight := int64(5)

// Setup the store before the ugprade
checkCosmosHubAfterUpgrade := s.SetupHostZonesBeforeUpgrade()

// Run the upgrade to set the bounds and clear pending queries
s.ConfirmUpgradeSucceededs("v16", dummyUpgradeHeight)

// Check the store after the upgrade
checkCosmosHubAfterUpgrade()
}

func (s *UpgradeTestSuite) SetupHostZonesBeforeUpgrade() func() {

// Create 10 dummy host zones
for i := 0; i < 10; i++ {
chainId := fmt.Sprintf("chain-%d", i)

hostZone := stakeibctypes.HostZone{
ChainId: chainId,
Halted: false,
}
s.App.StakeibcKeeper.SetHostZone(s.Ctx, hostZone)
}
// create Cosmos Hub Host Zone
hostZone := stakeibctypes.HostZone{
ChainId: CosmosHubChainIdTest,
Halted: true,
}
s.App.StakeibcKeeper.SetHostZone(s.Ctx, hostZone)

return func() {

hostZones := s.App.StakeibcKeeper.GetAllHostZone(s.Ctx)

for _, hostZone := range hostZones {
s.Require().False(hostZone.Halted, "host zone should not be halted")
}
// Confirm Cosmos Hub host zone is not unhalted
cosmosHubHostZone, found := s.App.StakeibcKeeper.GetHostZone(s.Ctx, CosmosHubChainIdTest)
s.Require().True(found, "Cosmos Hub host zone not found!")
s.Require().False(cosmosHubHostZone.Halted, "Cosmos Hub host zone should not be halted")
}
}
7 changes: 7 additions & 0 deletions proto/stride/stakeibc/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ service Msg {
rpc UndelegateHost(MsgUndelegateHost) returns (MsgUndelegateHostResponse);
rpc UpdateInnerRedemptionRateBounds(MsgUpdateInnerRedemptionRateBounds)
returns (MsgUpdateInnerRedemptionRateBoundsResponse);
rpc ResumeHostZone(MsgResumeHostZone) returns (MsgResumeHostZoneResponse);
}

message MsgUpdateInnerRedemptionRateBounds {
Expand Down Expand Up @@ -188,3 +189,9 @@ message MsgCalibrateDelegation {
string valoper = 3;
}
message MsgCalibrateDelegationResponse {}

message MsgResumeHostZone {
string creator = 1;
string chain_id = 2;
}
message MsgResumeHostZoneResponse {}
1 change: 1 addition & 0 deletions x/stakeibc/client/cli/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ func GetTxCmd() *cobra.Command {
cmd.AddCommand(CmdClearBalance())
cmd.AddCommand(CmdUndelegateHost())
cmd.AddCommand(CmdUpdateInnerRedemptionRateBounds())
cmd.AddCommand(CmdResumeHostZone())

return cmd
}
39 changes: 39 additions & 0 deletions x/stakeibc/client/cli/tx_resume_host_zone.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package cli

import (
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/client/tx"
"github.com/spf13/cobra"

"github.com/Stride-Labs/stride/v15/x/stakeibc/types"
)

func CmdResumeHostZone() *cobra.Command {
cmd := &cobra.Command{
Use: "resume-host-zone [chainid]",
Short: "Broadcast message resume-host-zone",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) (err error) {
argChainId := args[0]

clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}

msg := types.NewMsgResumeHostZone(
clientCtx.GetFromAddress().String(),
argChainId,
)
if err := msg.ValidateBasic(); err != nil {
return err
}
return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
},
}

flags.AddTxFlagsToCmd(cmd)

return cmd
}
3 changes: 3 additions & 0 deletions x/stakeibc/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ func NewMessageHandler(k keeper.Keeper) sdk.Handler {
case *types.MsgUpdateInnerRedemptionRateBounds:
res, err := msgServer.UpdateInnerRedemptionRateBounds(sdk.WrapSDKContext(ctx), msg)
return sdk.WrapServiceResult(ctx, res, err)
case *types.MsgResumeHostZone:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we test this on dockernet as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Definitely

res, err := msgServer.ResumeHostZone(sdk.WrapSDKContext(ctx), msg)
return sdk.WrapServiceResult(ctx, res, err)
default:
errMsg := fmt.Sprintf("unrecognized %s message type: %T", types.ModuleName, msg)
return nil, errorsmod.Wrap(sdkerrors.ErrUnknownRequest, errMsg)
Expand Down
4 changes: 4 additions & 0 deletions x/stakeibc/keeper/msg_server_lsm_liquid_stake.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ func (k Keeper) StartLSMLiquidStake(ctx sdk.Context, msg types.MsgLSMLiquidStake
}
hostZone := lsmLiquidStake.HostZone

if hostZone.Halted {
return types.LSMLiquidStake{}, errorsmod.Wrapf(types.ErrHaltedHostZone, "host zone %s is halted", hostZone.ChainId)
}

// Check if we already have tokens with this denom in records
_, found := k.RecordsKeeper.GetLSMTokenDeposit(ctx, hostZone.ChainId, lsmLiquidStake.Deposit.Denom)
if found {
Expand Down
40 changes: 40 additions & 0 deletions x/stakeibc/keeper/msg_server_resume_host_zone.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package keeper

import (
"context"
"fmt"

"github.com/Stride-Labs/stride/v15/x/stakeibc/types"

errorsmod "cosmossdk.io/errors"
sdk "github.com/cosmos/cosmos-sdk/types"
)

func (k msgServer) ResumeHostZone(goCtx context.Context, msg *types.MsgResumeHostZone) (*types.MsgResumeHostZoneResponse, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We also need to remove the blacklisted denom right?

ctx := sdk.UnwrapSDKContext(goCtx)

// Get Host Zone
hostZone, found := k.GetHostZone(ctx, msg.ChainId)
if !found {
errMsg := fmt.Sprintf("invalid chain id, zone for %s not found", msg.ChainId)
k.Logger(ctx).Error(errMsg)
return nil, errorsmod.Wrapf(types.ErrHostZoneNotFound, errMsg)
}

// Check the zone is halted
if !hostZone.Halted {
errMsg := fmt.Sprintf("invalid chain id, zone for %s not halted", msg.ChainId)
k.Logger(ctx).Error(errMsg)
return nil, errorsmod.Wrapf(types.ErrHostZoneNotHalted, errMsg)
}

// remove from blacklist
stDenom := types.StAssetDenomFromHostZoneDenom(hostZone.HostDenom)
k.RatelimitKeeper.RemoveDenomFromBlacklist(ctx, stDenom)

// Resume zone
hostZone.Halted = false
k.SetHostZone(ctx, hostZone)

return &types.MsgResumeHostZoneResponse{}, nil
}
99 changes: 99 additions & 0 deletions x/stakeibc/keeper/msg_server_resume_host_zone_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package keeper_test

import (
"fmt"

sdk "github.com/cosmos/cosmos-sdk/types"
_ "github.com/stretchr/testify/suite"

stakeibctypes "github.com/Stride-Labs/stride/v15/x/stakeibc/types"
)

type ResumeHostZoneTestCase struct {
validMsg stakeibctypes.MsgResumeHostZone
zone stakeibctypes.HostZone
}

func (s *KeeperTestSuite) SetupResumeHostZone() ResumeHostZoneTestCase {
// Register a host zone
hostZone := stakeibctypes.HostZone{
ChainId: HostChainId,
HostDenom: Atom,
IbcDenom: IbcAtom,
RedemptionRate: sdk.NewDec(1.0),
MinRedemptionRate: sdk.NewDec(9).Quo(sdk.NewDec(10)),
MaxRedemptionRate: sdk.NewDec(15).Quo(sdk.NewDec(10)),
Halted: true,
}

s.App.StakeibcKeeper.SetHostZone(s.Ctx, hostZone)

defaultMsg := stakeibctypes.MsgResumeHostZone{
Creator: s.TestAccs[0].String(),
ChainId: HostChainId,
}

return ResumeHostZoneTestCase{
validMsg: defaultMsg,
zone: hostZone,
}
}

// Verify that bounds can be set successfully
func (s *KeeperTestSuite) TestResumeHostZone_Success() {
tc := s.SetupResumeHostZone()

// Set the inner bounds on the host zone
_, err := s.GetMsgServer().ResumeHostZone(s.Ctx, &tc.validMsg)
s.Require().NoError(err, "should not throw an error")

// Confirm the inner bounds were set
zone, found := s.App.StakeibcKeeper.GetHostZone(s.Ctx, HostChainId)
s.Require().True(found, "host zone should be in the store")

s.Require().False(zone.Halted, "host zone should not be halted")
}

// verify that non-admins can't call the tx
func (s *KeeperTestSuite) TestResumeHostZone_NonAdmin() {
tc := s.SetupResumeHostZone()

invalidMsg := tc.validMsg
invalidMsg.Creator = s.TestAccs[1].String()

err := invalidMsg.ValidateBasic()
s.Require().Error(err, "nonadmins shouldn't be able to call this tx")
}

// verify that the function can't be called on missing zones
func (s *KeeperTestSuite) TestResumeHostZone_MissingZones() {
tc := s.SetupResumeHostZone()

invalidMsg := tc.validMsg
invalidChainId := "invalid-chain"
invalidMsg.ChainId = invalidChainId

// Set the inner bounds on the host zone
_, err := s.GetMsgServer().ResumeHostZone(s.Ctx, &invalidMsg)

s.Require().Error(err, "shouldn't be able to call tx on missing zones")
expectedErrorMsg := fmt.Sprintf("invalid chain id, zone for %s not found: host zone not found", invalidChainId)
s.Require().Equal(expectedErrorMsg, err.Error(), "should return correct error msg")
}

// verify that the function can't be called on unhalted zones
func (s *KeeperTestSuite) TestResumeHostZone_UnhaltedZones() {
tc := s.SetupResumeHostZone()

zone, found := s.App.StakeibcKeeper.GetHostZone(s.Ctx, HostChainId)
s.Require().True(found, "host zone should be in the store")
s.Require().True(zone.Halted, "host zone should be halted")
zone.Halted = false
s.App.StakeibcKeeper.SetHostZone(s.Ctx, zone)

// Set the inner bounds on the host zone
_, err := s.GetMsgServer().ResumeHostZone(s.Ctx, &tc.validMsg)
s.Require().Error(err, "shouldn't be able to call tx on unhalted zones")
expectedErrorMsg := fmt.Sprintf("invalid chain id, zone for %s not halted: host zone is not halted", HostChainId)
s.Require().Equal(expectedErrorMsg, err.Error(), "should return correct error msg")
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ func (k msgServer) UpdateInnerRedemptionRateBounds(goCtx context.Context, msg *t
// Set the inner bounds on the host zone
zone.MinInnerRedemptionRate = innerMinSafetyThreshold
zone.MaxInnerRedemptionRate = innerMaxSafetyThreshold

k.SetHostZone(ctx, zone)

return &types.MsgUpdateInnerRedemptionRateBoundsResponse{}, nil
Expand Down
2 changes: 2 additions & 0 deletions x/stakeibc/types/codec.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ func RegisterCodec(cdc *codec.LegacyAmino) {
cdc.RegisterConcrete(&MsgUndelegateHost{}, "stakeibc/UndelegateHost", nil)
cdc.RegisterConcrete(&MsgCalibrateDelegation{}, "stakeibc/CalibrateDelegation", nil)
cdc.RegisterConcrete(&MsgUpdateInnerRedemptionRateBounds{}, "stakeibc/UpdateInnerRedemptionRateBounds", nil)
cdc.RegisterConcrete(&MsgResumeHostZone{}, "stakeibc/ResumeHostZone", nil)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we test this with ledger?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a good point, let's test it in Dockernet

// this line is used by starport scaffolding # 2
}

Expand All @@ -45,6 +46,7 @@ func RegisterInterfaces(registry cdctypes.InterfaceRegistry) {
&MsgUndelegateHost{},
&MsgCalibrateDelegation{},
&MsgUpdateInnerRedemptionRateBounds{},
&MsgResumeHostZone{},
)

registry.RegisterImplementations((*govtypes.Content)(nil),
Expand Down
Loading