@agoric/cosmos 0.35.0-u18.5 → 0.35.0-u18a.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,7 @@
1
1
  package vtransfer_test
2
2
 
3
3
  import (
4
+ "bytes"
4
5
  "context"
5
6
  "encoding/json"
6
7
  "fmt"
@@ -11,7 +12,10 @@ import (
11
12
 
12
13
  app "github.com/Agoric/agoric-sdk/golang/cosmos/app"
13
14
  "github.com/Agoric/agoric-sdk/golang/cosmos/vm"
15
+ "github.com/cosmos/cosmos-sdk/codec"
14
16
  "github.com/cosmos/cosmos-sdk/store"
17
+ "github.com/cosmos/cosmos-sdk/testutil/testdata"
18
+ "github.com/iancoleman/orderedmap"
15
19
  "github.com/stretchr/testify/suite"
16
20
  "github.com/tendermint/tendermint/libs/log"
17
21
  dbm "github.com/tendermint/tm-db"
@@ -20,18 +24,25 @@ import (
20
24
  swingsettesting "github.com/Agoric/agoric-sdk/golang/cosmos/x/swingset/testing"
21
25
  swingsettypes "github.com/Agoric/agoric-sdk/golang/cosmos/x/swingset/types"
22
26
  vibckeeper "github.com/Agoric/agoric-sdk/golang/cosmos/x/vibc/keeper"
27
+ vibctypes "github.com/Agoric/agoric-sdk/golang/cosmos/x/vibc/types"
23
28
 
24
29
  "github.com/cosmos/cosmos-sdk/baseapp"
25
30
  sdk "github.com/cosmos/cosmos-sdk/types"
26
31
  authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
27
32
  banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
33
+ packetforwardtypes "github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v6/packetforward/types"
28
34
  ibctransfertypes "github.com/cosmos/ibc-go/v6/modules/apps/transfer/types"
29
35
  channeltypes "github.com/cosmos/ibc-go/v6/modules/core/04-channel/types"
36
+ ibcexported "github.com/cosmos/ibc-go/v6/modules/core/exported"
30
37
  ibctesting "github.com/cosmos/ibc-go/v6/testing"
31
38
  "github.com/cosmos/ibc-go/v6/testing/simapp"
32
39
  tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
33
40
  )
34
41
 
42
+ const (
43
+ StorePacketData = true
44
+ )
45
+
35
46
  type IntegrationTestSuite struct {
36
47
  suite.Suite
37
48
 
@@ -40,17 +51,42 @@ type IntegrationTestSuite struct {
40
51
  // testing chains used for convenience and readability
41
52
  chainA *ibctesting.TestChain
42
53
  chainB *ibctesting.TestChain
54
+ chainC *ibctesting.TestChain
55
+
56
+ lastChannelOffset map[int]int
57
+ endpoints map[int]map[int]*ibctesting.Endpoint
43
58
 
44
59
  queryClient ibctransfertypes.QueryClient
45
60
  }
46
61
 
62
+ type TestingAppMaker func() (ibctesting.TestingApp, map[string]json.RawMessage)
63
+
64
+ func TestTransferTestSuite(t *testing.T) {
65
+ s := new(IntegrationTestSuite)
66
+ suite.Run(t, s)
67
+ }
68
+
47
69
  // interBlockCacheOpt returns a BaseApp option function that sets the persistent
48
70
  // inter-block write-through cache.
49
71
  func interBlockCacheOpt() func(*baseapp.BaseApp) {
50
72
  return baseapp.SetInterBlockCache(store.NewCommitKVStoreCacheManager())
51
73
  }
52
74
 
53
- type TestingAppMaker func() (ibctesting.TestingApp, map[string]json.RawMessage)
75
+ func (s *IntegrationTestSuite) getEndpoint(a, b int) *ibctesting.Endpoint {
76
+ amap := s.endpoints[a]
77
+ if amap == nil {
78
+ return nil
79
+ }
80
+ return amap[b]
81
+ }
82
+
83
+ func (s *IntegrationTestSuite) cacheEndpoint(a, b int, endpoint *ibctesting.Endpoint) {
84
+ amap := s.endpoints[a]
85
+ if amap == nil {
86
+ amap = make(map[int]*ibctesting.Endpoint)
87
+ }
88
+ amap[b] = endpoint
89
+ }
54
90
 
55
91
  // Each instance has unique IBC genesis state with deterministic
56
92
  // client/connection/channel initial sequence numbers
@@ -62,6 +98,15 @@ func computeSequences(instance int) (clientSeq, connectionSeq, channelSeq int) {
62
98
  return baseSequence, baseSequence + 10, baseSequence + 50
63
99
  }
64
100
 
101
+ func (s *IntegrationTestSuite) nextChannelOffset(instance int) int {
102
+ offset, ok := s.lastChannelOffset[instance]
103
+ if ok {
104
+ offset += 1
105
+ }
106
+ s.lastChannelOffset[instance] = offset
107
+ return offset
108
+ }
109
+
65
110
  func SetupAgoricTestingApp(instance int) TestingAppMaker {
66
111
  return func() (ibctesting.TestingApp, map[string]json.RawMessage) {
67
112
  db := dbm.NewMemDB()
@@ -132,17 +177,15 @@ func SetupAgoricTestingApp(instance int) TestingAppMaker {
132
177
  }
133
178
  }
134
179
 
135
- func TestKeeperTestSuite(t *testing.T) {
136
- suite.Run(t, new(IntegrationTestSuite))
137
- }
138
-
139
- // SetupTest initializes an IntegrationTestSuite with two similar chains, a
180
+ // SetupTest initializes an IntegrationTestSuite with three similar chains, a
140
181
  // shared coordinator, and a query client that happens to point at chainA.
141
182
  func (s *IntegrationTestSuite) SetupTest() {
183
+ s.lastChannelOffset = make(map[int]int)
184
+ s.endpoints = make(map[int]map[int]*ibctesting.Endpoint)
142
185
  s.coordinator = ibctesting.NewCoordinator(s.T(), 0)
143
186
 
144
187
  chains := make(map[string]*ibctesting.TestChain)
145
- for i := 0; i < 2; i++ {
188
+ for i := 0; i < 3; i++ {
146
189
  ibctesting.DefaultTestingAppInit = SetupAgoricTestingApp(i)
147
190
 
148
191
  chainID := ibctesting.GetChainID(i)
@@ -183,6 +226,7 @@ func (s *IntegrationTestSuite) SetupTest() {
183
226
  s.coordinator.Chains = chains
184
227
  s.chainA = s.coordinator.GetChain(ibctesting.GetChainID(0))
185
228
  s.chainB = s.coordinator.GetChain(ibctesting.GetChainID(1))
229
+ s.chainC = s.coordinator.GetChain(ibctesting.GetChainID(2))
186
230
 
187
231
  agoricApp := s.GetApp(s.chainA)
188
232
 
@@ -191,6 +235,10 @@ func (s *IntegrationTestSuite) SetupTest() {
191
235
  s.queryClient = ibctransfertypes.NewQueryClient(queryHelper)
192
236
  }
193
237
 
238
+ func (s *IntegrationTestSuite) GetChainByIndex(index int) *ibctesting.TestChain {
239
+ return s.coordinator.GetChain(ibctesting.GetChainID(index))
240
+ }
241
+
194
242
  func (s *IntegrationTestSuite) GetApp(chain *ibctesting.TestChain) *app.GaiaApp {
195
243
  app, ok := chain.App.(*app.GaiaApp)
196
244
  if !ok {
@@ -200,30 +248,53 @@ func (s *IntegrationTestSuite) GetApp(chain *ibctesting.TestChain) *app.GaiaApp
200
248
  return app
201
249
  }
202
250
 
203
- func (s *IntegrationTestSuite) NewTransferPath() *ibctesting.Path {
204
- path := ibctesting.NewPath(s.chainA, s.chainB)
205
- _, _, channelASeq := computeSequences(0)
206
- _, _, channelBSeq := computeSequences(1)
207
- path.EndpointA.ChannelID = fmt.Sprintf("channel-%d", channelASeq)
208
- path.EndpointB.ChannelID = fmt.Sprintf("channel-%d", channelBSeq)
251
+ func (s *IntegrationTestSuite) NewTransferPath(endpointAChainIdx, endpointBChainIdx int) *ibctesting.Path {
252
+ endpointAChain := s.coordinator.GetChain(ibctesting.GetChainID(endpointAChainIdx))
253
+ endpointBChain := s.coordinator.GetChain(ibctesting.GetChainID(endpointBChainIdx))
254
+
255
+ chAOffset := s.nextChannelOffset(endpointAChainIdx)
256
+ chBOffset := s.nextChannelOffset(endpointBChainIdx)
257
+ path := ibctesting.NewPath(endpointAChain, endpointBChain)
258
+ _, _, channelASeq := computeSequences(endpointAChainIdx)
259
+ _, _, channelBSeq := computeSequences(endpointBChainIdx)
260
+ path.EndpointA.ChannelID = fmt.Sprintf("channel-%d", channelASeq+chAOffset)
261
+ path.EndpointB.ChannelID = fmt.Sprintf("channel-%d", channelBSeq+chBOffset)
209
262
  path.EndpointA.ChannelConfig.PortID = ibctesting.TransferPort
210
263
  path.EndpointB.ChannelConfig.PortID = ibctesting.TransferPort
211
264
  path.EndpointA.ChannelConfig.Version = "ics20-1"
212
265
  path.EndpointB.ChannelConfig.Version = "ics20-1"
213
266
 
214
- s.coordinator.Setup(path)
267
+ endpoint := s.getEndpoint(endpointAChainIdx, endpointBChainIdx)
268
+ if endpoint == nil {
269
+ s.coordinator.SetupConnections(path)
270
+ s.cacheEndpoint(endpointAChainIdx, endpointBChainIdx, path.EndpointA)
271
+ s.cacheEndpoint(endpointBChainIdx, endpointAChainIdx, path.EndpointB)
272
+ } else {
273
+ path.EndpointA.ClientID = endpoint.ClientID
274
+ path.EndpointA.ConnectionID = endpoint.ConnectionID
275
+
276
+ path.EndpointB.ClientID = endpoint.Counterparty.ClientID
277
+ path.EndpointB.ConnectionID = endpoint.Counterparty.ConnectionID
278
+ }
279
+ s.coordinator.CreateChannels(path)
215
280
 
216
- s.coordinator.CommitBlock(s.chainA, s.chainB)
281
+ s.coordinator.CommitBlock(endpointAChain, endpointBChain)
217
282
 
218
283
  return path
219
284
  }
220
285
 
286
+ func (s *IntegrationTestSuite) resetActionQueue(chain *ibctesting.TestChain) {
287
+ err := swingsettesting.ResetActionQueue(s.T(), chain.GetContext(), s.GetApp(chain).SwingSetKeeper)
288
+ s.Require().NoError(err)
289
+ }
290
+
221
291
  func (s *IntegrationTestSuite) assertActionQueue(chain *ibctesting.TestChain, expectedRecords []swingsettypes.InboundQueueRecord) {
222
292
  actualRecords, err := swingsettesting.GetActionQueueRecords(
223
293
  s.T(),
224
294
  chain.GetContext(),
225
295
  s.GetApp(chain).SwingSetKeeper,
226
296
  )
297
+ s.resetActionQueue(chain)
227
298
  s.Require().NoError(err)
228
299
 
229
300
  exLen := len(expectedRecords)
@@ -263,44 +334,86 @@ func (s *IntegrationTestSuite) RegisterBridgeTarget(chain *ibctesting.TestChain,
263
334
  agdServer := s.GetApp(chain).AgdServer
264
335
  defer agdServer.SetControllerContext(chain.GetContext())()
265
336
  var reply string
266
- err := agdServer.ReceiveMessage(
337
+ bz, err := json.Marshal(struct {
338
+ Type string
339
+ Target string
340
+ }{"BRIDGE_TARGET_REGISTER", target})
341
+ s.Require().NoError(err)
342
+ err = agdServer.ReceiveMessage(
267
343
  &vm.Message{
268
344
  Port: agdServer.GetPort("vtransfer"),
269
- Data: `{"type":"BRIDGE_TARGET_REGISTER","target":"` + target + `"}`,
345
+ Data: string(bz),
270
346
  },
271
347
  &reply,
272
348
  )
273
349
  s.Require().NoError(err)
274
- s.Require().Equal(reply, "true")
350
+ s.Require().Equal("true", reply)
275
351
  }
276
352
 
277
- func (s *IntegrationTestSuite) TransferFromSourceChain(
278
- srcChain *ibctesting.TestChain,
353
+ func (s *IntegrationTestSuite) TransferFromEndpoint(
354
+ srcContext sdk.Context,
355
+ src *ibctesting.Endpoint,
279
356
  data ibctransfertypes.FungibleTokenPacketData,
280
- src, dst *ibctesting.Endpoint,
281
- ) (channeltypes.Packet, error) {
357
+ ) error {
282
358
  tokenAmt, ok := sdk.NewIntFromString(data.Amount)
283
359
  s.Require().True(ok)
284
360
 
285
- timeoutHeight := srcChain.GetTimeoutHeight()
286
- packet := channeltypes.NewPacket(data.GetBytes(), 0, src.ChannelConfig.PortID, src.ChannelID, dst.ChannelConfig.PortID, dst.ChannelID, timeoutHeight, 0)
361
+ timeoutHeight := src.Counterparty.Chain.GetTimeoutHeight()
287
362
 
288
363
  // send a transfer packet from src
289
- imt := ibctransfertypes.MsgTransfer{
290
- SourcePort: packet.SourcePort,
291
- SourceChannel: packet.SourceChannel,
292
- Memo: data.Memo,
293
- Token: sdk.NewCoin(data.Denom, tokenAmt),
294
- Sender: data.Sender,
295
- Receiver: data.Receiver,
296
- TimeoutHeight: packet.TimeoutHeight,
297
- TimeoutTimestamp: packet.TimeoutTimestamp,
364
+ imt := ibctransfertypes.NewMsgTransfer(
365
+ src.ChannelConfig.PortID,
366
+ src.ChannelID,
367
+ sdk.NewCoin(data.Denom, tokenAmt),
368
+ data.Sender,
369
+ data.Receiver,
370
+ timeoutHeight,
371
+ 0,
372
+ data.Memo,
373
+ )
374
+
375
+ tk := s.GetApp(src.Chain).TransferKeeper
376
+ _, err := tk.Transfer(srcContext, imt)
377
+ return err
378
+ }
379
+
380
+ func (s *IntegrationTestSuite) prependDenomTrace(sender *ibctesting.Endpoint, trace string) string {
381
+ return fmt.Sprintf("%s/%s/%s", sender.ChannelConfig.PortID, sender.ChannelID, trace)
382
+ }
383
+
384
+ func (s *IntegrationTestSuite) overrideSendPacketData(cdc codec.Codec, data []byte, hookedSender string) ([]byte, error) {
385
+ var ftpd ibctransfertypes.FungibleTokenPacketData
386
+ err := json.Unmarshal(data, &ftpd)
387
+ if err != nil {
388
+ return nil, err
389
+ }
390
+
391
+ // XXX: This is a hack to get around the fact that `TransferKeeper.Transfer`
392
+ // doesn't understand hooked senders. We need to put the hooked sender back
393
+ // in so that the vtransfer keeper can strip it out as if it had been there
394
+ // all along.
395
+ newFtpd := ftpd
396
+ newFtpd.Sender = hookedSender
397
+
398
+ // Permute the encoded data to ensure that it is different that what the TransferKeeper.Transfer specified.
399
+ if bz := ftpd.GetBytes(); !bytes.Equal(data, bz) {
400
+ return newFtpd.GetBytes(), nil
401
+ }
402
+
403
+ bz, err := cdc.MarshalJSON(&ftpd)
404
+ if err != nil {
405
+ return nil, err
406
+ }
407
+
408
+ if !bytes.Equal(data, bz) {
409
+ newBz, err := cdc.MarshalJSON(&newFtpd)
410
+ if err != nil {
411
+ return nil, err
412
+ }
413
+ return newBz, nil
298
414
  }
299
- imr, err := s.GetApp(srcChain).TransferKeeper.Transfer(srcChain.GetContext(), &imt)
300
- s.Require().NoError(err)
301
- packet.Sequence = imr.Sequence
302
415
 
303
- return packet, nil
416
+ return nil, fmt.Errorf("failed to find a way to permute packet data: %s", string(data))
304
417
  }
305
418
 
306
419
  func (s *IntegrationTestSuite) mintToAddress(chain *ibctesting.TestChain, addr sdk.AccAddress, denom, amount string) {
@@ -314,139 +427,388 @@ func (s *IntegrationTestSuite) mintToAddress(chain *ibctesting.TestChain, addr s
314
427
  s.Require().NoError(err)
315
428
  err = app.BankKeeper.SendCoinsFromModuleToAccount(chain.GetContext(), ibctransfertypes.ModuleName, addr, coins)
316
429
  s.Require().NoError(err)
317
-
318
- // Verify success.
319
- balances := app.BankKeeper.GetAllBalances(chain.GetContext(), addr)
320
- s.Require().Equal(coins[0], balances[1])
321
430
  }
322
431
 
323
- // TestTransferFromAgdToAgd relays an IBC transfer initiated from a chain A to a
324
- // chain B, and relays the chain B's resulting acknowledgement in return. It
325
- // verifies that the source and destination accounts' bridge targets are called
326
- // by inspecting their resulting actionQueue records. By committing blocks
327
- // between actions, the test verifies that the VM results are permitted to be
328
- // async across blocks.
329
- func (s *IntegrationTestSuite) TestTransferFromAgdToAgd() {
330
- path := s.NewTransferPath()
331
- s.Require().Equal(path.EndpointA.ChannelID, "channel-1050")
332
-
333
- s.Run("TransferFromAgdToAgd", func() {
334
- // create a transfer packet's data contents
335
- baseReceiver := s.chainB.SenderAccounts[1].SenderAccount.GetAddress().String()
336
- receiverHook, err := types.JoinHookedAddress(baseReceiver, []byte("?what=arbitrary-data&why=to-test-bridge-targets"))
337
- s.Require().NoError(err)
338
- transferData := ibctransfertypes.NewFungibleTokenPacketData(
339
- "uosmo",
340
- "1000000",
341
- s.chainA.SenderAccount.GetAddress().String(),
342
- receiverHook,
343
- `"This is a JSON memo"`,
344
- )
432
+ // TestHops relays an IBC transfer initiated from a chain A to a chain B, via 0
433
+ // or more intermediate chains' PacketForwardMiddleware, and relays the chain
434
+ // B's resulting acknowledgement back through the intermediate chains to chain A
435
+ // in return. It verifies that the source and destination accounts' bridge
436
+ // targets are called by inspecting their resulting actionQueue records. By
437
+ // committing blocks between actions, the test verifies that the VM results are
438
+ // permitted to be async across blocks.
439
+ func (s *IntegrationTestSuite) TestHops() {
440
+ testCases := []struct {
441
+ name string
442
+ senderIsTarget bool
443
+ receiverIsTarget bool
444
+ senderHookData []byte
445
+ receiverHookData []byte
446
+ }{
447
+ {"NoTargetsNoHooks", false, false, nil, nil},
448
+ {"NoTargetsReceiverHook", false, false, nil, []byte("?what=arbitrary-data&why=to-test-bridge-targets")},
449
+ {"NoTargetsSenderHook", false, false, []byte("?name=alice&peer=bob"), nil},
450
+ {"NoTargetsBothHooks", false, false, []byte("?name=alice&peer=bob"), []byte("?what=arbitrary-data&why=to-test-bridge-targets")},
451
+ {"SenderTargetNoHooks", true, false, nil, nil},
452
+ {"SenderTargetReceiverHook", true, false, nil, []byte("?what=arbitrary-data&why=to-test-bridge-targets")},
453
+ {"SenderTargetSenderHook", true, false, []byte("?name=alice&peer=bob"), nil},
454
+ {"SenderTargetBothHooks", true, false, []byte("?name=alice&peer=bob"), []byte("?what=arbitrary-data&why=to-test-bridge-targets")},
455
+ {"ReceiverTargetNoHooks", false, true, nil, nil},
456
+ {"ReceiverTargetReceiverHook", false, true, nil, []byte("?what=arbitrary-data&why=to-test-bridge-targets")},
457
+ {"ReceiverTargetSenderHook", false, true, []byte("?name=alice&peer=bob"), nil},
458
+ {"ReceiverTargetBothHooks", false, true, []byte("?name=alice&peer=bob"), []byte("?what=arbitrary-data&why=to-test-bridge-targets")},
459
+ {"BothTargetsNoHooks", true, true, nil, nil},
460
+ {"BothTargetsReceiverHook", true, true, nil, []byte("?what=arbitrary-data&why=to-test-bridge-targets")},
461
+ {"BothTargetsSenderHook", true, true, []byte("?name=alice&peer=bob"), nil},
462
+ {"BothTargetsBothHooks", true, true, []byte("?name=alice&peer=bob"), []byte("?what=arbitrary-data&why=to-test-bridge-targets")},
463
+ }
345
464
 
346
- // Register the sender and receiver as bridge targets on their specific
347
- // chain.
348
- s.RegisterBridgeTarget(s.chainA, transferData.Sender)
349
- s.RegisterBridgeTarget(s.chainB, baseReceiver)
465
+ for hops := 1; hops <= 2; hops += 1 {
466
+ for _, tc := range testCases {
467
+ tc := tc
468
+ name := fmt.Sprintf("%s_%dHop", tc.name, hops)
469
+ s.Run(name, func() {
470
+ _, _, baseSenderAddr := testdata.KeyTestPubAddr()
471
+ baseSender := baseSenderAddr.String()
472
+
473
+ _, _, baseReceiverAddr := testdata.KeyTestPubAddr()
474
+ baseReceiver := baseReceiverAddr.String()
475
+
476
+ var receiver, sender string
477
+ var err error
478
+ if tc.senderHookData != nil {
479
+ sender, err = types.JoinHookedAddress(baseSender, tc.senderHookData)
480
+ s.Require().NoError(err)
481
+ } else {
482
+ sender = baseSender
483
+ }
350
484
 
351
- s.mintToAddress(s.chainA, s.chainA.SenderAccount.GetAddress(), transferData.Denom, transferData.Amount)
485
+ if tc.receiverHookData != nil {
486
+ receiver, err = types.JoinHookedAddress(baseReceiver, tc.receiverHookData)
487
+ s.Require().NoError(err)
488
+ } else {
489
+ receiver = baseReceiver
490
+ }
352
491
 
353
- // Initiate the transfer
354
- packet, err := s.TransferFromSourceChain(s.chainA, transferData, path.EndpointA, path.EndpointB)
355
- s.Require().NoError(err)
492
+ var overriddenPacketData []byte
493
+ overrideSendPacketData := func(ctx sdk.Context, cdc codec.Codec, data []byte) ([]byte, error) {
494
+ newData, err := s.overrideSendPacketData(cdc, data, sender)
495
+ overriddenPacketData = newData
496
+ return overriddenPacketData, err
497
+ }
498
+ // Reset the chain state.
499
+ for i := 0; i <= hops; i += 1 {
500
+ chain := s.GetChainByIndex(i)
501
+ s.resetActionQueue(chain)
502
+ s.GetApp(chain).VtransferKeeper.SetDebugging(StorePacketData, overrideSendPacketData)
503
+
504
+ // Only the first chain is the sender, so don't override any other packets.
505
+ overrideSendPacketData = nil
506
+ }
356
507
 
357
- // Relay the packet
358
- s.coordinator.CommitBlock(s.chainA)
359
- err = path.EndpointB.UpdateClient()
360
- s.Require().NoError(err)
361
- s.coordinator.CommitBlock(s.chainB)
508
+ // Construct the transfer path from chainA=0, [chainC=2, [chainD=3...]], and finally chainB=1.
509
+ // This guarantees that the first and last chains are s.chainA and s.chainB.
510
+ paths := make([]*ibctesting.Path, hops)
511
+ {
512
+ endpointAChainIndex := 0 // s.chainA
513
+ endpointBChainIndex := 2 // s.chainC
514
+ for i := 0; i < hops; i += 1 {
515
+ if i == hops-1 {
516
+ // Final path's endpointB is s.chainB=1.
517
+ endpointBChainIndex = 1
518
+ }
519
+ // Each path is an endpointA->endpointB pair. We specify them by index.
520
+ paths[i] = s.NewTransferPath(endpointAChainIndex, endpointBChainIndex)
521
+ // The next path's A is the current path's B...
522
+ endpointAChainIndex = endpointBChainIndex
523
+ // and the next path's B is the next chain in the sequence.
524
+ endpointBChainIndex += 1
525
+ }
526
+ }
362
527
 
363
- writeAcknowledgementHeight := s.chainB.CurrentHeader.Height
364
- writeAcknowledgementTime := s.chainB.CurrentHeader.Time.Unix()
528
+ // create a transfer packet's data contents
529
+ hopReceiver := receiver
530
+ var memoBytes []byte
531
+ for pathIdx := hops - 1; pathIdx > 0; pathIdx -= 1 {
532
+ m := struct {
533
+ Forward packetforwardtypes.ForwardMetadata `json:"forward"`
534
+ }{}
535
+ if memoBytes != nil {
536
+ m.Forward.Next = packetforwardtypes.NewJSONObject(false, memoBytes, *orderedmap.New())
537
+ }
538
+ m.Forward.Receiver = hopReceiver
539
+
540
+ // Previous hops should not have a bech32 address in the receiver field,
541
+ // or tokens may get stuck en route rather than returned on error.
542
+ hopReceiver = "pfm"
543
+ m.Forward.Port = paths[pathIdx].EndpointA.ChannelConfig.PortID
544
+ m.Forward.Channel = paths[pathIdx].EndpointA.ChannelID
545
+
546
+ memoBytes, err = json.Marshal(m)
547
+ s.Require().NoError(err)
548
+ }
365
549
 
366
- err = path.EndpointB.RecvPacket(packet)
367
- s.Require().NoError(err)
550
+ var memo string
551
+ if memoBytes != nil {
552
+ memo = string(memoBytes)
553
+ } else {
554
+ memo = `This is not a JSON memo`
555
+ }
368
556
 
369
- // Create a success ack as defined by ICS20.
370
- ack := channeltypes.NewResultAcknowledgement([]byte{1})
371
- // Create a different ack to show that a contract can change it.
372
- contractAck := channeltypes.NewResultAcknowledgement([]byte{5})
557
+ denomTrace := "uosmo"
558
+ transferData := ibctransfertypes.NewFungibleTokenPacketData(
559
+ denomTrace,
560
+ "1000000",
561
+ baseSender, // TODO: ideally this would just be sender, and `TransferKeeper.Transfer` would accept address hooks.
562
+ hopReceiver,
563
+ memo,
564
+ )
565
+
566
+ // Register the sender and receiver as bridge targets on their specific
567
+ // chain.
568
+ if tc.senderIsTarget {
569
+ s.RegisterBridgeTarget(s.chainA, baseSender)
570
+ }
571
+ if tc.receiverIsTarget {
572
+ s.RegisterBridgeTarget(s.chainB, baseReceiver)
573
+ }
373
574
 
374
- s.coordinator.CommitBlock(s.chainA, s.chainB)
575
+ s.mintToAddress(s.chainA, baseSenderAddr, transferData.Denom, transferData.Amount)
375
576
 
376
- {
377
- expectedRecords := []swingsettypes.InboundQueueRecord{}
378
- s.assertActionQueue(s.chainA, expectedRecords)
379
- }
577
+ // Initiate the transfer
578
+ sendContext := s.chainA.GetContext()
579
+ err = s.TransferFromEndpoint(sendContext, paths[0].EndpointA, transferData)
580
+ s.Require().NoError(err)
581
+
582
+ sendPacket, err := ParsePacketFromEvents(sendContext.EventManager().Events())
583
+ s.Require().NoError(err)
584
+
585
+ s.coordinator.CommitBlock(s.chainA)
586
+
587
+ // Relay the packet through the intermediaries to the final destination.
588
+ var packetRes *sdk.Result
589
+ var writeAcknowledgementHeight, writeAcknowledgementTime int64
590
+ for pathIdx := 0; pathIdx < hops; pathIdx += 1 {
591
+ nextPath := paths[pathIdx]
592
+ err = nextPath.EndpointB.UpdateClient()
593
+ s.Require().NoError(err)
594
+ s.coordinator.CommitBlock(nextPath.EndpointB.Chain)
595
+
596
+ writeAcknowledgementHeight = nextPath.EndpointB.Chain.CurrentHeader.Height
597
+ writeAcknowledgementTime = nextPath.EndpointB.Chain.CurrentHeader.Time.Unix()
598
+
599
+ packetRes, err = nextPath.EndpointB.RecvPacketWithResult(sendPacket)
600
+ s.Require().NoError(err)
601
+
602
+ s.coordinator.CommitBlock(nextPath.EndpointA.Chain, nextPath.EndpointB.Chain)
603
+
604
+ denomTrace = s.prependDenomTrace(nextPath.EndpointB, denomTrace)
605
+
606
+ {
607
+ expectedRecords := []swingsettypes.InboundQueueRecord{}
608
+ s.assertActionQueue(nextPath.EndpointA.Chain, expectedRecords)
609
+ }
610
+
611
+ if pathIdx >= hops-1 {
612
+ break
613
+ }
614
+
615
+ // The PFM should have received the packet and advertised a send toward the last path.
616
+ sendPacket, err = ParsePacketFromEvents(packetRes.GetEvents())
617
+ s.Require().NoError(err)
618
+ }
619
+
620
+ var ack ibcexported.Acknowledgement
621
+ var ackedPacket channeltypes.Packet
622
+
623
+ expectedAck := channeltypes.NewResultAcknowledgement([]byte{1})
380
624
 
381
- {
382
- expectedRecords := []swingsettypes.InboundQueueRecord{
383
625
  {
384
- Action: &vibckeeper.WriteAcknowledgementEvent{
385
- ActionHeader: &vm.ActionHeader{
386
- Type: "VTRANSFER_IBC_EVENT",
387
- BlockHeight: writeAcknowledgementHeight,
388
- BlockTime: writeAcknowledgementTime,
389
- },
390
- Event: "writeAcknowledgement",
391
- Target: baseReceiver,
392
- Packet: packet,
393
- Acknowledgement: ack.Acknowledgement(),
394
- },
395
- Context: swingsettypes.ActionContext{
396
- BlockHeight: writeAcknowledgementHeight,
397
- // TxHash is filled in below
398
- MsgIdx: 0,
399
- },
400
- },
401
- }
626
+ var events sdk.Events
627
+ var ackData []byte
628
+ if packetRes != nil {
629
+ events = packetRes.GetEvents()
630
+ ackData, err = ParseAckFromEvents(events)
631
+ }
632
+ if tc.receiverIsTarget {
633
+ s.Require().Nil(ackData)
634
+ // The packet was not yet acknowledged, so write out an ack from the VM, one block later
635
+ s.coordinator.CommitBlock(s.chainB)
636
+
637
+ vmAckContext := s.chainB.GetContext()
638
+ err = s.GetApp(s.chainB).VtransferKeeper.ReceiveWriteAcknowledgement(vmAckContext, sendPacket, expectedAck)
639
+ s.Require().NoError(err)
640
+
641
+ events = vmAckContext.EventManager().Events()
642
+ ackData, err = ParseAckFromEvents(events)
643
+ }
644
+
645
+ s.Require().NoError(err)
646
+
647
+ ackedPacket, err = ParsePacketFromFilteredEvents(events, channeltypes.EventTypeWriteAck)
648
+ s.Require().NoError(err)
649
+ ack = vibctypes.NewRawAcknowledgement(ackData)
650
+
651
+ s.coordinator.CommitBlock(s.chainB)
652
+
653
+ expectedRecords := []swingsettypes.InboundQueueRecord{}
654
+ if tc.receiverIsTarget {
655
+ expectedRecords = append(expectedRecords, swingsettypes.InboundQueueRecord{
656
+ Action: &vibckeeper.WriteAcknowledgementEvent{
657
+ ActionHeader: &vm.ActionHeader{
658
+ Type: "VTRANSFER_IBC_EVENT",
659
+ BlockHeight: writeAcknowledgementHeight,
660
+ BlockTime: writeAcknowledgementTime,
661
+ },
662
+ Event: "writeAcknowledgement",
663
+ Target: baseReceiver,
664
+ Packet: sendPacket,
665
+ Acknowledgement: expectedAck.Acknowledgement(),
666
+ },
667
+ Context: swingsettypes.ActionContext{
668
+ BlockHeight: writeAcknowledgementHeight,
669
+ // TxHash is filled in below
670
+ MsgIdx: 0,
671
+ },
672
+ })
673
+ }
674
+
675
+ s.assertActionQueue(s.chainB, expectedRecords)
676
+ }
402
677
 
403
- s.assertActionQueue(s.chainB, expectedRecords)
678
+ // Send the acks back.
679
+ for pathIdx := hops - 1; pathIdx > 0; pathIdx -= 1 {
680
+ priorPath := paths[pathIdx]
404
681
 
405
- // write out a different acknowledgement from the "contract", one block later.
406
- s.coordinator.CommitBlock(s.chainB)
407
- err = s.GetApp(s.chainB).VtransferKeeper.ReceiveWriteAcknowledgement(s.chainB.GetContext(), packet, contractAck)
408
- s.Require().NoError(err)
682
+ // Update Client
683
+ err = priorPath.EndpointA.UpdateClient()
684
+ s.Require().NoError(err)
409
685
 
410
- s.coordinator.CommitBlock(s.chainB)
411
- }
686
+ // Prove the PFM packet's acknowledgement.
687
+ ackRes, err := acknowledgePacketWithResult(priorPath.EndpointA, ackedPacket, ack.Acknowledgement())
688
+ s.Require().NoError(err)
412
689
 
413
- // Update Client
414
- err = path.EndpointA.UpdateClient()
415
- s.Require().NoError(err)
690
+ ackedPacket, err = ParsePacketFromFilteredEvents(ackRes.GetEvents(), channeltypes.EventTypeWriteAck)
691
+ s.Require().NoError(err)
416
692
 
417
- acknowledgementHeight := s.chainA.CurrentHeader.Height
418
- acknowledgementTime := s.chainA.CurrentHeader.Time.Unix()
693
+ ackData, err := ParseAckFromEvents(ackRes.GetEvents())
694
+ s.Require().NoError(err)
695
+ ack = vibctypes.NewRawAcknowledgement(ackData)
419
696
 
420
- // Prove the packet's acknowledgement.
421
- err = path.EndpointA.AcknowledgePacket(packet, contractAck.Acknowledgement())
422
- s.Require().NoError(err)
697
+ s.coordinator.CommitBlock(priorPath.EndpointA.Chain, priorPath.EndpointB.Chain)
698
+ }
699
+
700
+ // Update Client
701
+ err = paths[0].EndpointA.UpdateClient()
702
+ s.Require().NoError(err)
703
+
704
+ acknowledgementHeight := s.chainA.CurrentHeader.Height
705
+ acknowledgementTime := s.chainA.CurrentHeader.Time.Unix()
706
+
707
+ // Prove the initial packet's acknowledgement.
708
+ ackRes, err := acknowledgePacketWithResult(paths[0].EndpointA, ackedPacket, ack.Acknowledgement())
709
+ s.Require().NoError(err)
710
+
711
+ // Commit the block to finalize the acknowledgement.
712
+ s.coordinator.CommitBlock(s.chainA, s.chainB)
713
+
714
+ // Verify the resulting events.
715
+ gotEvents := 0
716
+ expectedEvents := 2
717
+ for _, event := range ackRes.GetEvents() {
718
+ if event.Type == ibctransfertypes.EventTypePacket {
719
+ gotEvents += 1
720
+ if gotEvents == 2 && len(event.Attributes) == 1 {
721
+ // We get a trailing event with a single "success" attribute.
722
+ s.Require().Equal(ibctransfertypes.AttributeKeyAckSuccess, string(event.Attributes[0].Key))
723
+ s.Require().Equal("\x01", string(event.Attributes[0].Value))
724
+ continue
725
+ }
726
+ expectedAttrs := 6
727
+ gotAttrs := 0
728
+ for _, attr := range event.Attributes {
729
+ switch string(attr.Key) {
730
+ case "module":
731
+ s.Require().Equal(ibctransfertypes.ModuleName, string(attr.Value))
732
+ gotAttrs += 1
733
+ case ibctransfertypes.AttributeKeyAckSuccess:
734
+ s.Require().Equal("\x01", string(attr.Value))
735
+ gotAttrs += 1
736
+ case ibctransfertypes.AttributeKeyMemo:
737
+ s.Require().Equal(transferData.Memo, string(attr.Value))
738
+ gotAttrs += 1
739
+ case ibctransfertypes.AttributeKeyReceiver:
740
+ s.Require().Equal(transferData.Receiver, string(attr.Value))
741
+ gotAttrs += 1
742
+ case "sender": // ibctransfertypes.AttributeKeySender:
743
+ s.Require().Equal(transferData.Sender, string(attr.Value))
744
+ gotAttrs += 1
745
+ case ibctransfertypes.AttributeKeyDenom:
746
+ s.Require().Equal(transferData.Denom, string(attr.Value))
747
+ gotAttrs += 1
748
+ case ibctransfertypes.AttributeKeyAmount:
749
+ s.Require().Equal(transferData.Amount, string(attr.Value))
750
+ gotAttrs += 1
751
+ }
752
+ }
753
+ s.Require().Equal(expectedAttrs, gotAttrs, `expected %d %s type attributes, got %d`, expectedAttrs, ibctransfertypes.EventTypePacket, gotAttrs)
754
+ }
755
+ }
423
756
 
424
- s.coordinator.CommitBlock(s.chainA, s.chainB)
757
+ // The resulting IBC packet event should be what we expected.
758
+ s.Require().Equal(expectedEvents, gotEvents, `expected %d %s type events, got %d`, expectedEvents, ibctransfertypes.EventTypePacket, gotEvents)
425
759
 
426
- {
427
- expectedRecords := []swingsettypes.InboundQueueRecord{
428
760
  {
429
- Action: &vibckeeper.WriteAcknowledgementEvent{
430
- ActionHeader: &vm.ActionHeader{
431
- Type: "VTRANSFER_IBC_EVENT",
432
- BlockHeight: acknowledgementHeight,
433
- BlockTime: acknowledgementTime,
434
- },
435
- Event: "acknowledgementPacket",
436
- Target: transferData.Sender,
437
- Packet: packet,
438
- Acknowledgement: contractAck.Acknowledgement(),
439
- Relayer: s.chainA.SenderAccount.GetAddress(),
440
- },
441
- Context: swingsettypes.ActionContext{
442
- BlockHeight: acknowledgementHeight,
443
- // TxHash is filled in below
444
- MsgIdx: 0,
445
- },
446
- },
447
- }
761
+ // Undo the sender data override.
762
+ expectedPacket := ackedPacket
763
+ expectedPacket.Data = overriddenPacketData
764
+
765
+ expectedRecords := []swingsettypes.InboundQueueRecord{}
766
+ if tc.senderIsTarget {
767
+ expectedRecords = append(expectedRecords, swingsettypes.InboundQueueRecord{
768
+ Action: &vibckeeper.WriteAcknowledgementEvent{
769
+ ActionHeader: &vm.ActionHeader{
770
+ Type: "VTRANSFER_IBC_EVENT",
771
+ BlockHeight: acknowledgementHeight,
772
+ BlockTime: acknowledgementTime,
773
+ },
774
+ Event: "acknowledgementPacket",
775
+ Target: baseSender,
776
+ Packet: expectedPacket,
777
+ Acknowledgement: ack.Acknowledgement(),
778
+ Relayer: s.chainA.SenderAccount.GetAddress(),
779
+ },
780
+ Context: swingsettypes.ActionContext{
781
+ BlockHeight: acknowledgementHeight,
782
+ // TxHash is filled in below
783
+ MsgIdx: 0,
784
+ },
785
+ })
786
+ }
787
+
788
+ s.assertActionQueue(s.chainA, expectedRecords)
789
+ }
448
790
 
449
- s.assertActionQueue(s.chainA, expectedRecords)
791
+ // Verify the resulting received coin balance.
792
+ req := &banktypes.QueryAllBalancesRequest{
793
+ Address: baseReceiver,
794
+ }
795
+ res, err := s.GetApp(s.chainB).BankKeeper.AllBalances(s.chainB.GetContext(), req)
796
+ s.Require().NoError(err)
797
+
798
+ amt, ok := sdk.NewIntFromString(transferData.Amount)
799
+ s.Require().True(ok)
800
+
801
+ // Decode the denom trace to get the denom hash.
802
+ hashReq := &ibctransfertypes.QueryDenomHashRequest{
803
+ Trace: denomTrace,
804
+ }
805
+ hashRes, err := s.GetApp(s.chainB).TransferKeeper.DenomHash(s.chainB.GetContext(), hashReq)
806
+ s.Require().NoError(err)
807
+ receivedDenom := `ibc/` + hashRes.Hash
808
+
809
+ coins := sdk.NewCoins(sdk.NewCoin(receivedDenom, amt))
810
+ s.Require().True(coins.IsEqual(res.Balances))
811
+ })
450
812
  }
451
- })
813
+ }
452
814
  }