-
Notifications
You must be signed in to change notification settings - Fork 586
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
[FEAT] ICS20-V2 Path Forwarding #6693
Changes from 136 commits
c07bca9
e66bd89
034f472
4cc6a85
71f830c
28ff9b6
4e55137
ca056cf
147cf17
9bbfa1a
4f57916
e04047e
c73d5f6
03cc2e5
65f4476
db85a29
999ed80
be72180
4c333c5
0478cb9
37f5b5e
3fd3345
24e2407
7858528
55d7bf9
1a95262
44e15cb
334d5a9
7466b19
5c46d79
99e6b0a
a114af6
d800caa
dd873a1
a292a3f
e25ba0c
f39d173
9b39944
06ca9a5
7897ef3
786a4f1
5747756
7e2e6df
a84b0e7
43877df
8eae033
dbcff45
bb69698
f19a145
8f86dda
d4b06c8
a9391a4
575403e
50ccd94
87eb32e
e8b9d5a
57aab01
2519593
3bf6c04
64c2fc4
7cd7260
6673e74
550eeef
4c54a88
debc4cb
e50fa40
6d87d95
7c8f516
feb7b01
6fd1773
efcfa5d
21dbb37
70d7a29
465b16c
ec472cf
e483b9a
0c9f368
413b7c1
ae2046a
ba7c4a8
6b3b5aa
5070e50
d874b54
a8cd302
fbb9cd8
c4987a7
a90b671
c348732
789b09b
c00ddd9
fc6c111
72714b3
c546f69
60a6b99
dc47641
832b1bd
360b2f8
84c7c33
007dee1
14d5486
a28a549
d6db0c5
64bc502
1ca1f2f
87d1f91
ef6d22f
da2f9f6
e68143b
6619821
43eceed
aa860ac
2b4d24b
59e3df7
e3ffcff
9c5ae03
c2a6bc6
c3ccbfc
5345776
74088ba
272c12b
4349a1d
24cd07f
dca8c42
37d7335
6e1a082
8f9691f
c505243
d715455
4e61ea9
3e2a5fa
5ea9d79
100ccc7
13d3ac6
b6d77d7
4d19be1
2838aef
7afb7f6
0221c6b
cc46a63
e67aeaf
966a644
e92e086
6ea614f
18d0567
1b7214d
1f5e61f
0a78bde
07036a9
1442874
25c73c4
baf3fd3
7d2a881
4ea36c2
0a28e9b
7411f2d
199c14a
c7af85a
25239a2
90d57db
e310e27
f28e937
50daed0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,12 +5,16 @@ package transfer | |
import ( | ||
"context" | ||
"testing" | ||
"time" | ||
|
||
"github.com/strangelove-ventures/interchaintest/v8/ibc" | ||
testifysuite "github.com/stretchr/testify/suite" | ||
|
||
"github.com/cosmos/ibc-go/e2e/testsuite" | ||
"github.com/cosmos/ibc-go/e2e/testsuite/query" | ||
"github.com/cosmos/ibc-go/e2e/testvalues" | ||
transfertypes "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" | ||
clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" | ||
) | ||
|
||
func TestTransferForwardingTestSuite(t *testing.T) { | ||
|
@@ -98,3 +102,69 @@ func (s *TransferForwardingTestSuite) TestThreeChainSetup() { | |
s.Require().Equal(expected, actualBalance.Int64()) | ||
}) | ||
} | ||
|
||
DimitrisJim marked this conversation as resolved.
Show resolved
Hide resolved
|
||
func (s *TransferForwardingTestSuite) TestForwarding_WithLastChainBeingICS20v1_Succeeds() { | ||
DimitrisJim marked this conversation as resolved.
Show resolved
Hide resolved
|
||
ctx := context.TODO() | ||
t := s.T() | ||
|
||
relayer, chains := s.GetRelayer(), s.GetAllChains() | ||
|
||
chainA, chainB, chainC := chains[0], chains[1], chains[2] | ||
|
||
channelAtoB := s.GetChainAChannel() | ||
|
||
// Creating a new path between chain B and chain C with a ICS20-v1 channel | ||
opts := s.TransferChannelOptions() | ||
opts.Version = transfertypes.V1 | ||
channelBtoC, _ := s.CreatePath(ctx, chainB, chainC, ibc.DefaultClientOpts(), opts) | ||
s.Require().Equal(transfertypes.V1, channelBtoC.Version, "the channel version is not ics20-1") | ||
|
||
chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) | ||
chainAAddress := chainAWallet.FormattedAddress() | ||
chainADenom := chainA.Config().Denom | ||
|
||
chainCWallet := s.CreateUserOnChainC(ctx, testvalues.StartingTokenAmount) | ||
chainCAddress := chainCWallet.FormattedAddress() | ||
|
||
t.Run("IBC transfer from A to C with forwarding through B", func(t *testing.T) { | ||
inFiveMinutes := time.Now().Add(5 * time.Minute).UnixNano() | ||
forwarding := transfertypes.NewForwarding(false, transfertypes.Hop{ | ||
PortId: channelBtoC.PortID, | ||
ChannelId: channelBtoC.ChannelID, | ||
}) | ||
|
||
msgTransfer := testsuite.GetMsgTransfer( | ||
channelAtoB.PortID, | ||
channelAtoB.ChannelID, | ||
transfertypes.V2, | ||
testvalues.DefaultTransferCoins(chainADenom), | ||
chainAAddress, | ||
chainCAddress, | ||
clienttypes.ZeroHeight(), | ||
uint64(inFiveMinutes), | ||
"", | ||
forwarding) | ||
resp := s.BroadcastMessages(ctx, chainA, chainAWallet, msgTransfer) | ||
s.AssertTxSuccess(resp) | ||
}) | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we're omitting the usual escrow checks and stuff here, but I think that's actually fine. We have those in other tests and this test ends up being short and sweet because of it! |
||
t.Run("start relayer", func(t *testing.T) { | ||
s.StartRelayer(relayer) | ||
}) | ||
|
||
t.Run("packets are relayed from A to B to C", func(t *testing.T) { | ||
chainCDenom := transfertypes.NewDenom(chainADenom, | ||
transfertypes.NewTrace(channelBtoC.Counterparty.PortID, channelBtoC.Counterparty.ChannelID), | ||
transfertypes.NewTrace(channelAtoB.Counterparty.PortID, channelAtoB.Counterparty.ChannelID), | ||
) | ||
|
||
s.AssertPacketRelayed(ctx, chainA, channelAtoB.PortID, channelAtoB.ChannelID, 1) | ||
s.AssertPacketRelayed(ctx, chainB, channelBtoC.PortID, channelBtoC.ChannelID, 1) | ||
|
||
actualBalance, err := query.Balance(ctx, chainC, chainCAddress, chainCDenom.IBCDenom()) | ||
s.Require().NoError(err) | ||
|
||
expected := testvalues.IBCTransferAmount | ||
s.Require().Equal(expected, actualBalance.Int64()) | ||
}) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -191,57 +191,70 @@ func (s *E2ETestSuite) SetupChains(ctx context.Context, channelOptionsModifier C | |
// SetupTest will by default use the default channel options to create a path between the chains. | ||
// if non default channel options are required, the test suite must override the `SetupTest` function. | ||
func (s *E2ETestSuite) SetupTest() { | ||
s.SetupPath(ibc.DefaultClientOpts(), defaultChannelOpts(s.GetAllChains())) | ||
s.SetupPaths(ibc.DefaultClientOpts(), defaultChannelOpts(s.GetAllChains())) | ||
} | ||
|
||
// SetupPath creates a path between the chains using the provided client and channel options. | ||
func (s *E2ETestSuite) SetupPath(clientOpts ibc.CreateClientOptions, channelOpts ibc.CreateChannelOptions) { | ||
// SetupPaths creates paths between the chains using the provided client and channel options. | ||
// The paths are created such that ChainA is connected to ChainB, ChainB is connected to ChainC etc. | ||
func (s *E2ETestSuite) SetupPaths(clientOpts ibc.CreateClientOptions, channelOpts ibc.CreateChannelOptions) { | ||
s.T().Logf("Setting up path for: %s", s.T().Name()) | ||
r := s.GetRelayer() | ||
|
||
if s.channels[s.T().Name()] == nil { | ||
s.channels[s.T().Name()] = make(map[ibc.Chain][]ibc.ChannelOutput) | ||
} | ||
|
||
ctx := context.TODO() | ||
allChains := s.GetAllChains() | ||
for i := 0; i < len(allChains)-1; i++ { | ||
chainA, chainB := allChains[i], allChains[i+1] | ||
pathName := s.generatePathName() | ||
s.T().Logf("establishing path between %s and %s on path %s", chainA.Config().ChainID, chainB.Config().ChainID, pathName) | ||
_, _ = s.CreatePath(ctx, chainA, chainB, clientOpts, channelOpts) | ||
} | ||
} | ||
|
||
func (s *E2ETestSuite) CreatePath( | ||
DimitrisJim marked this conversation as resolved.
Show resolved
Hide resolved
|
||
ctx context.Context, | ||
chainA ibc.Chain, | ||
chainB ibc.Chain, | ||
clientOpts ibc.CreateClientOptions, | ||
channelOpts ibc.CreateChannelOptions, | ||
) (chainAChannel ibc.ChannelOutput, chainBChannel ibc.ChannelOutput) { | ||
r := s.GetRelayer() | ||
|
||
err := r.GeneratePath(ctx, s.GetRelayerExecReporter(), chainA.Config().ChainID, chainB.Config().ChainID, pathName) | ||
s.Require().NoError(err) | ||
pathName := s.generatePathName() | ||
s.T().Logf("establishing path between %s and %s on path %s", chainA.Config().ChainID, chainB.Config().ChainID, pathName) | ||
|
||
// Create new clients | ||
err = r.CreateClients(ctx, s.GetRelayerExecReporter(), pathName, clientOpts) | ||
s.Require().NoError(err) | ||
err = test.WaitForBlocks(ctx, 1, chainA, chainB) | ||
s.Require().NoError(err) | ||
err := r.GeneratePath(ctx, s.GetRelayerExecReporter(), chainA.Config().ChainID, chainB.Config().ChainID, pathName) | ||
s.Require().NoError(err) | ||
|
||
err = r.CreateConnections(ctx, s.GetRelayerExecReporter(), pathName) | ||
s.Require().NoError(err) | ||
err = test.WaitForBlocks(ctx, 1, chainA, chainB) | ||
s.Require().NoError(err) | ||
// Create new clients | ||
err = r.CreateClients(ctx, s.GetRelayerExecReporter(), pathName, clientOpts) | ||
s.Require().NoError(err) | ||
err = test.WaitForBlocks(ctx, 1, chainA, chainB) | ||
s.Require().NoError(err) | ||
|
||
err = r.CreateChannel(ctx, s.GetRelayerExecReporter(), pathName, channelOpts) | ||
s.Require().NoError(err) | ||
err = test.WaitForBlocks(ctx, 1, chainA, chainB) | ||
s.Require().NoError(err) | ||
err = r.CreateConnections(ctx, s.GetRelayerExecReporter(), pathName) | ||
s.Require().NoError(err) | ||
err = test.WaitForBlocks(ctx, 1, chainA, chainB) | ||
s.Require().NoError(err) | ||
|
||
s.testPaths[s.T().Name()] = append(s.testPaths[s.T().Name()], pathName) | ||
err = r.CreateChannel(ctx, s.GetRelayerExecReporter(), pathName, channelOpts) | ||
s.Require().NoError(err) | ||
err = test.WaitForBlocks(ctx, 1, chainA, chainB) | ||
s.Require().NoError(err) | ||
|
||
for _, c := range []ibc.Chain{chainA, chainB} { | ||
channels, err := r.GetChannels(ctx, s.GetRelayerExecReporter(), c.Config().ChainID) | ||
s.Require().NoError(err) | ||
channelsA, err := r.GetChannels(ctx, s.GetRelayerExecReporter(), chainA.Config().ChainID) | ||
s.Require().NoError(err) | ||
|
||
// only the most recent channel is relevant. | ||
s.channels[s.T().Name()][c] = []ibc.ChannelOutput{channels[len(channels)-1]} | ||
channelsB, err := r.GetChannels(ctx, s.GetRelayerExecReporter(), chainB.Config().ChainID) | ||
s.Require().NoError(err) | ||
|
||
err = relayer.ApplyPacketFilter(ctx, s.T(), r, c.Config().ChainID, channels) | ||
s.Require().NoError(err, "failed to watch port and channel on chain: %s", c.Config().ChainID) | ||
} | ||
if s.channels[s.T().Name()] == nil { | ||
s.channels[s.T().Name()] = make(map[ibc.Chain][]ibc.ChannelOutput) | ||
} | ||
|
||
// keep track of channels associated with a given chain for access within the tests. | ||
s.channels[s.T().Name()][chainA] = channelsA | ||
s.channels[s.T().Name()][chainB] = channelsB | ||
Comment on lines
+253
to
+254
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. there was a fix on main that assigns the correct values here, should look something like Only the last channel created is relevant for this test. tagging myself to fix! @chatton There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. there's actually a small restructure that needs to happen here #6721 I will address this in that PR once it gets synced with main after this is merged |
||
|
||
s.testPaths[s.T().Name()] = append(s.testPaths[s.T().Name()], pathName) | ||
|
||
return channelsA[len(channelsA)-1], channelsB[len(channelsB)-1] | ||
} | ||
|
||
// GetChainAChannel returns the ibc.ChannelOutput for the current test. | ||
|
@@ -692,7 +705,7 @@ func getValidatorsAndFullNodes(chainIdx int) (int, int) { | |
} | ||
|
||
// GetMsgTransfer returns a MsgTransfer that is constructed based on the channel version | ||
func GetMsgTransfer(portID, channelID, version string, tokens sdk.Coins, sender, receiver string, timeoutHeight clienttypes.Height, timeoutTimestamp uint64, memo string) *transfertypes.MsgTransfer { | ||
func GetMsgTransfer(portID, channelID, version string, tokens sdk.Coins, sender, receiver string, timeoutHeight clienttypes.Height, timeoutTimestamp uint64, memo string, forwarding transfertypes.Forwarding) *transfertypes.MsgTransfer { | ||
if len(tokens) == 0 { | ||
panic(errors.New("tokens cannot be empty")) | ||
} | ||
|
@@ -712,7 +725,7 @@ func GetMsgTransfer(portID, channelID, version string, tokens sdk.Coins, sender, | |
Tokens: sdk.NewCoins(), | ||
} | ||
case transfertypes.V2: | ||
msg = transfertypes.NewMsgTransfer(portID, channelID, tokens, sender, receiver, timeoutHeight, timeoutTimestamp, memo) | ||
msg = transfertypes.NewMsgTransfer(portID, channelID, tokens, sender, receiver, timeoutHeight, timeoutTimestamp, memo, forwarding) | ||
default: | ||
panic(fmt.Errorf("unsupported transfer version: %s", version)) | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should probably also have a changelog entry for authz support.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we should have a larger entry to highlight the changes to transfer?