From 92c4760406b367297f525e0ac92d2833d7725572 Mon Sep 17 00:00:00 2001 From: Puneet <59960662+puneet2019@users.noreply.github.com> Date: Wed, 13 Mar 2024 18:55:43 +0400 Subject: [PATCH] update pfm version for v11.x (#307) update pfm version --- app/keepers/keepers.go | 6 +- app/keepers/keys.go | 2 +- app/keepers/modules.go | 4 +- app/modules.go | 4 +- app/upgrades/v8/constants.go | 2 +- app/upgrades/v8/legacy_subspaces.go | 2 +- go.mod | 9 +- go.sum | 10 +- interchaintest/lsm_halt_fork_test.go | 4 +- interchaintest/module_pfm_test.go | 435 +++++++++++++++++++++++++++ 10 files changed, 457 insertions(+), 21 deletions(-) diff --git a/app/keepers/keepers.go b/app/keepers/keepers.go index 8e6c50ce..e73897ab 100644 --- a/app/keepers/keepers.go +++ b/app/keepers/keepers.go @@ -46,9 +46,9 @@ import ( "github.com/cosmos/cosmos-sdk/x/upgrade" upgradekeeper "github.com/cosmos/cosmos-sdk/x/upgrade/keeper" upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" - packetforward "github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v7/router" - packetforwardkeeper "github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v7/router/keeper" - packetforwardtypes "github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v7/router/types" + packetforward "github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v7/packetforward" + packetforwardkeeper "github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v7/packetforward/keeper" + packetforwardtypes "github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v7/packetforward/types" ibchooks "github.com/cosmos/ibc-apps/modules/ibc-hooks/v7" ibchookskeeper "github.com/cosmos/ibc-apps/modules/ibc-hooks/v7/keeper" ibchookstypes "github.com/cosmos/ibc-apps/modules/ibc-hooks/v7/types" diff --git a/app/keepers/keys.go b/app/keepers/keys.go index c9b90c75..c5c350ff 100644 --- a/app/keepers/keys.go +++ b/app/keepers/keys.go @@ -20,7 +20,7 @@ import ( slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" - packetforwardtypes "github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v7/router/types" + packetforwardtypes "github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v7/packetforward/types" ibchookstypes "github.com/cosmos/ibc-apps/modules/ibc-hooks/v7/types" icacontrollertypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/controller/types" icahosttypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/host/types" diff --git a/app/keepers/modules.go b/app/keepers/modules.go index 74746f73..4675b8af 100644 --- a/app/keepers/modules.go +++ b/app/keepers/modules.go @@ -25,7 +25,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/staking" "github.com/cosmos/cosmos-sdk/x/upgrade" upgradeclient "github.com/cosmos/cosmos-sdk/x/upgrade/client" - "github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v7/router" + "github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v7/packetforward" ibchooks "github.com/cosmos/ibc-apps/modules/ibc-hooks/v7" ica "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts" ibcfee "github.com/cosmos/ibc-go/v7/modules/apps/29-fee" @@ -91,6 +91,6 @@ var AppModuleBasics = []module.AppModuleBasic{ consensus.AppModuleBasic{}, groupmodule.AppModuleBasic{}, ibchooks.AppModuleBasic{}, - router.AppModuleBasic{}, + packetforward.AppModuleBasic{}, buildermodule.AppModuleBasic{}, } diff --git a/app/modules.go b/app/modules.go index 4a31df15..c3b682f1 100644 --- a/app/modules.go +++ b/app/modules.go @@ -39,8 +39,8 @@ import ( stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "github.com/cosmos/cosmos-sdk/x/upgrade" upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" - packetforward "github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v7/router" - packetforwardtypes "github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v7/router/types" + packetforward "github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v7/packetforward" + packetforwardtypes "github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v7/packetforward/types" ibchooks "github.com/cosmos/ibc-apps/modules/ibc-hooks/v7" ibchookstypes "github.com/cosmos/ibc-apps/modules/ibc-hooks/v7/types" ica "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts" diff --git a/app/upgrades/v8/constants.go b/app/upgrades/v8/constants.go index d548dd22..338c3e93 100644 --- a/app/upgrades/v8/constants.go +++ b/app/upgrades/v8/constants.go @@ -5,7 +5,7 @@ import ( consensusparamstypes "github.com/cosmos/cosmos-sdk/x/consensus/types" crisistypes "github.com/cosmos/cosmos-sdk/x/crisis/types" group "github.com/cosmos/cosmos-sdk/x/group" - packetforwardtypes "github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v7/router/types" + packetforwardtypes "github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v7/packetforward/types" ibchookstypes "github.com/cosmos/ibc-apps/modules/ibc-hooks/v7/types" ibcfeetypes "github.com/cosmos/ibc-go/v7/modules/apps/29-fee/types" oracletypes "github.com/persistenceOne/persistence-sdk/v2/x/oracle/types" diff --git a/app/upgrades/v8/legacy_subspaces.go b/app/upgrades/v8/legacy_subspaces.go index c4daf0b1..313206ee 100644 --- a/app/upgrades/v8/legacy_subspaces.go +++ b/app/upgrades/v8/legacy_subspaces.go @@ -16,7 +16,7 @@ import ( paramstypes "github.com/cosmos/cosmos-sdk/x/params/types" slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - packetforwardtypes "github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v7/router/types" + packetforwardtypes "github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v7/packetforward/types" icacontrollertypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/controller/types" icahosttypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/host/types" ibctransfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" diff --git a/go.mod b/go.mod index 1e572ecd..fb9dfd6f 100644 --- a/go.mod +++ b/go.mod @@ -11,10 +11,10 @@ require ( github.com/CosmWasm/wasmvm v1.2.4 github.com/cometbft/cometbft v0.37.2 github.com/cometbft/cometbft-db v0.9.1 - github.com/cosmos/cosmos-sdk v0.47.3 - github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v7 v7.0.0-20230523193151-73dea436e53f + github.com/cosmos/cosmos-sdk v0.47.5 + github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v7 v7.0.2-0.20240307225840-3d1ded976e33 github.com/cosmos/ibc-apps/modules/ibc-hooks/v7 v7.0.0-20230706221116-a1e480497c12 - github.com/cosmos/ibc-go/v7 v7.1.0 + github.com/cosmos/ibc-go/v7 v7.3.1 github.com/gorilla/mux v1.8.1 github.com/persistenceOne/persistence-sdk/v2 v2.1.1 github.com/persistenceOne/pstake-native/v2 v2.11.0 @@ -36,7 +36,7 @@ require ( cloud.google.com/go/storage v1.30.1 // indirect cosmossdk.io/core v0.6.1 // indirect cosmossdk.io/depinject v1.0.0-alpha.4 // indirect - cosmossdk.io/log v1.2.0 // indirect + cosmossdk.io/log v1.2.1 // indirect filippo.io/edwards25519 v1.0.0 // indirect github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect github.com/99designs/keyring v1.2.2 // indirect @@ -203,7 +203,6 @@ replace ( replace ( github.com/CosmWasm/wasmd => github.com/persistenceOne/wasmd v0.40.2-lsm3 github.com/cosmos/cosmos-sdk => github.com/persistenceOne/cosmos-sdk v0.47.3-lsm5 - github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v7 => github.com/persistenceOne/ibc-apps/middleware/packet-forward-middleware/v7 v7.0.0-20240109170424-f4a8f29910eb github.com/cosmos/ibc-apps/modules/ibc-hooks/v7 => github.com/persistenceOne/ibc-apps/modules/ibc-hooks/v7 v7.0.0-20240109170424-f4a8f29910eb github.com/cosmos/ibc-go/v7 => github.com/persistenceOne/ibc-go/v7 v7.2.0-lsm3 github.com/skip-mev/pob => github.com/persistenceOne/pob v1.0.3-lsm3 diff --git a/go.sum b/go.sum index 3747119e..0c541eae 100644 --- a/go.sum +++ b/go.sum @@ -199,8 +199,8 @@ cosmossdk.io/depinject v1.0.0-alpha.4 h1:PLNp8ZYAMPTUKyG9IK2hsbciDWqna2z1Wsl98ok cosmossdk.io/depinject v1.0.0-alpha.4/go.mod h1:HeDk7IkR5ckZ3lMGs/o91AVUc7E596vMaOmslGFM3yU= cosmossdk.io/errors v1.0.1 h1:bzu+Kcr0kS/1DuPBtUFdWjzLqyUuCiyHjyJB6srBV/0= cosmossdk.io/errors v1.0.1/go.mod h1:MeelVSZThMi4bEakzhhhE/CKqVv3nOJDA25bIqRDu/U= -cosmossdk.io/log v1.2.0 h1:BbykkDsutXPSy8RojFB3KZEWyvMsToLy0ykb/ZhsLqQ= -cosmossdk.io/log v1.2.0/go.mod h1:GNSCc/6+DhFIj1aLn/j7Id7PaO8DzNylUZoOYBL9+I4= +cosmossdk.io/log v1.2.1 h1:Xc1GgTCicniwmMiKwDxUjO4eLhPxoVdI9vtMW8Ti/uk= +cosmossdk.io/log v1.2.1/go.mod h1:GNSCc/6+DhFIj1aLn/j7Id7PaO8DzNylUZoOYBL9+I4= cosmossdk.io/math v1.2.0 h1:8gudhTkkD3NxOP2YyyJIYYmt6dQ55ZfJkDOaxXpy7Ig= cosmossdk.io/math v1.2.0/go.mod h1:l2Gnda87F0su8a/7FEKJfFdJrM0JZRXQaohlgJeyQh0= cosmossdk.io/tools/rosetta v0.2.1 h1:ddOMatOH+pbxWbrGJKRAawdBkPYLfKXutK9IETnjYxw= @@ -407,6 +407,8 @@ github.com/cosmos/gogoproto v1.4.10 h1:QH/yT8X+c0F4ZDacDv3z+xE3WU1P1Z3wQoLMBRJoK github.com/cosmos/gogoproto v1.4.10/go.mod h1:3aAZzeRWpAwr+SS/LLkICX2/kDFyaYVzckBDzygIxek= github.com/cosmos/iavl v0.20.0 h1:fTVznVlepH0KK8NyKq8w+U7c2L6jofa27aFX6YGlm38= github.com/cosmos/iavl v0.20.0/go.mod h1:WO7FyvaZJoH65+HFOsDir7xU9FWk2w9cHXNW1XHcl7A= +github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v7 v7.0.2-0.20240307225840-3d1ded976e33 h1:5/dbALdSYkREvsDaFpHZqKD6YBLhotSaVUTNqSp/1l0= +github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v7 v7.0.2-0.20240307225840-3d1ded976e33/go.mod h1:GGUJN4LnB3J1Luu4kxTklQLbW69So3QXUndSalB003s= github.com/cosmos/ics23/go v0.10.0 h1:iXqLLgp2Lp+EdpIuwXTYIQU+AiHj9mOC2X9ab++bZDM= github.com/cosmos/ics23/go v0.10.0/go.mod h1:ZfJSmng/TBNTBkFemHHHj5YY7VAU/MBU980F4VU1NG0= github.com/cosmos/keyring v1.2.0 h1:8C1lBP9xhImmIabyXW4c3vFjjLiBdGCmfLUfeZlV1Yo= @@ -1013,8 +1015,6 @@ github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNc github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= github.com/persistenceOne/cosmos-sdk v0.47.3-lsm5 h1:z5k9M81ogHgaMyLck7GVzfdNYvn03ZTXsCpQDzy69eQ= github.com/persistenceOne/cosmos-sdk v0.47.3-lsm5/go.mod h1:4oxikyyHyEe1wlYQFMGITfW/r01wYtfj8yjwru7bSWE= -github.com/persistenceOne/ibc-apps/middleware/packet-forward-middleware/v7 v7.0.0-20240109170424-f4a8f29910eb h1:diFYtzQUDh2WKIy21ywl45rWidpVbd0jAKcR0ddlndw= -github.com/persistenceOne/ibc-apps/middleware/packet-forward-middleware/v7 v7.0.0-20240109170424-f4a8f29910eb/go.mod h1:v+x4hTcShRZylXZNsK5zXFQ8QMV448rbIY4e9HDXBkY= github.com/persistenceOne/ibc-apps/modules/ibc-hooks/v7 v7.0.0-20240109170424-f4a8f29910eb h1:p0eVmgkvqjwWpqxn1YtKRj+tVa1+3B3JELK+riYSHQM= github.com/persistenceOne/ibc-apps/modules/ibc-hooks/v7 v7.0.0-20240109170424-f4a8f29910eb/go.mod h1:fbwFAk6oxuKiFvp5SLCItT34A1+F5LjGcMNOTuU8h4c= github.com/persistenceOne/ibc-go/v7 v7.2.0-lsm3 h1:U4NsRXpg9VHCFVyrk1JfG+sIA3frtZIWbtNmmumjta8= @@ -1243,6 +1243,8 @@ go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqe go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/mock v0.2.0 h1:TaP3xedm7JaAgScZO7tlvlKrqT0p7I6OsdGB5YNSMDU= +go.uber.org/mock v0.2.0/go.mod h1:J0y0rp9L3xiff1+ZBfKxlC1fz2+aO16tw0tsDOixfuM= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= diff --git a/interchaintest/lsm_halt_fork_test.go b/interchaintest/lsm_halt_fork_test.go index 9db6e735..9c036cbd 100644 --- a/interchaintest/lsm_halt_fork_test.go +++ b/interchaintest/lsm_halt_fork_test.go @@ -114,7 +114,7 @@ func TestPersistenceLSMHaltFork(t *testing.T) { require.Len(t, validators, validatorsCount, "validator returned must match count of validators created") // Bond first user - firstUserBondAmount := sdk.NewInt(100_000_000_000) + firstUserBondAmount := math.NewInt(100_000_000_000) firstUserBondCoins := sdk.NewCoin(testDenom, firstUserBondAmount) txHash, err := chainNode.ExecTx(ctx, firstUser.KeyName(), "staking", "delegate", validators[0].OperatorAddress, firstUserBondCoins.String(), @@ -167,7 +167,7 @@ func TestPersistenceLSMHaltFork(t *testing.T) { checkCtx, cancelFn = context.WithTimeout(context.Background(), 15*time.Second) // Create second user delegation - secondUserBondAmount := sdk.NewInt(88_000_000_000) // 88k magic number to tell from 100k that comes from A + secondUserBondAmount := math.NewInt(88_000_000_000) // 88k magic number to tell from 100k that comes from A secondUserBondCoins := sdk.NewCoin(testDenom, secondUserBondAmount) _, err = chainNode.ExecTx(checkCtx, secondUser.KeyName(), "staking", "delegate", validators[0].OperatorAddress, secondUserBondCoins.String(), diff --git a/interchaintest/module_pfm_test.go b/interchaintest/module_pfm_test.go index 9176d8a5..70a30ca5 100644 --- a/interchaintest/module_pfm_test.go +++ b/interchaintest/module_pfm_test.go @@ -10,6 +10,7 @@ import ( "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" 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" @@ -299,3 +300,437 @@ func TestPacketForwardMiddlewareRouter(t *testing.T) { require.Equal(t, math.NewInt(transferAmount), thirdHopEscrowBalance) }) } + +// TestTimeoutOnForward ensures the PFM has proper accounting on the escrow accounts over a timeout event. +func TestTimeoutOnForward(t *testing.T) { + if testing.Short() { + t.Skip("skipping in short mode") + } + + var ( + ctx = context.Background() + client, network = interchaintest.DockerSetup(t) + rep = testreporter.NewNopReporter() + eRep = rep.RelayerExecReporter(t) + chainIdA, chainIdB, chainIdC, chainIdD = "chain-a", "chain-b", "chain-c", "chain-d" + ) + + helpers.SetConfig() + + // base config which all networks will use as defaults. + baseCfg := ibc.ChainConfig{ + Type: "cosmos", + Name: "persistence", + ChainID: "", // change this for each + Images: []ibc.DockerImage{ + PersistenceCoreImage, + }, + Bin: "persistenceCore", + Bech32Prefix: "persistence", + Denom: helpers.PersistenceBondDenom, + CoinType: fmt.Sprintf("%d", helpers.PersistenceCoinType), + GasPrices: fmt.Sprintf("0%s", helpers.PersistenceBondDenom), + GasAdjustment: 2.0, + TrustingPeriod: "112h", + NoHostMount: false, + ConfigFileOverrides: nil, + EncodingConfig: persistenceEncoding(), + UsingNewGenesisCommand: true, + ModifyGenesis: cosmos.ModifyGenesis(defaultGenesisOverridesKV), + } + + // Set specific chain ids for each so they are their own unique networks + baseCfg.ChainID = chainIdA + configA := baseCfg + + baseCfg.ChainID = chainIdB + configB := baseCfg + + baseCfg.ChainID = chainIdC + configC := baseCfg + + baseCfg.ChainID = chainIdD + configD := baseCfg + + // Create chain factory with multiple Persistence individual networks. + numVals := 1 + numFullNodes := 0 + + cf := interchaintest.NewBuiltinChainFactory(zaptest.NewLogger(t), []*interchaintest.ChainSpec{ + { + Name: "persistence", + ChainConfig: configA, + NumValidators: &numVals, + NumFullNodes: &numFullNodes, + }, + { + Name: "persistence", + ChainConfig: configB, + NumValidators: &numVals, + NumFullNodes: &numFullNodes, + }, + { + Name: "persistence", + ChainConfig: configC, + NumValidators: &numVals, + NumFullNodes: &numFullNodes, + }, + { + Name: "persistence", + ChainConfig: configD, + NumValidators: &numVals, + NumFullNodes: &numFullNodes, + }, + }) + + 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, pathCD) + require.NoError(t, err) + + chainAHeight, err := chainA.Height(ctx) + require.NoError(t, err) + + chainBHeight, err = chainB.Height(ctx) + require.NoError(t, err) + + // Poll for the MsgTimeout on chainB and the MsgAck on chainA + _, err = cosmos.PollForMessage[*chantypes.MsgTimeout](ctx, chainB, chainB.Config().EncodingConfig.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)) + + // Send IBC transfer from ChainA -> ChainB -> ChainC -> ChainD that will succeed + secondHopMetadata = &PacketMetadata{ + Forward: &ForwardMetadata{ + Receiver: userD.FormattedAddress(), + Channel: cdChan.ChannelID, + Port: cdChan.PortID, + }, + } + 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, + }, + } + + memo, err = json.Marshal(firstHopMetadata) + require.NoError(t, err) + + opts = ibc.TransferOptions{ + Memo: string(memo), + } + + chainAHeight, err = chainA.Height(ctx) + require.NoError(t, err) + + transferTx, err = chainA.SendIBCTransfer(ctx, abChan.ChannelID, userA.KeyName(), transfer, opts) + require.NoError(t, err) + + _, err = testutil.PollForAck(ctx, chainA, chainAHeight, chainAHeight+30, transferTx.Packet) + require.NoError(t, err) + + err = testutil.WaitForBlocks(ctx, 5, chainA) + require.NoError(t, err) + + // Assert balances are updated to reflect tokens now being on ChainD + 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.Sub(transferAmount))) + require.True(t, chainBBalance.Equal(zeroBal)) + require.True(t, chainCBalance.Equal(zeroBal)) + require.True(t, chainDBalance.Equal(transferAmount)) + + 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(transferAmount)) + require.True(t, secondHopEscrowBalance.Equal(transferAmount)) + require.True(t, thirdHopEscrowBalance.Equal(transferAmount)) + + // Compose IBC tx that will attempt to go from ChainD -> ChainC -> ChainB -> ChainA but timeout between ChainB->ChainA + transfer = ibc.WalletAmount{ + Address: userC.FormattedAddress(), + Denom: thirdHopDenom, + Amount: transferAmount, + } + + secondHopMetadata = &PacketMetadata{ + Forward: &ForwardMetadata{ + Receiver: userA.FormattedAddress(), + Channel: baChan.ChannelID, + Port: baChan.PortID, + Timeout: 1 * time.Second, + }, + } + nextBz, err = json.Marshal(secondHopMetadata) + require.NoError(t, err) + next = string(nextBz) + + firstHopMetadata = &PacketMetadata{ + Forward: &ForwardMetadata{ + Receiver: userB.FormattedAddress(), + Channel: cbChan.ChannelID, + Port: cbChan.PortID, + Next: &next, + }, + } + + memo, err = json.Marshal(firstHopMetadata) + require.NoError(t, err) + + chainDHeight, err := chainD.Height(ctx) + require.NoError(t, err) + + transferTx, err = chainD.SendIBCTransfer(ctx, dcChan.ChannelID, userD.KeyName(), transfer, ibc.TransferOptions{Memo: string(memo)}) + require.NoError(t, err) + + _, err = testutil.PollForAck(ctx, chainD, chainDHeight, chainDHeight+25, transferTx.Packet) + require.NoError(t, err) + + err = testutil.WaitForBlocks(ctx, 5, chainD) + require.NoError(t, err) + + // Assert balances to ensure timeout happened and user funds are still present on ChainD + 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.Sub(transferAmount))) + require.True(t, chainBBalance.Equal(zeroBal)) + require.True(t, chainCBalance.Equal(zeroBal)) + require.True(t, chainDBalance.Equal(transferAmount)) + + 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(transferAmount)) + require.True(t, secondHopEscrowBalance.Equal(transferAmount)) + require.True(t, thirdHopEscrowBalance.Equal(transferAmount)) +}