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

[LUM-879] Fix deposit retry #70

Merged
merged 2 commits into from
Feb 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions x/millions/keeper/callbacks_bank_send.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ func BankSendCallback(k Keeper, ctx sdk.Context, packet channeltypes.Packet, ack
return err
}

// Scenarios:
// - Timeout: Does nothing, handled when restoring the ICA channel
// - Error: Put entity in error state to allow users to retry
if ackResponse.Status == icacallbackstypes.AckResponseStatus_TIMEOUT {
k.Logger(ctx).Debug("Received timeout for a bank send to native packet")
} else if ackResponse.Status == icacallbackstypes.AckResponseStatus_FAILURE {
Expand Down
5 changes: 3 additions & 2 deletions x/millions/keeper/callbacks_claim.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,9 @@ func ClaimCallback(k Keeper, ctx sdk.Context, packet channeltypes.Packet, ackRes
return errorsmod.Wrapf(types.ErrUnmarshalFailure, fmt.Sprintf("Unable to unmarshal claim callback args: %s", err.Error()))
}

// If the response status is a timeout, that's not an "error" since the relayer will retry then fail or succeed.
// We just log it out and return no error
// Scenarios:
// - Timeout: Does nothing, handled when restoring the ICA channel
// - Error: Put entity in error state to allow users to retry
if ackResponse.Status == icacallbackstypes.AckResponseStatus_TIMEOUT {
k.Logger(ctx).Debug("Received timeout for a claim packet")
} else if ackResponse.Status == icacallbackstypes.AckResponseStatus_FAILURE {
Expand Down
5 changes: 3 additions & 2 deletions x/millions/keeper/callbacks_delegate.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,9 @@ func DelegateCallback(k Keeper, ctx sdk.Context, packet channeltypes.Packet, ack
return errorsmod.Wrapf(types.ErrUnmarshalFailure, fmt.Sprintf("Unable to unmarshal delegate callback args: %s", err.Error()))
}

// If the response status is a timeout, that's not an "error" since the relayer will retry then fail or succeed.
// We just log it out and return no error
// Scenarios:
// - Timeout: Does nothing, handled when restoring the ICA channel
// - Error: Put entity in error state to allow users to retry
if ackResponse.Status == icacallbackstypes.AckResponseStatus_TIMEOUT {
k.Logger(ctx).Debug("Received timeout for a delegate packet")
} else if ackResponse.Status == icacallbackstypes.AckResponseStatus_FAILURE {
Expand Down
6 changes: 4 additions & 2 deletions x/millions/keeper/callbacks_redelegate.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,12 @@ func RedelegateCallback(k Keeper, ctx sdk.Context, packet channeltypes.Packet, a
return err
}

// If the response status is a timeout, that's not an "error" since the relayer will retry then fail or succeed.
// We just log it out and return no error
// Scenarios:
// - Timeout: Treated as an error (see below)
// - Error: Revert Pool validator set known split
if ackResponse.Status == icacallbackstypes.AckResponseStatus_TIMEOUT {
k.Logger(ctx).Debug("Received timeout for a redelegate packet")
return k.OnRedelegateToActiveValidatorsOnRemoteZoneCompleted(ctx, redelegateCallback.GetPoolId(), redelegateCallback.GetOperatorAddress(), redelegateCallback.GetSplitDelegations(), true)
} else if ackResponse.Status == icacallbackstypes.AckResponseStatus_FAILURE {
k.Logger(ctx).Debug("Received failure for a redelegate packet")
return k.OnRedelegateToActiveValidatorsOnRemoteZoneCompleted(ctx, redelegateCallback.GetPoolId(), redelegateCallback.GetOperatorAddress(), redelegateCallback.GetSplitDelegations(), true)
Expand Down
6 changes: 4 additions & 2 deletions x/millions/keeper/callbacks_set_withdraw_address.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,10 @@ func SetWithdrawAddressCallback(k Keeper, ctx sdk.Context, packet channeltypes.P
return err
}

// If the response status is a timeout, that's not an "error" since the relayer will retry then fail or succeed.
// We just log it out and return no error
// Scenarios:
// This operation is done right after the ICA channel open procedure
// - Timeout: Does nothing - fix may be needed manually
// - Error: Does nothing - fix may be needed manually
if ackResponse.Status == icacallbackstypes.AckResponseStatus_TIMEOUT {
k.Logger(ctx).Debug("Received timeout for a set withdraw address packet")
} else if ackResponse.Status == icacallbackstypes.AckResponseStatus_FAILURE {
Expand Down
5 changes: 3 additions & 2 deletions x/millions/keeper/callbacks_transfer_from_native.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,9 @@ func TransferFromNativeCallback(k Keeper, ctx sdk.Context, packet channeltypes.P
return err
}

// If the response status is a timeout, that's not an "error" since the relayer will retry then fail or succeed.
// We just log it out and return no error
// Scenarios:
// - Timeout: Does nothing, handled when restoring the ICA channel
// - Error: Put entity in error state to allow users to retry
if ackResponse.Status == icacallbackstypes.AckResponseStatus_TIMEOUT {
k.Logger(ctx).Debug("Received timeout for a transfer from native packet")
} else if ackResponse.Status == icacallbackstypes.AckResponseStatus_FAILURE {
Expand Down
6 changes: 4 additions & 2 deletions x/millions/keeper/callbacks_transfer_to_native.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,12 @@ func TransferToNativeCallback(k Keeper, ctx sdk.Context, packet channeltypes.Pac
return errorsmod.Wrapf(types.ErrUnmarshalFailure, fmt.Sprintf("Unable to unmarshal transfer to native callback args: %s", err.Error()))
}

// If the response status is a timeout, that's not an "error" since the relayer will retry then fail or succeed.
// We just log it out and return no error
// Scenarios:
// - Timeout: Treated as an error (see below)
// - Error: Put entity in error state to allow users to retry
if ackResponse.Status == icacallbackstypes.AckResponseStatus_TIMEOUT {
k.Logger(ctx).Debug("Received timeout for a transfer to native packet")
return k.OnTransferDepositToRemoteZoneCompleted(ctx, transferCallback.GetPoolId(), transferCallback.GetDepositId(), true)
} else if ackResponse.Status == icacallbackstypes.AckResponseStatus_FAILURE {
k.Logger(ctx).Debug("Received failure for a transfer to native packet")
return k.OnTransferDepositToRemoteZoneCompleted(ctx, transferCallback.GetPoolId(), transferCallback.GetDepositId(), true)
Expand Down
6 changes: 3 additions & 3 deletions x/millions/keeper/callbacks_undelegate.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,13 @@ func UndelegateCallback(k Keeper, ctx sdk.Context, packet channeltypes.Packet, a
return err
}

// If the response status is a timeout, that's not an "error" since the relayer will retry then fail or succeed.
// We just log it out and return no error
// Scenarios:
// - Timeout: Does nothing, handled when restoring the ICA channel
// - Error: Put back entities in the queue automatically
if ackResponse.Status == icacallbackstypes.AckResponseStatus_TIMEOUT {
k.Logger(ctx).Debug("Received timeout for an undelegate packet")
} else if ackResponse.Status == icacallbackstypes.AckResponseStatus_FAILURE {
k.Logger(ctx).Debug("Received failure for an undelegate packet")
// Failed OnUndelegateEpochUnbondingOnRemoteZoneCompleted
return k.OnUndelegateWithdrawalsOnRemoteZoneCompleted(
ctx,
undelegateCallback.PoolId,
Expand Down
17 changes: 4 additions & 13 deletions x/millions/keeper/msg_server_deposit.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,18 +126,9 @@ func (k msgServer) DepositRetry(goCtx context.Context, msg *types.MsgDepositRetr
return nil, types.ErrInvalidDepositorAddress
}

// State should be set to failure in order to retry something
if deposit.State != types.DepositState_Failure {
return nil, errorsmod.Wrapf(
types.ErrInvalidDepositState,
"state is %s instead of %s",
deposit.State.String(), types.DepositState_Failure.String(),
)
}

newState := types.DepositState_Unspecified
if deposit.State == types.DepositState_IbcTransfer && ctx.BlockTime().After(deposit.UpdatedAt.Add(2*types.IBCTimeoutNanos*time.Nanosecond)) {
// Handle IBC stucked operation for more than twice the specified IBC timeout
if deposit.State == types.DepositState_IbcTransfer && ctx.BlockTime().After(deposit.UpdatedAt.Add(types.IBCForceRetryNanos*time.Nanosecond)) {
// Handle IBC stucked operation for more than 3 days (no timeout received)
newState = types.DepositState_IbcTransfer
if err := k.TransferDepositToRemoteZone(ctx, deposit.PoolId, deposit.DepositId); err != nil {
return nil, err
Expand All @@ -157,8 +148,8 @@ func (k msgServer) DepositRetry(goCtx context.Context, msg *types.MsgDepositRetr
} else {
return nil, errorsmod.Wrapf(
types.ErrInvalidDepositState,
"error_state is %s instead of %s or %s",
deposit.ErrorState.String(), types.DepositState_IbcTransfer.String(), types.DepositState_IcaDelegate.String(),
"state is %s instead of %s",
deposit.State.String(), types.DepositState_Failure.String(),
)
}

Expand Down
4 changes: 2 additions & 2 deletions x/millions/keeper/msg_server_pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ func (k msgServer) restoreICADepositEntities(ctx sdk.Context, poolID uint64) {
// Restore withdrawals ICA locked operations on ICADeposit account
withdrawals := k.Keeper.ListPoolWithdrawals(ctx, poolID)
for _, w := range withdrawals {
if w.State == types.WithdrawalState_IcaUndelegate {
if w.State == types.WithdrawalState_IcaUndelegate || w.State == types.WithdrawalState_IbcTransfer {
w.ErrorState = w.State
w.State = types.WithdrawalState_Failure
k.Keeper.setPoolWithdrawal(ctx, w)
Expand All @@ -98,7 +98,7 @@ func (k msgServer) restoreICADepositEntities(ctx sdk.Context, poolID uint64) {
// Restore draws ICA locked operations on ICADeposit account
draws := k.Keeper.ListPoolDraws(ctx, poolID)
for _, d := range draws {
if d.State == types.DrawState_IcaWithdrawRewards {
if d.State == types.DrawState_IcaWithdrawRewards || d.State == types.DrawState_IbcTransfer {
d.ErrorState = d.State
d.State = types.DrawState_Failure
k.Keeper.SetPoolDraw(ctx, d)
Expand Down
4 changes: 3 additions & 1 deletion x/millions/keeper/queries_balance.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@ func BalanceCallback(k Keeper, ctx sdk.Context, args []byte, query icqueriestype
return errorsmod.Wrapf(types.ErrPoolNotFound, "no registered draw %d for pool %d", drawID, poolID)
}

// Based on type, we want different processing
// Scenarios:
// - Timeout: Treated as an error (see below)
// - Error: Put entity in error state to allow users to retry
if status == icqueriestypes.QueryResponseStatus_TIMEOUT {
k.Logger(ctx).Error(fmt.Sprintf("QUERY TIMEOUT - QueryId: %s, TTL: %d, BlockTime: %d", query.Id, query.TimeoutTimestamp, ctx.BlockHeader().Time.UnixNano()))
_, err := k.OnQueryFreshPrizePoolCoinsOnRemoteZoneCompleted(ctx, pool.GetPoolId(), draw.GetDrawId(), sdk.NewCoins(), true)
Expand Down
3 changes: 2 additions & 1 deletion x/millions/types/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ const (
)

const (
IBCTimeoutNanos = 1_800_000_000_000 // 30 minutes
IBCTimeoutNanos = 1_800_000_000_000 // 30 minutes
IBCForceRetryNanos = 259_200_000_000_000 // 3 days
)

const (
Expand Down
Loading