Skip to content

Commit

Permalink
fix npe when memo is not empty (cosmos#77)
Browse files Browse the repository at this point in the history
* fix npe when memo is not empty

* test: add e2e tests for pfm

---------

Co-authored-by: Justin Tieri <37750742+jtieri@users.noreply.github.com>
Co-authored-by: jtieri <justin@thetieris.com>
  • Loading branch information
3 people committed Aug 3, 2023
1 parent d72e83c commit 7c8f814
Show file tree
Hide file tree
Showing 5 changed files with 2,914 additions and 0 deletions.
252 changes: 252 additions & 0 deletions middleware/packet-forward-middleware/e2e/forward_timeout_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,252 @@
package e2e

import (
"context"
"encoding/json"
"testing"
"time"

"cosmossdk.io/math"
transfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types"
chantypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types"
"github.com/strangelove-ventures/interchaintest/v7"
"github.com/strangelove-ventures/interchaintest/v7/chain/cosmos"
"github.com/strangelove-ventures/interchaintest/v7/ibc"
"github.com/strangelove-ventures/interchaintest/v7/testreporter"
"github.com/strangelove-ventures/interchaintest/v7/testutil"
"github.com/stretchr/testify/require"
"go.uber.org/zap/zaptest"
)

func TestTimeoutOnForward(t *testing.T) {
if testing.Short() {
t.Skip("skipping in short mode")
}

client, network := interchaintest.DockerSetup(t)

rep := testreporter.NewNopReporter()
eRep := rep.RelayerExecReporter(t)

ctx := context.Background()

chainIdA, chainIdB, chainIdC, chainIdD := "chain-a", "chain-b", "chain-c", "chain-d"

fullNodes := 1
vals := 1

cf := interchaintest.NewBuiltinChainFactory(zaptest.NewLogger(t), []*interchaintest.ChainSpec{
{Name: "gaia", Version: "v9.0.1", ChainConfig: ibc.ChainConfig{ChainID: chainIdA, GasPrices: "0.0uatom"}, NumFullNodes: &fullNodes, NumValidators: &vals},
{Name: "gaia", Version: "v9.0.1", ChainConfig: ibc.ChainConfig{ChainID: chainIdB, GasPrices: "0.0uatom"}, NumFullNodes: &fullNodes, NumValidators: &vals},
{Name: "gaia", Version: "v9.0.1", ChainConfig: ibc.ChainConfig{ChainID: chainIdC, GasPrices: "0.0uatom"}, NumFullNodes: &fullNodes, NumValidators: &vals},
{Name: "gaia", Version: "v9.0.1", ChainConfig: ibc.ChainConfig{ChainID: chainIdD, GasPrices: "0.0uatom"}, NumFullNodes: &fullNodes, NumValidators: &vals},
})

chains, err := cf.Chains(t.Name())
require.NoError(t, err)

chainA, chainB, chainC, chainD := chains[0].(*cosmos.CosmosChain), chains[1].(*cosmos.CosmosChain), chains[2].(*cosmos.CosmosChain), chains[3].(*cosmos.CosmosChain)

r := interchaintest.NewBuiltinRelayerFactory(
ibc.CosmosRly,
zaptest.NewLogger(t),
).Build(t, client, network)

const pathAB = "ab"
const pathBC = "bc"
const pathCD = "cd"

ic := interchaintest.NewInterchain().
AddChain(chainA).
AddChain(chainB).
AddChain(chainC).
AddChain(chainD).
AddRelayer(r, "relayer").
AddLink(interchaintest.InterchainLink{
Chain1: chainA,
Chain2: chainB,
Relayer: r,
Path: pathAB,
}).
AddLink(interchaintest.InterchainLink{
Chain1: chainB,
Chain2: chainC,
Relayer: r,
Path: pathBC,
}).
AddLink(interchaintest.InterchainLink{
Chain1: chainC,
Chain2: chainD,
Relayer: r,
Path: pathCD,
})

require.NoError(t, ic.Build(ctx, eRep, interchaintest.InterchainBuildOptions{
TestName: t.Name(),
Client: client,
NetworkID: network,
SkipPathCreation: false,
}))

t.Cleanup(func() {
_ = ic.Close()
})

// Start the relayer on only the path between chainA<>chainB so that the initial transfer succeeds
err = r.StartRelayer(ctx, eRep, pathAB)
require.NoError(t, err)

t.Cleanup(
func() {
err := r.StopRelayer(ctx, eRep)
if err != nil {
t.Logf("an error occured while stopping the relayer: %s", err)
}
},
)

// Fund user accounts with initial balances and get the transfer channel information between each set of chains
initBal := math.NewInt(10_000_000_000)
users := interchaintest.GetAndFundTestUsers(t, ctx, t.Name(), initBal.Int64(), chainA, chainB, chainC, chainD)

abChan, err := ibc.GetTransferChannel(ctx, r, eRep, chainIdA, chainIdB)
require.NoError(t, err)

baChan := abChan.Counterparty

cbChan, err := ibc.GetTransferChannel(ctx, r, eRep, chainIdC, chainIdB)
require.NoError(t, err)

bcChan := cbChan.Counterparty

dcChan, err := ibc.GetTransferChannel(ctx, r, eRep, chainIdD, chainIdC)
require.NoError(t, err)

cdChan := dcChan.Counterparty

userA, userB, userC, userD := users[0], users[1], users[2], users[3]

// Compose the prefixed denoms and ibc denom for asserting balances
firstHopDenom := transfertypes.GetPrefixedDenom(baChan.PortID, baChan.ChannelID, chainA.Config().Denom)
secondHopDenom := transfertypes.GetPrefixedDenom(cbChan.PortID, cbChan.ChannelID, firstHopDenom)
thirdHopDenom := transfertypes.GetPrefixedDenom(dcChan.PortID, dcChan.ChannelID, secondHopDenom)

firstHopDenomTrace := transfertypes.ParseDenomTrace(firstHopDenom)
secondHopDenomTrace := transfertypes.ParseDenomTrace(secondHopDenom)
thirdHopDenomTrace := transfertypes.ParseDenomTrace(thirdHopDenom)

firstHopIBCDenom := firstHopDenomTrace.IBCDenom()
secondHopIBCDenom := secondHopDenomTrace.IBCDenom()
thirdHopIBCDenom := thirdHopDenomTrace.IBCDenom()

firstHopEscrowAccount := transfertypes.GetEscrowAddress(abChan.PortID, abChan.ChannelID).String()
secondHopEscrowAccount := transfertypes.GetEscrowAddress(bcChan.PortID, bcChan.ChannelID).String()
thirdHopEscrowAccount := transfertypes.GetEscrowAddress(cdChan.PortID, abChan.ChannelID).String()

zeroBal := math.ZeroInt()
transferAmount := math.NewInt(100_000)

// Attempt to send packet from Chain A->Chain B->Chain C->Chain D
transfer := ibc.WalletAmount{
Address: userB.FormattedAddress(),
Denom: chainA.Config().Denom,
Amount: transferAmount,
}

retries := uint8(0)
secondHopMetadata := &PacketMetadata{
Forward: &ForwardMetadata{
Receiver: userD.FormattedAddress(),
Channel: cdChan.ChannelID,
Port: cdChan.PortID,
Retries: &retries,
},
}
nextBz, err := json.Marshal(secondHopMetadata)
require.NoError(t, err)
next := string(nextBz)

firstHopMetadata := &PacketMetadata{
Forward: &ForwardMetadata{
Receiver: userC.FormattedAddress(),
Channel: bcChan.ChannelID,
Port: bcChan.PortID,
Next: &next,
Retries: &retries,
Timeout: time.Second * 10, // Set low timeout for forward from chainB<>chainC
},
}

memo, err := json.Marshal(firstHopMetadata)
require.NoError(t, err)

opts := ibc.TransferOptions{
Memo: string(memo),
}

chainBHeight, err := chainB.Height(ctx)
require.NoError(t, err)

transferTx, err := chainA.SendIBCTransfer(ctx, abChan.ChannelID, userA.KeyName(), transfer, opts)
require.NoError(t, err)

// Poll for MsgRecvPacket on chainB
_, err = cosmos.PollForMessage[*chantypes.MsgRecvPacket](ctx, chainB, cosmos.DefaultEncoding().InterfaceRegistry, chainBHeight, chainBHeight+20, nil)
require.NoError(t, err)

// Stop the relayer and wait for the timeout to happen on chainC
err = r.StopRelayer(ctx, eRep)
require.NoError(t, err)

time.Sleep(time.Second * 11)

// Restart the relayer
err = r.StartRelayer(ctx, eRep, pathAB, pathBC)
require.NoError(t, err)

chainAHeight, err := chainA.Height(ctx)
require.NoError(t, err)

chainBHeight, err = chainB.Height(ctx)

// Poll for the MsgTimeout on chainB and the MsgAck on chainA
_, err = cosmos.PollForMessage[*chantypes.MsgTimeout](ctx, chainB, cosmos.DefaultEncoding().InterfaceRegistry, chainBHeight, chainBHeight+20, nil)
require.NoError(t, err)

_, err = testutil.PollForAck(ctx, chainA, chainAHeight, chainAHeight+30, transferTx.Packet)
require.NoError(t, err)

err = testutil.WaitForBlocks(ctx, 1, chainA)
require.NoError(t, err)

// Assert balances to ensure that the funds are still on the original sending chain
chainABalance, err := chainA.GetBalance(ctx, userA.FormattedAddress(), chainA.Config().Denom)
require.NoError(t, err)

chainBBalance, err := chainB.GetBalance(ctx, userB.FormattedAddress(), firstHopIBCDenom)
require.NoError(t, err)

chainCBalance, err := chainC.GetBalance(ctx, userC.FormattedAddress(), secondHopIBCDenom)
require.NoError(t, err)

chainDBalance, err := chainD.GetBalance(ctx, userD.FormattedAddress(), thirdHopIBCDenom)
require.NoError(t, err)

require.True(t, chainABalance.Equal(initBal))
require.True(t, chainBBalance.Equal(zeroBal))
require.True(t, chainCBalance.Equal(zeroBal))
require.True(t, chainDBalance.Equal(zeroBal))

firstHopEscrowBalance, err := chainA.GetBalance(ctx, firstHopEscrowAccount, chainA.Config().Denom)
require.NoError(t, err)

secondHopEscrowBalance, err := chainB.GetBalance(ctx, secondHopEscrowAccount, firstHopIBCDenom)
require.NoError(t, err)

thirdHopEscrowBalance, err := chainC.GetBalance(ctx, thirdHopEscrowAccount, secondHopIBCDenom)
require.NoError(t, err)

require.True(t, firstHopEscrowBalance.Equal(zeroBal))
require.True(t, secondHopEscrowBalance.Equal(zeroBal))
require.True(t, thirdHopEscrowBalance.Equal(zeroBal))
}

0 comments on commit 7c8f814

Please sign in to comment.