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

Undel host function #935

Merged
merged 28 commits into from
Sep 18, 2023
Merged
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
0c5c1ac
msg server handler + proto msg
riley-stride Sep 17, 2023
7543728
make proto-gen
riley-stride Sep 17, 2023
073791f
add msg server, undelegate host msg, cli tx and admin gate
riley-stride Sep 17, 2023
fd7732d
undelegatehost outline + copy of UnbondFromHostZone as UndelegateHost…
riley-stride Sep 17, 2023
0b37cd5
write undelegateHostEvmos
riley-stride Sep 17, 2023
a4c082d
clone undelegate callback
riley-stride Sep 17, 2023
6d87e5f
undelegate host
riley-stride Sep 17, 2023
6cdf5d7
isCallable -> isPrevented
riley-stride Sep 17, 2023
cc5c91a
isPrevented -> UndelegateHostPreventedKey, add helpers
riley-stride Sep 17, 2023
a3a895a
undelegate host shold propagate evmos error
riley-stride Sep 17, 2023
f5ae9e1
for reviewer: copy over UnbondFromHostZone in place of UndelegateHost…
riley-stride Sep 17, 2023
6e8be69
for reviewer: UndelegateHostEvmos (diffs vs UnbondFromHostZone)
riley-stride Sep 17, 2023
c02032a
fix callback, add custom error, set EvmosHostZoneChainId for production
riley-stride Sep 17, 2023
f01f388
incr max to 100m and wrap in big int
riley-stride Sep 17, 2023
fb33de3
flow splitDelegations through to callback, and copy UpdateDelegationB…
riley-stride Sep 17, 2023
20075fd
customise UpdateDelegationBalances copy for host (should update stake…
riley-stride Sep 17, 2023
64f52ad
set MaxNumTokensUnbondableStr at 10m
riley-stride Sep 18, 2023
9f2ca61
DecrementValidatorDelegationChangesInProgress in callback
riley-stride Sep 18, 2023
c6ff78f
unit test undelegatehostcallback
riley-stride Sep 18, 2023
ba55bc6
copy unbondhost test before customizing
riley-stride Sep 18, 2023
2a55210
undelegate host unit tests
riley-stride Sep 18, 2023
b6f4e4f
nit print fix
riley-stride Sep 18, 2023
89dda99
add kv unit test, nit cleanup, set max to 2.5mil
riley-stride Sep 18, 2023
5261d12
cleanup unit test
riley-stride Sep 18, 2023
701b452
UndelegateICABatchSize 30 -> 32
riley-stride Sep 18, 2023
c8a4bb9
merge with main
riley-stride Sep 18, 2023
d49cd00
add missing return in handler
riley-stride Sep 18, 2023
05b83ac
fixed unit tests
sampocs Sep 18, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions proto/stride/stakeibc/callbacks.proto
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,14 @@ message UndelegateCallback {
repeated uint64 epoch_unbonding_record_ids = 3;
}

message UndelegateHostCallback {
string amt = 1 [
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
(gogoproto.nullable) = false
];
repeated SplitDelegation split_delegations = 2;
}

message RedemptionCallback {
string host_zone_id = 1;
repeated uint64 epoch_unbonding_record_ids = 2;
Expand Down
10 changes: 10 additions & 0 deletions proto/stride/stakeibc/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ service Msg {
rpc CalibrateDelegation(MsgCalibrateDelegation)
returns (MsgCalibrateDelegationResponse);
rpc ClearBalance(MsgClearBalance) returns (MsgClearBalanceResponse);
rpc UndelegateHost(MsgUndelegateHost) returns (MsgUndelegateHostResponse);
rpc UpdateInnerRedemptionRateBounds(MsgUpdateInnerRedemptionRateBounds)
returns (MsgUpdateInnerRedemptionRateBoundsResponse);
}
Expand Down Expand Up @@ -172,6 +173,15 @@ message MsgUpdateValidatorSharesExchRate {
string valoper = 3;
}
message MsgUpdateValidatorSharesExchRateResponse {}
message MsgUndelegateHost {
string creator = 1;
string amount = 2 [
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
(gogoproto.nullable) = false
];

}
message MsgUndelegateHostResponse {}

message MsgCalibrateDelegation {
string creator = 1;
Expand Down
1 change: 1 addition & 0 deletions x/stakeibc/client/cli/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ func GetTxCmd() *cobra.Command {
cmd.AddCommand(CmdUpdateValidatorSharesExchRate())
cmd.AddCommand(CmdCalibrateDelegation())
cmd.AddCommand(CmdClearBalance())
cmd.AddCommand(CmdUndelegateHost())
cmd.AddCommand(CmdUpdateInnerRedemptionRateBounds())

return cmd
Expand Down
49 changes: 49 additions & 0 deletions x/stakeibc/client/cli/tx_undelegate_host.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package cli

import (
"strconv"

errorsmod "cosmossdk.io/errors"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/client/tx"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/spf13/cobra"

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

var _ = strconv.Itoa(0)

func CmdUndelegateHost() *cobra.Command {
cmd := &cobra.Command{
Use: "undelegate-host [amount]",
Short: "Broadcast message undelegate-host",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) (err error) {
argAmount, found := sdk.NewIntFromString(args[0])
if !found {
return errorsmod.Wrap(sdkerrors.ErrInvalidType, "can not convert string to int")
}

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

msg := types.NewMsgUndelegateHost(
clientCtx.GetFromAddress().String(),
argAmount,
)
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 @@ -58,6 +58,9 @@ func NewMessageHandler(k keeper.Keeper) sdk.Handler {
case *types.MsgUpdateValidatorSharesExchRate:
res, err := msgServer.UpdateValidatorSharesExchRate(sdk.WrapSDKContext(ctx), msg)
return sdk.WrapServiceResult(ctx, res, err)
case *types.MsgUndelegateHost:
res, err := msgServer.UndelegateHost(sdk.WrapSDKContext(ctx), msg)
return sdk.WrapServiceResult(ctx, res, err)
case *types.MsgCalibrateDelegation:
res, err := msgServer.CalibrateDelegation(sdk.WrapSDKContext(ctx), msg)
return sdk.WrapServiceResult(ctx, res, err)
Expand Down
16 changes: 9 additions & 7 deletions x/stakeibc/keeper/icacallbacks.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,22 @@ import (
)

const (
ICACallbackID_Delegate = "delegate"
ICACallbackID_Claim = "claim"
ICACallbackID_Undelegate = "undelegate"
ICACallbackID_Reinvest = "reinvest"
ICACallbackID_Redemption = "redemption"
ICACallbackID_Rebalance = "rebalance"
ICACallbackID_Detokenize = "detokenize"
ICACallbackID_Delegate = "delegate"
ICACallbackID_Claim = "claim"
ICACallbackID_Undelegate = "undelegate"
ICACallbackID_UndelegateHost = "undelegatehost"
ICACallbackID_Reinvest = "reinvest"
ICACallbackID_Redemption = "redemption"
ICACallbackID_Rebalance = "rebalance"
ICACallbackID_Detokenize = "detokenize"
)

func (k Keeper) Callbacks() icacallbackstypes.ModuleCallbacks {
return []icacallbackstypes.ICACallback{
{CallbackId: ICACallbackID_Delegate, CallbackFunc: icacallbackstypes.ICACallbackFunction(k.DelegateCallback)},
{CallbackId: ICACallbackID_Claim, CallbackFunc: icacallbackstypes.ICACallbackFunction(k.ClaimCallback)},
{CallbackId: ICACallbackID_Undelegate, CallbackFunc: icacallbackstypes.ICACallbackFunction(k.UndelegateCallback)},
{CallbackId: ICACallbackID_UndelegateHost, CallbackFunc: icacallbackstypes.ICACallbackFunction(k.UndelegateHostCallback)},
{CallbackId: ICACallbackID_Reinvest, CallbackFunc: icacallbackstypes.ICACallbackFunction(k.ReinvestCallback)},
{CallbackId: ICACallbackID_Redemption, CallbackFunc: icacallbackstypes.ICACallbackFunction(k.RedemptionCallback)},
{CallbackId: ICACallbackID_Rebalance, CallbackFunc: icacallbackstypes.ICACallbackFunction(k.RebalanceCallback)},
Expand Down
83 changes: 83 additions & 0 deletions x/stakeibc/keeper/icacallbacks_undelegate.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,3 +233,86 @@ func (k Keeper) BurnTokens(ctx sdk.Context, hostZone types.HostZone, stTokenBurn
k.Logger(ctx).Info(fmt.Sprintf("Total supply %s", k.bankKeeper.GetSupply(ctx, stCoinDenom)))
return nil
}

// ICA Callback after undelegating host
//
// If successful:
// * sets SetUndelegateHostPrevented
// If timeout:
// * Does nothing
// If failure:
// * Does nothing
func (k Keeper) UndelegateHostCallback(ctx sdk.Context, packet channeltypes.Packet, ackResponse *icacallbackstypes.AcknowledgementResponse, args []byte) error {
// Fetch callback args
var undelegateHostCallback types.UndelegateHostCallback
if err := proto.Unmarshal(args, &undelegateHostCallback); err != nil {
return errorsmod.Wrapf(types.ErrUnmarshalFailure, fmt.Sprintf("Unable to unmarshal undelegate host callback args: %s", err.Error()))
}
k.Logger(ctx).Info("Starting undelegate host callback for amount %v%s", undelegateHostCallback.Amt)

// Regardless of failure/success/timeout, indicate that this ICA has completed
hostZone, found := k.GetHostZone(ctx, EvmosHostZoneChainId)
if !found {
return errorsmod.Wrapf(sdkerrors.ErrKeyNotFound, "Host zone not found: %s", EvmosHostZoneChainId)
}
for _, splitDelegation := range undelegateHostCallback.SplitDelegations {
if err := k.DecrementValidatorDelegationChangesInProgress(&hostZone, splitDelegation.Validator); err != nil {
// TODO: Revert after v14 upgrade
if errors.Is(err, types.ErrInvalidValidatorDelegationUpdates) {
k.Logger(ctx).Error(utils.LogICACallbackWithHostZone(EvmosHostZoneChainId, ICACallbackID_Undelegate,
"Invariant failed - delegation changes in progress fell below 0 for %s", splitDelegation.Validator))
continue
}
return err
}
}
k.SetHostZone(ctx, hostZone)

// Check for timeout (ack nil)
if ackResponse.Status == icacallbackstypes.AckResponseStatus_TIMEOUT {
k.Logger(ctx).Error("UndelegateHostCallback Timeout:", icacallbackstypes.AckResponseStatus_TIMEOUT, packet)
return nil
}

// Check for a failed transaction (ack error)
if ackResponse.Status == icacallbackstypes.AckResponseStatus_FAILURE {
k.Logger(ctx).Error("UndelegateHostCallback failure (ack error):", icacallbackstypes.AckResponseStatus_FAILURE, packet)
return nil
}

// Get the host zone
evmosHost, found := k.GetHostZone(ctx, EvmosHostZoneChainId)
if !found {
return errorsmod.Wrapf(types.ErrHostZoneNotFound, "host zone %s not found", EvmosHostZoneChainId)
}

k.Logger(ctx).Info("UndelegateHostCallback success:", icacallbackstypes.AckResponseStatus_SUCCESS, packet)

// Update delegation balances
err := k.UpdateDelegationBalancesHost(ctx, evmosHost, undelegateHostCallback)
if err != nil {
k.Logger(ctx).Error(fmt.Sprintf("UndelegateCallback | %s", err.Error()))
return err
}

k.Logger(ctx).Info("UndelegateHostCallback: SetUndelegateHostPrevented")
if err := k.SetUndelegateHostPrevented(ctx); err != nil {
k.Logger(ctx).Error(fmt.Sprintf("UndelegateHostCallback failed due to SetUndelegateHostPrevented | %s", err.Error()))
return err
}

return nil
}

// Decrement the delegation field on host and each validator's delegations after a successful unbonding ICA
func (k Keeper) UpdateDelegationBalancesHost(ctx sdk.Context, hostZone types.HostZone, undelegateHostCallback types.UndelegateHostCallback) error {
// Undelegate from each validator and update Evmos staked balance, if successful
for _, undelegation := range undelegateHostCallback.SplitDelegations {
err := k.AddDelegationToValidator(ctx, &hostZone, undelegation.Validator, undelegation.Amount.Neg(), ICACallbackID_UndelegateHost)
if err != nil {
return err
}
}
k.SetHostZone(ctx, hostZone)
return nil
}
Loading