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

autopilot fallback address #1039

Merged
merged 34 commits into from
Jan 11, 2024
Merged
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
d3e0501
pull in LS + forward changes from https://github.com/Stride-Labs/stri…
asalzmann Nov 28, 2023
1c6ed0b
integration test working
asalzmann Nov 28, 2023
596250b
fix unittest
asalzmann Nov 28, 2023
220c8f1
restrict to either autopilot or pfm
sampocs Dec 21, 2023
528994a
added hash receiver helper
sampocs Dec 22, 2023
264cc28
added bank keeper to autopilot
sampocs Dec 22, 2023
45b1465
first pass at hashed recipient implementation
sampocs Dec 22, 2023
99db724
cleaned up variable names
sampocs Dec 22, 2023
f9d5d09
cleanup and docs
sampocs Dec 22, 2023
3bdfcd7
moved types to autopilot
sampocs Dec 22, 2023
eb408d2
walked back almost all the changes from above :(
sampocs Dec 22, 2023
8ee8ef2
cleanup again
sampocs Dec 22, 2023
b1d4503
added bank keeper again
sampocs Dec 27, 2023
208eac7
added bank send to hashed sender
sampocs Dec 27, 2023
2c22d17
renamed hashed address function
sampocs Dec 28, 2023
c0ca4f5
added keepers to store fallback address
sampocs Dec 28, 2023
a119b8d
implemented onAck and onTimeout
sampocs Dec 28, 2023
f102574
added unit test for helpers
sampocs Dec 28, 2023
619c480
added unit tests for on ack packet
sampocs Dec 29, 2023
baaeb50
added unit tests for full callbacks
sampocs Dec 29, 2023
c8288dc
info -> error log
sampocs Dec 29, 2023
299f585
autopilot liquid stake and forward unit tests (#1041)
sampocs Jan 4, 2024
1bfa7ca
renamed key function
sampocs Jan 9, 2024
cd7ce0b
replaced CheckAckStatus with helper from icacallbacks
sampocs Jan 9, 2024
8958d80
removed retry and replaced with send to fallback address
sampocs Jan 9, 2024
55635d0
fixed unit tests
sampocs Jan 9, 2024
2bf291f
updated timeout to 3 hours
sampocs Jan 9, 2024
bd61e0a
Merge branch 'main' into sam/autopilot-hash-sender-2
sampocs Jan 9, 2024
a814f92
updated timeout comments
sampocs Jan 9, 2024
5042669
updated checkmes
sampocs Jan 9, 2024
158c77d
Merge branch 'sam/autopilot-hash-sender-2' into sam/autopilot-fallbac…
sampocs Jan 9, 2024
158ccec
Merge branch 'main' into sam/autopilot-fallback-address
sampocs Jan 10, 2024
ba9330c
fixed unit test after merge
sampocs Jan 11, 2024
2bec06a
Merge branch 'main' into sam/autopilot-fallback-address
sampocs Jan 11, 2024
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
1 change: 1 addition & 0 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -613,6 +613,7 @@ func NewStrideApp(
appCodec,
keys[autopilottypes.StoreKey],
app.GetSubspace(autopilottypes.ModuleName),
app.BankKeeper,
app.StakeibcKeeper,
app.ClaimKeeper,
app.TransferKeeper,
Expand Down
3 changes: 1 addition & 2 deletions app/apptesting/test_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import (
upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types"
"github.com/cosmos/gogoproto/proto"
icatypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types"
ibctransfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types"
transfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types"
clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types"
connectiontypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types"
Expand Down Expand Up @@ -461,7 +460,7 @@ func (s *AppTestHelper) CreateAndStoreIBCDenom(baseDenom string) (ibcDenom strin
}

func (s *AppTestHelper) MarshalledICS20PacketData() sdk.AccAddress {
data := ibctransfertypes.FungibleTokenPacketData{}
data := transfertypes.FungibleTokenPacketData{}
return data.GetBytes()
}

Expand Down
13 changes: 6 additions & 7 deletions x/autopilot/keeper/airdrop.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,15 @@ import (
channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types"

"github.com/Stride-Labs/stride/v16/utils"
"github.com/Stride-Labs/stride/v16/x/autopilot/types"
claimtypes "github.com/Stride-Labs/stride/v16/x/claim/types"
stakeibctypes "github.com/Stride-Labs/stride/v16/x/stakeibc/types"
)

// Attempt to link a host address with a stride address to enable airdrop claims
func (k Keeper) TryUpdateAirdropClaim(
ctx sdk.Context,
packet channeltypes.Packet,
data transfertypes.FungibleTokenPacketData,
packetMetadata types.ClaimPacketMetadata,
transferMetadata transfertypes.FungibleTokenPacketData,
) error {
params := k.GetParams(ctx)
if !params.ClaimActive {
Expand All @@ -39,11 +38,11 @@ func (k Keeper) TryUpdateAirdropClaim(
}

// grab relevant addresses
senderStrideAddress := utils.ConvertAddressToStrideAddress(data.Sender)
senderStrideAddress := utils.ConvertAddressToStrideAddress(transferMetadata.Sender)
if senderStrideAddress == "" {
return errorsmod.Wrapf(sdkerrors.ErrInvalidAddress, fmt.Sprintf("invalid sender address (%s)", data.Sender))
return errorsmod.Wrapf(sdkerrors.ErrInvalidAddress, fmt.Sprintf("invalid sender address (%s)", transferMetadata.Sender))
}
newStrideAddress := packetMetadata.StrideAddress
newStrideAddress := transferMetadata.Receiver

// find the airdrop for this host chain ID
airdrop, found := k.claimKeeper.GetAirdropByChainId(ctx, hostZone.ChainId)
Expand All @@ -56,7 +55,7 @@ func (k Keeper) TryUpdateAirdropClaim(

airdropId := airdrop.AirdropIdentifier
k.Logger(ctx).Info(fmt.Sprintf("updating airdrop address %s (orig %s) to %s for airdrop %s",
senderStrideAddress, data.Sender, newStrideAddress, airdropId))
senderStrideAddress, transferMetadata.Sender, newStrideAddress, airdropId))

return k.claimKeeper.UpdateAirdropAddress(ctx, senderStrideAddress, newStrideAddress, airdropId)
}
39 changes: 39 additions & 0 deletions x/autopilot/keeper/fallback.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package keeper

import (
"github.com/cosmos/cosmos-sdk/store/prefix"
sdk "github.com/cosmos/cosmos-sdk/types"

"github.com/Stride-Labs/stride/v16/x/autopilot/types"
)

// Stores a fallback address for an outbound transfer
func (k Keeper) SetTransferFallbackAddress(ctx sdk.Context, channelId string, sequence uint64, address string) {
sampocs marked this conversation as resolved.
Show resolved Hide resolved
store := prefix.NewStore(ctx.KVStore(k.storeKey), types.TransferFallbackAddressPrefix)
riley-stride marked this conversation as resolved.
Show resolved Hide resolved
key := types.GetTransferFallbackAddressKey(channelId, sequence)
value := []byte(address)
store.Set(key, value)
}

// Removes a fallback address from the store
// This is used after the ack or timeout for a packet has been received
func (k Keeper) RemoveTransferFallbackAddress(ctx sdk.Context, channelId string, sequence uint64) {
store := prefix.NewStore(ctx.KVStore(k.storeKey), types.TransferFallbackAddressPrefix)
key := types.GetTransferFallbackAddressKey(channelId, sequence)
store.Delete(key)
}

// Returns a fallback address, given the channel ID and sequence number of the packet
// If no fallback address has been stored, return false
func (k Keeper) GetTransferFallbackAddress(ctx sdk.Context, channelId string, sequence uint64) (address string, found bool) {
store := prefix.NewStore(ctx.KVStore(k.storeKey), types.TransferFallbackAddressPrefix)

key := types.GetTransferFallbackAddressKey(channelId, sequence)
valueBz := store.Get(key)

if len(valueBz) == 0 {
return "", false
}

return string(valueBz), true
}
22 changes: 22 additions & 0 deletions x/autopilot/keeper/fallback_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package keeper_test

// Tests Get/Set/RemoveTransferFallbackAddress
func (s *KeeperTestSuite) TestTransferFallbackAddress() {
channelId := "channel-0"
sequence := uint64(100)
expectedAddress := "stride1xjp08gxef09fck6yj2lg0vrgpcjhqhp055ffhj"

// Add a new fallback address
s.App.AutopilotKeeper.SetTransferFallbackAddress(s.Ctx, channelId, sequence, expectedAddress)

// Confirm we can retrieve it
actualAddress, found := s.App.AutopilotKeeper.GetTransferFallbackAddress(s.Ctx, channelId, sequence)
s.Require().True(found, "address should have been found")
s.Require().Equal(expectedAddress, actualAddress, "fallback addres")

// Remove it and confirm we can no longer retrieve it
s.App.AutopilotKeeper.RemoveTransferFallbackAddress(s.Ctx, channelId, sequence)

_, found = s.App.AutopilotKeeper.GetTransferFallbackAddress(s.Ctx, channelId, sequence)
s.Require().False(found, "address should have been removed")
}
97 changes: 97 additions & 0 deletions x/autopilot/keeper/ibc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package keeper

import (
"fmt"

errorsmod "cosmossdk.io/errors"
sdkmath "cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types"
transfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types"
channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types"

"github.com/Stride-Labs/stride/v16/x/icacallbacks"
icacallbacktypes "github.com/Stride-Labs/stride/v16/x/icacallbacks/types"
)

// In the event of an ack error after a outbound transfer, we'll have to bank send to a fallback address
func (k Keeper) SendToFallbackAddress(ctx sdk.Context, packetData []byte, fallbackAddress string) error {
// First unmarshal the transfer metadata to get the sender/reciever, and token amount/denom
var transferMetadata transfertypes.FungibleTokenPacketData
if err := transfertypes.ModuleCdc.UnmarshalJSON(packetData, &transferMetadata); err != nil {
return err
}

// Pull out the original sender of the transfer which will also be the bank sender
sender := transferMetadata.Sender
senderAccount, err := sdk.AccAddressFromBech32(sender)
if err != nil {
return errorsmod.Wrapf(err, "invalid sender address")
}
fallbackAccount, err := sdk.AccAddressFromBech32(fallbackAddress)
if err != nil {
return errorsmod.Wrapf(err, "invalid fallback address")
}

// Build the token from the transfer metadata
amount, ok := sdkmath.NewIntFromString(transferMetadata.Amount)
if !ok {
return fmt.Errorf("unable to parse amount from transfer packet: %v", transferMetadata)
}
token := sdk.NewCoin(transferMetadata.Denom, amount)

// Finally send to the fallback account
if err := k.bankKeeper.SendCoins(ctx, senderAccount, fallbackAccount, sdk.NewCoins(token)); err != nil {
return err
}

return nil
}

// If there was a timeout or failed ack from an outbound transfer of one of the autopilot actions,
// we'll need to check if there was a fallback address. If one was stored, bank send to that address
// If the ack was successful, we should delete the address (if it exists)
func (k Keeper) HandleFallbackAddress(ctx sdk.Context, packet channeltypes.Packet, acknowledgement []byte, packetTimedOut bool) error {
// Retrieve the fallback address for the given packet
// We use the packet source channel here since this will correspond with the channel on Stride
channelId := packet.SourceChannel
sequence := packet.Sequence
fallbackAddress, fallbackAddressFound := k.GetTransferFallbackAddress(ctx, channelId, sequence)

// If there was no fallback address, there's nothing else to do
if !fallbackAddressFound {
riley-stride marked this conversation as resolved.
Show resolved Hide resolved
return nil
}

// Remove the fallback address since the packet is no longer pending
k.RemoveTransferFallbackAddress(ctx, channelId, sequence)

// If the packet timed out, send to the fallback address
if packetTimedOut {
return k.SendToFallbackAddress(ctx, packet.Data, fallbackAddress)
}

// If the packet did not timeout, check whether the ack was successful or was an ack error
isICATx := false
ackResponse, err := icacallbacks.UnpackAcknowledgementResponse(ctx, k.Logger(ctx), acknowledgement, isICATx)
if err != nil {
return err
}

// If successful, no additional action is necessary
if ackResponse.Status == icacallbacktypes.AckResponseStatus_SUCCESS {
return nil
}

// If there was an ack error, we'll need to bank send to the fallback address
return k.SendToFallbackAddress(ctx, packet.Data, fallbackAddress)
}

// OnTimeoutPacket should always send to the fallback address
func (k Keeper) OnTimeoutPacket(ctx sdk.Context, packet channeltypes.Packet) error {
sampocs marked this conversation as resolved.
Show resolved Hide resolved
return k.HandleFallbackAddress(ctx, packet, []byte{}, true)
}

// OnAcknowledgementPacket should send to the fallback address if the ack is an ack error
func (k Keeper) OnAcknowledgementPacket(ctx sdk.Context, packet channeltypes.Packet, acknowledgement []byte) error {
return k.HandleFallbackAddress(ctx, packet, acknowledgement, false)
}
Loading