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 hashed sender #1038

Merged
merged 21 commits into from
Jan 10, 2024
Merged
Show file tree
Hide file tree
Changes from 15 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
5 changes: 4 additions & 1 deletion app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -612,8 +612,11 @@ func NewStrideApp(
appCodec,
keys[autopilottypes.StoreKey],
app.GetSubspace(autopilottypes.ModuleName),
app.BankKeeper,
app.StakeibcKeeper,
app.ClaimKeeper)
app.ClaimKeeper,
app.TransferKeeper,
)
autopilotModule := autopilot.NewAppModule(appCodec, app.AutopilotKeeper)

app.VestingKeeper = evmosvestingkeeper.NewKeeper(
Expand Down
7 changes: 7 additions & 0 deletions dockernet/config.sh
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,13 @@ STWALK_DENOM="stuwalk"
STEVMOS_DENOM="staevmos"
STDYDX_DENOM="studydx"

IBC_GAIA_CHANNEL_0_STATOM_DENOM='ibc/054A44EC8D9B68B9A6F0D5708375E00A5569A28F21E0064FF12CADC3FEF1D04F'
IBC_GAIA_CHANNEL_1_STATOM_DENOM='ibc/8B21DA0E34A49AE151FEEBCCF3AFE1188E24BA8E19439FB93434DF6008E7E228'
IBC_GAIA_CHANNEL_2_STATOM_DENOM='ibc/60CB7A5465C318C8F68F603D78721A2ECC1DA2D0E905C6AD9ACD1CAC3F0DB22D'
IBC_GAIA_CHANNEL_3_STATOM_DENOM='ibc/0C0FD07C29EB075C18EA77B73CF9FCE68A268E0738C9F5B11D13E418AD889437'

IBC_GAIA_STATOM_DENOM=$IBC_GAIA_CHANNEL_0_STATOM_DENOM

IBC_STRD_DENOM='ibc/FF6C2E86490C1C4FBBD24F55032831D2415B9D7882F85C3CC9C2401D79362BEA'

IBC_GAIA_CHANNEL_0_DENOM='ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2'
Expand Down
28 changes: 28 additions & 0 deletions dockernet/tests/integration_tests.bats
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,34 @@ setup_file() {
assert_equal "$diff" $STAKE_AMOUNT
}

@test "[INTEGRATION-BASIC-$CHAIN_NAME] packet forwarding automatically liquid stake and ibc transfer stAsset to original network" {
memo='{ "autopilot": { "receiver": "'"$(STRIDE_ADDRESS)"'", "stakeibc": { "action": "LiquidStake", "ibc_receiver": "'$HOST_VAL_ADDRESS'" } } }'

# get initial balances
stibctoken_balance_start=$($HOST_MAIN_CMD q bank balances $HOST_VAL_ADDRESS --denom $IBC_GAIA_STATOM_DENOM | GETBAL)

# Send the IBC transfer with the JSON memo
transfer_msg_prefix="$HOST_MAIN_CMD tx ibc-transfer transfer transfer $HOST_TRANSFER_CHANNEL"
if [[ "$CHAIN_NAME" == "GAIA" ]]; then
# For GAIA (ibc-v3), pass the memo into the receiver field
$transfer_msg_prefix "$memo" ${PACKET_FORWARD_STAKE_AMOUNT}${HOST_DENOM} --from $HOST_VAL -y
elif [[ "$CHAIN_NAME" == "HOST" ]]; then
# For HOST (ibc-v5), pass an address for a receiver and the memo in the --memo field
$transfer_msg_prefix $(STRIDE_ADDRESS) ${PACKET_FORWARD_STAKE_AMOUNT}${HOST_DENOM} --memo "$memo" --from $HOST_VAL -y
else
# For all other hosts, skip this test
skip "Packet forward liquid stake test is only run on GAIA and HOST"
fi

# Wait for the transfer to complete
WAIT_FOR_BALANCE_CHANGE $CHAIN_NAME $HOST_VAL_ADDRESS $IBC_GAIA_STATOM_DENOM

# make sure stATOM balance increased
stibctoken_balance_end=$($HOST_MAIN_CMD q bank balances $HOST_VAL_ADDRESS --denom $IBC_GAIA_STATOM_DENOM | GETBAL)
stibctoken_balance_diff=$(($stibctoken_balance_end-$stibctoken_balance_start))
assert_equal "$stibctoken_balance_diff" "$PACKET_FORWARD_STAKE_AMOUNT"
}

# check that tokens on the host are staked
@test "[INTEGRATION-BASIC-$CHAIN_NAME] tokens on $CHAIN_NAME were staked" {
# wait for another epoch to pass so that tokens are staked
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
riley-stride marked this conversation as resolved.
Show resolved Hide resolved

// 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)
}
2 changes: 1 addition & 1 deletion x/autopilot/keeper/airdrop_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ func (s *KeeperTestSuite) TestAirdropOnRecvPacket() {
destinationPortID: transfertypes.PortID,
packetData: transfertypes.FungibleTokenPacketData{
Receiver: strideAddress,
Memo: strings.Repeat("X", 300),
Memo: strings.Repeat("X", 513),
},
transferShouldSucceed: false,
airdropShouldUpdate: false,
Expand Down
7 changes: 7 additions & 0 deletions x/autopilot/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
paramtypes "github.com/cosmos/cosmos-sdk/x/params/types"
ibctransferkeeper "github.com/cosmos/ibc-go/v7/modules/apps/transfer/keeper"

"github.com/Stride-Labs/stride/v16/x/autopilot/types"
claimkeeper "github.com/Stride-Labs/stride/v16/x/claim/keeper"
Expand All @@ -20,17 +21,21 @@ type (
Cdc codec.BinaryCodec
storeKey storetypes.StoreKey
paramstore paramtypes.Subspace
bankKeeper types.BankKeeper
stakeibcKeeper stakeibckeeper.Keeper
claimKeeper claimkeeper.Keeper
transferKeeper ibctransferkeeper.Keeper
}
)

func NewKeeper(
Cdc codec.BinaryCodec,
storeKey storetypes.StoreKey,
ps paramtypes.Subspace,
bankKeeper types.BankKeeper,
stakeibcKeeper stakeibckeeper.Keeper,
claimKeeper claimkeeper.Keeper,
transferKeeper ibctransferkeeper.Keeper,
) *Keeper {
// set KeyTable if it has not already been set
if !ps.HasKeyTable() {
Expand All @@ -41,8 +46,10 @@ func NewKeeper(
Cdc: Cdc,
storeKey: storeKey,
paramstore: ps,
bankKeeper: bankKeeper,
stakeibcKeeper: stakeibcKeeper,
claimKeeper: claimKeeper,
transferKeeper: transferKeeper,
}
}

Expand Down
128 changes: 100 additions & 28 deletions x/autopilot/keeper/liquidstake.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@ import (
"fmt"

errorsmod "cosmossdk.io/errors"

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

Expand All @@ -16,68 +15,141 @@ import (
stakeibctypes "github.com/Stride-Labs/stride/v16/x/stakeibc/types"
)

// Attempts to do an autopilot liquid stake (and optional forward)
// The liquid stake is only allowed if the inbound packet came along a trusted channel
func (k Keeper) TryLiquidStaking(
ctx sdk.Context,
packet channeltypes.Packet,
newData transfertypes.FungibleTokenPacketData,
packetMetadata types.StakeibcPacketMetadata,
transferMetadata transfertypes.FungibleTokenPacketData,
autopilotMetadata types.StakeibcPacketMetadata,
) error {
params := k.GetParams(ctx)
if !params.StakeibcActive {
return errorsmod.Wrapf(types.ErrPacketForwardingInactive, "autopilot stakeibc routing is inactive")
}

// In this case, we can't process a liquid staking transaction, because we're dealing with STRD tokens
if transfertypes.ReceiverChainIsSource(packet.GetSourcePort(), packet.GetSourceChannel(), newData.Denom) {
return errors.New("the native token is not supported for liquid staking")
}

amount, ok := sdk.NewIntFromString(newData.Amount)
// Verify the amount is valid
amount, ok := sdk.NewIntFromString(transferMetadata.Amount)
if !ok {
return errors.New("not a parsable amount field")
}

// Note: newData.denom is base denom e.g. uatom - not ibc/xxx
var token = sdk.NewCoin(newData.Denom, amount)
// In this case, we can't process a liquid staking transaction, because we're dealing with native tokens (e.g. STRD, stATOM)
if transfertypes.ReceiverChainIsSource(packet.GetSourcePort(), packet.GetSourceChannel(), transferMetadata.Denom) {
return errors.New("the native token is not supported for liquid staking")
riley-stride marked this conversation as resolved.
Show resolved Hide resolved
}

prefixedDenom := transfertypes.GetDenomPrefix(packet.GetDestPort(), packet.GetDestChannel()) + newData.Denom
// Note: the denom in the packet is the base denom e.g. uatom - not ibc/xxx
// We need to use the port and channel to build the IBC denom
prefixedDenom := transfertypes.GetPrefixedDenom(packet.GetDestPort(), packet.GetDestChannel(), transferMetadata.Denom)
ibcDenom := transfertypes.ParseDenomTrace(prefixedDenom).IBCDenom()

hostZone, err := k.stakeibcKeeper.GetHostZoneFromHostDenom(ctx, token.Denom)
hostZone, err := k.stakeibcKeeper.GetHostZoneFromHostDenom(ctx, transferMetadata.Denom)
if err != nil {
return fmt.Errorf("host zone not found for denom (%s)", token.Denom)
return fmt.Errorf("host zone not found for denom (%s)", transferMetadata.Denom)
}

// Verify the IBC denom of the packet matches the host zone, to confirm the packet
// was sent over a trusted channel
if hostZone.IbcDenom != ibcDenom {
return fmt.Errorf("ibc denom %s is not equal to host zone ibc denom %s", ibcDenom, hostZone.IbcDenom)
}

strideAddress, err := sdk.AccAddressFromBech32(packetMetadata.StrideAddress)
if err != nil {
return errorsmod.Wrapf(sdkerrors.ErrInvalidAddress, "invalid stride_address (%s) in autopilot memo", strideAddress)
}

return k.RunLiquidStake(ctx, strideAddress, token)
return k.RunLiquidStake(ctx, amount, transferMetadata, autopilotMetadata)
}

func (k Keeper) RunLiquidStake(ctx sdk.Context, addr sdk.AccAddress, token sdk.Coin) error {
// Submits a LiquidStake message from the transfer receiver
// If a forwarding recipient is specified, the stTokens are ibc transferred
func (k Keeper) RunLiquidStake(
ctx sdk.Context,
amount sdkmath.Int,
transferMetadata transfertypes.FungibleTokenPacketData,
autopilotMetadata types.StakeibcPacketMetadata,
) error {
msg := &stakeibctypes.MsgLiquidStake{
Creator: addr.String(),
Amount: token.Amount,
HostDenom: token.Denom,
Creator: transferMetadata.Receiver,
asalzmann marked this conversation as resolved.
Show resolved Hide resolved
Amount: amount,
HostDenom: transferMetadata.Denom,
}

if err := msg.ValidateBasic(); err != nil {
return err
}

msgServer := stakeibckeeper.NewMsgServerImpl(k.stakeibcKeeper)
_, err := msgServer.LiquidStake(
msgResponse, err := msgServer.LiquidStake(
sdk.WrapSDKContext(ctx),
msg,
)
if err != nil {
return errorsmod.Wrapf(sdkerrors.ErrInsufficientFunds, err.Error())
return errorsmod.Wrapf(err, err.Error())
}

// If the IBCReceiver is empty, there is no forwarding step
if autopilotMetadata.IbcReceiver == "" {
return nil
shellvish marked this conversation as resolved.
Show resolved Hide resolved
}
return nil

// Otherwise, if there is forwarding info, submit the IBC transfer
return k.IBCTransferStToken(ctx, msgResponse.StToken, transferMetadata, autopilotMetadata)
}

// Submits an IBC transfer of the stToken to a non-stride zone (either back to the host zone or to a different zone)
// The sender of the transfer is the hashed receiver of the original autopilot inbound transfer
func (k Keeper) IBCTransferStToken(
ctx sdk.Context,
stToken sdk.Coin,
transferMetadata transfertypes.FungibleTokenPacketData,
autopilotMetadata types.StakeibcPacketMetadata,
) error {
hostZone, err := k.stakeibcKeeper.GetHostZoneFromHostDenom(ctx, transferMetadata.Denom)
if err != nil {
return err
}

// If there's no channelID specified in the packet, default to the channel on the host zone
channelId := autopilotMetadata.TransferChannel
if channelId == "" {
channelId = hostZone.TransferChannelId
}

// Generate a hashed address for the sender to the next hop,
// to prevent impersonation at downstream zones
// Note: The channel ID here is different than the one used in PFM
// (we use the outbound channelID, they use the inbound channelID)
// DOUBLE CHECK ME that it shouldn't matter
sampocs marked this conversation as resolved.
Show resolved Hide resolved
hashedAddress, err := types.GenerateHashedAddress(channelId, transferMetadata.Sender)
if err != nil {
return err
}

// First we need to bank send to the hashed address
originalReceiver, err := sdk.AccAddressFromBech32(transferMetadata.Receiver)
if err != nil {
return err
}
hashedSender, err := sdk.AccAddressFromBech32(hashedAddress)
if err != nil {
return err
}
if err := k.bankKeeper.SendCoins(ctx, originalReceiver, hashedSender, sdk.NewCoins(stToken)); err != nil {
return err
}

// Use the default transfer timeout of 10 minutes
timeoutTimestamp := uint64(ctx.BlockTime().UnixNano()) + transfertypes.DefaultRelativePacketTimeoutTimestamp
asalzmann marked this conversation as resolved.
Show resolved Hide resolved

// Submit the transfer from the hashed address
transferMsg := &transfertypes.MsgTransfer{
SourcePort: transfertypes.PortID,
SourceChannel: channelId,
Token: stToken,
Sender: hashedAddress,
Receiver: autopilotMetadata.IbcReceiver,
shellvish marked this conversation as resolved.
Show resolved Hide resolved
TimeoutTimestamp: timeoutTimestamp,
Memo: "autopilot-liquid-stake-and-forward",
}
_, err = k.transferKeeper.Transfer(sdk.WrapSDKContext(ctx), transferMsg)

return err
}
Loading