@agoric/cosmos 0.35.0-u18.4 → 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.
Files changed (39) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/ante/inbound_test.go +2 -2
  3. package/app/app.go +26 -9
  4. package/app/upgrade.go +47 -134
  5. package/daemon/cmd/root.go +48 -15
  6. package/git-revision.txt +1 -1
  7. package/go.mod +82 -71
  8. package/go.sum +200 -167
  9. package/package.json +2 -2
  10. package/proto/agoric/swingset/swingset.proto +25 -0
  11. package/proto/agoric/vbank/vbank.proto +7 -0
  12. package/types/address_hooks.go +242 -0
  13. package/types/address_hooks_test.go +221 -0
  14. package/x/swingset/genesis.go +99 -21
  15. package/x/swingset/keeper/keeper.go +16 -7
  16. package/x/swingset/module.go +17 -2
  17. package/x/swingset/testing/queue.go +8 -0
  18. package/x/swingset/types/default-params.go +31 -5
  19. package/x/swingset/types/expected_keepers.go +2 -2
  20. package/x/swingset/types/msgs.go +34 -12
  21. package/x/swingset/types/params.go +53 -43
  22. package/x/swingset/types/params_test.go +75 -9
  23. package/x/swingset/types/swingset.pb.go +386 -56
  24. package/x/vbank/README.md +6 -1
  25. package/x/vbank/keeper/keeper.go +4 -9
  26. package/x/vbank/keeper/migrations.go +30 -0
  27. package/x/vbank/module.go +8 -2
  28. package/x/vbank/types/params.go +43 -2
  29. package/x/vbank/types/vbank.pb.go +105 -36
  30. package/x/vbank/vbank_test.go +12 -7
  31. package/x/vibc/keeper/keeper.go +2 -5
  32. package/x/vibc/keeper/triggers.go +3 -6
  33. package/x/vibc/types/receiver.go +11 -5
  34. package/x/vstorage/testing/queue.go +10 -3
  35. package/x/vtransfer/ibc_middleware.go +5 -1
  36. package/x/vtransfer/ibc_middleware_test.go +511 -145
  37. package/x/vtransfer/keeper/keeper.go +215 -50
  38. package/x/vtransfer/types/genesis.pb.go +1 -0
  39. package/x/vtransfer/utils_test.go +111 -0
@@ -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,26 +12,37 @@ 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"
18
22
 
23
+ "github.com/Agoric/agoric-sdk/golang/cosmos/types"
19
24
  swingsettesting "github.com/Agoric/agoric-sdk/golang/cosmos/x/swingset/testing"
20
25
  swingsettypes "github.com/Agoric/agoric-sdk/golang/cosmos/x/swingset/types"
21
26
  vibckeeper "github.com/Agoric/agoric-sdk/golang/cosmos/x/vibc/keeper"
27
+ vibctypes "github.com/Agoric/agoric-sdk/golang/cosmos/x/vibc/types"
22
28
 
23
29
  "github.com/cosmos/cosmos-sdk/baseapp"
24
30
  sdk "github.com/cosmos/cosmos-sdk/types"
25
31
  authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
26
32
  banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
33
+ packetforwardtypes "github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v6/packetforward/types"
27
34
  ibctransfertypes "github.com/cosmos/ibc-go/v6/modules/apps/transfer/types"
28
35
  channeltypes "github.com/cosmos/ibc-go/v6/modules/core/04-channel/types"
36
+ ibcexported "github.com/cosmos/ibc-go/v6/modules/core/exported"
29
37
  ibctesting "github.com/cosmos/ibc-go/v6/testing"
30
38
  "github.com/cosmos/ibc-go/v6/testing/simapp"
31
39
  tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
32
40
  )
33
41
 
42
+ const (
43
+ StorePacketData = true
44
+ )
45
+
34
46
  type IntegrationTestSuite struct {
35
47
  suite.Suite
36
48
 
@@ -39,17 +51,42 @@ type IntegrationTestSuite struct {
39
51
  // testing chains used for convenience and readability
40
52
  chainA *ibctesting.TestChain
41
53
  chainB *ibctesting.TestChain
54
+ chainC *ibctesting.TestChain
55
+
56
+ lastChannelOffset map[int]int
57
+ endpoints map[int]map[int]*ibctesting.Endpoint
42
58
 
43
59
  queryClient ibctransfertypes.QueryClient
44
60
  }
45
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
+
46
69
  // interBlockCacheOpt returns a BaseApp option function that sets the persistent
47
70
  // inter-block write-through cache.
48
71
  func interBlockCacheOpt() func(*baseapp.BaseApp) {
49
72
  return baseapp.SetInterBlockCache(store.NewCommitKVStoreCacheManager())
50
73
  }
51
74
 
52
- 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
+ }
53
90
 
54
91
  // Each instance has unique IBC genesis state with deterministic
55
92
  // client/connection/channel initial sequence numbers
@@ -61,6 +98,15 @@ func computeSequences(instance int) (clientSeq, connectionSeq, channelSeq int) {
61
98
  return baseSequence, baseSequence + 10, baseSequence + 50
62
99
  }
63
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
+
64
110
  func SetupAgoricTestingApp(instance int) TestingAppMaker {
65
111
  return func() (ibctesting.TestingApp, map[string]json.RawMessage) {
66
112
  db := dbm.NewMemDB()
@@ -131,17 +177,15 @@ func SetupAgoricTestingApp(instance int) TestingAppMaker {
131
177
  }
132
178
  }
133
179
 
134
- func TestKeeperTestSuite(t *testing.T) {
135
- suite.Run(t, new(IntegrationTestSuite))
136
- }
137
-
138
- // SetupTest initializes an IntegrationTestSuite with two similar chains, a
180
+ // SetupTest initializes an IntegrationTestSuite with three similar chains, a
139
181
  // shared coordinator, and a query client that happens to point at chainA.
140
182
  func (s *IntegrationTestSuite) SetupTest() {
183
+ s.lastChannelOffset = make(map[int]int)
184
+ s.endpoints = make(map[int]map[int]*ibctesting.Endpoint)
141
185
  s.coordinator = ibctesting.NewCoordinator(s.T(), 0)
142
186
 
143
187
  chains := make(map[string]*ibctesting.TestChain)
144
- for i := 0; i < 2; i++ {
188
+ for i := 0; i < 3; i++ {
145
189
  ibctesting.DefaultTestingAppInit = SetupAgoricTestingApp(i)
146
190
 
147
191
  chainID := ibctesting.GetChainID(i)
@@ -182,6 +226,7 @@ func (s *IntegrationTestSuite) SetupTest() {
182
226
  s.coordinator.Chains = chains
183
227
  s.chainA = s.coordinator.GetChain(ibctesting.GetChainID(0))
184
228
  s.chainB = s.coordinator.GetChain(ibctesting.GetChainID(1))
229
+ s.chainC = s.coordinator.GetChain(ibctesting.GetChainID(2))
185
230
 
186
231
  agoricApp := s.GetApp(s.chainA)
187
232
 
@@ -190,6 +235,10 @@ func (s *IntegrationTestSuite) SetupTest() {
190
235
  s.queryClient = ibctransfertypes.NewQueryClient(queryHelper)
191
236
  }
192
237
 
238
+ func (s *IntegrationTestSuite) GetChainByIndex(index int) *ibctesting.TestChain {
239
+ return s.coordinator.GetChain(ibctesting.GetChainID(index))
240
+ }
241
+
193
242
  func (s *IntegrationTestSuite) GetApp(chain *ibctesting.TestChain) *app.GaiaApp {
194
243
  app, ok := chain.App.(*app.GaiaApp)
195
244
  if !ok {
@@ -199,30 +248,53 @@ func (s *IntegrationTestSuite) GetApp(chain *ibctesting.TestChain) *app.GaiaApp
199
248
  return app
200
249
  }
201
250
 
202
- func (s *IntegrationTestSuite) NewTransferPath() *ibctesting.Path {
203
- path := ibctesting.NewPath(s.chainA, s.chainB)
204
- _, _, channelASeq := computeSequences(0)
205
- _, _, channelBSeq := computeSequences(1)
206
- path.EndpointA.ChannelID = fmt.Sprintf("channel-%d", channelASeq)
207
- 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)
208
262
  path.EndpointA.ChannelConfig.PortID = ibctesting.TransferPort
209
263
  path.EndpointB.ChannelConfig.PortID = ibctesting.TransferPort
210
264
  path.EndpointA.ChannelConfig.Version = "ics20-1"
211
265
  path.EndpointB.ChannelConfig.Version = "ics20-1"
212
266
 
213
- 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)
214
280
 
215
- s.coordinator.CommitBlock(s.chainA, s.chainB)
281
+ s.coordinator.CommitBlock(endpointAChain, endpointBChain)
216
282
 
217
283
  return path
218
284
  }
219
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
+
220
291
  func (s *IntegrationTestSuite) assertActionQueue(chain *ibctesting.TestChain, expectedRecords []swingsettypes.InboundQueueRecord) {
221
292
  actualRecords, err := swingsettesting.GetActionQueueRecords(
222
293
  s.T(),
223
294
  chain.GetContext(),
224
295
  s.GetApp(chain).SwingSetKeeper,
225
296
  )
297
+ s.resetActionQueue(chain)
226
298
  s.Require().NoError(err)
227
299
 
228
300
  exLen := len(expectedRecords)
@@ -262,44 +334,86 @@ func (s *IntegrationTestSuite) RegisterBridgeTarget(chain *ibctesting.TestChain,
262
334
  agdServer := s.GetApp(chain).AgdServer
263
335
  defer agdServer.SetControllerContext(chain.GetContext())()
264
336
  var reply string
265
- 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(
266
343
  &vm.Message{
267
344
  Port: agdServer.GetPort("vtransfer"),
268
- Data: `{"type":"BRIDGE_TARGET_REGISTER","target":"` + target + `"}`,
345
+ Data: string(bz),
269
346
  },
270
347
  &reply,
271
348
  )
272
349
  s.Require().NoError(err)
273
- s.Require().Equal(reply, "true")
350
+ s.Require().Equal("true", reply)
274
351
  }
275
352
 
276
- func (s *IntegrationTestSuite) TransferFromSourceChain(
277
- srcChain *ibctesting.TestChain,
353
+ func (s *IntegrationTestSuite) TransferFromEndpoint(
354
+ srcContext sdk.Context,
355
+ src *ibctesting.Endpoint,
278
356
  data ibctransfertypes.FungibleTokenPacketData,
279
- src, dst *ibctesting.Endpoint,
280
- ) (channeltypes.Packet, error) {
357
+ ) error {
281
358
  tokenAmt, ok := sdk.NewIntFromString(data.Amount)
282
359
  s.Require().True(ok)
283
360
 
284
- timeoutHeight := srcChain.GetTimeoutHeight()
285
- packet := channeltypes.NewPacket(data.GetBytes(), 0, src.ChannelConfig.PortID, src.ChannelID, dst.ChannelConfig.PortID, dst.ChannelID, timeoutHeight, 0)
361
+ timeoutHeight := src.Counterparty.Chain.GetTimeoutHeight()
286
362
 
287
363
  // send a transfer packet from src
288
- imt := ibctransfertypes.MsgTransfer{
289
- SourcePort: packet.SourcePort,
290
- SourceChannel: packet.SourceChannel,
291
- Memo: data.Memo,
292
- Token: sdk.NewCoin(data.Denom, tokenAmt),
293
- Sender: data.Sender,
294
- Receiver: data.Receiver,
295
- TimeoutHeight: packet.TimeoutHeight,
296
- 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
297
414
  }
298
- imr, err := s.GetApp(srcChain).TransferKeeper.Transfer(srcChain.GetContext(), &imt)
299
- s.Require().NoError(err)
300
- packet.Sequence = imr.Sequence
301
415
 
302
- return packet, nil
416
+ return nil, fmt.Errorf("failed to find a way to permute packet data: %s", string(data))
303
417
  }
304
418
 
305
419
  func (s *IntegrationTestSuite) mintToAddress(chain *ibctesting.TestChain, addr sdk.AccAddress, denom, amount string) {
@@ -313,136 +427,388 @@ func (s *IntegrationTestSuite) mintToAddress(chain *ibctesting.TestChain, addr s
313
427
  s.Require().NoError(err)
314
428
  err = app.BankKeeper.SendCoinsFromModuleToAccount(chain.GetContext(), ibctransfertypes.ModuleName, addr, coins)
315
429
  s.Require().NoError(err)
316
-
317
- // Verify success.
318
- balances := app.BankKeeper.GetAllBalances(chain.GetContext(), addr)
319
- s.Require().Equal(coins[0], balances[1])
320
430
  }
321
431
 
322
- // TestTransferFromAgdToAgd relays an IBC transfer initiated from a chain A to a
323
- // chain B, and relays the chain B's resulting acknowledgement in return. It
324
- // verifies that the source and destination accounts' bridge targets are called
325
- // by inspecting their resulting actionQueue records. By committing blocks
326
- // between actions, the test verifies that the VM results are permitted to be
327
- // async across blocks.
328
- func (s *IntegrationTestSuite) TestTransferFromAgdToAgd() {
329
- path := s.NewTransferPath()
330
- s.Require().Equal(path.EndpointA.ChannelID, "channel-1050")
331
-
332
- s.Run("TransferFromAgdToAgd", func() {
333
- // create a transfer packet's data contents
334
- transferData := ibctransfertypes.NewFungibleTokenPacketData(
335
- "uosmo",
336
- "1000000",
337
- s.chainA.SenderAccount.GetAddress().String(),
338
- s.chainB.SenderAccounts[1].SenderAccount.GetAddress().String(),
339
- `"This is a JSON memo"`,
340
- )
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
+ }
341
464
 
342
- // Register the sender and receiver as bridge targets on their specific
343
- // chain.
344
- s.RegisterBridgeTarget(s.chainA, transferData.Sender)
345
- s.RegisterBridgeTarget(s.chainB, transferData.Receiver)
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
+ }
346
484
 
347
- 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
+ }
348
491
 
349
- // Initiate the transfer
350
- packet, err := s.TransferFromSourceChain(s.chainA, transferData, path.EndpointA, path.EndpointB)
351
- 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
+ }
352
507
 
353
- // Relay the packet
354
- s.coordinator.CommitBlock(s.chainA)
355
- err = path.EndpointB.UpdateClient()
356
- s.Require().NoError(err)
357
- 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
+ }
358
527
 
359
- writeAcknowledgementHeight := s.chainB.CurrentHeader.Height
360
- 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
+ }
361
549
 
362
- err = path.EndpointB.RecvPacket(packet)
363
- 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
+ }
364
556
 
365
- // Create a success ack as defined by ICS20.
366
- ack := channeltypes.NewResultAcknowledgement([]byte{1})
367
- // Create a different ack to show that a contract can change it.
368
- 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
+ }
369
574
 
370
- s.coordinator.CommitBlock(s.chainA, s.chainB)
575
+ s.mintToAddress(s.chainA, baseSenderAddr, transferData.Denom, transferData.Amount)
371
576
 
372
- {
373
- expectedRecords := []swingsettypes.InboundQueueRecord{}
374
- s.assertActionQueue(s.chainA, expectedRecords)
375
- }
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})
376
624
 
377
- {
378
- expectedRecords := []swingsettypes.InboundQueueRecord{
379
625
  {
380
- Action: &vibckeeper.WriteAcknowledgementEvent{
381
- ActionHeader: &vm.ActionHeader{
382
- Type: "VTRANSFER_IBC_EVENT",
383
- BlockHeight: writeAcknowledgementHeight,
384
- BlockTime: writeAcknowledgementTime,
385
- },
386
- Event: "writeAcknowledgement",
387
- Target: transferData.Receiver,
388
- Packet: packet,
389
- Acknowledgement: ack.Acknowledgement(),
390
- },
391
- Context: swingsettypes.ActionContext{
392
- BlockHeight: writeAcknowledgementHeight,
393
- // TxHash is filled in below
394
- MsgIdx: 0,
395
- },
396
- },
397
- }
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
+ }
398
677
 
399
- s.assertActionQueue(s.chainB, expectedRecords)
678
+ // Send the acks back.
679
+ for pathIdx := hops - 1; pathIdx > 0; pathIdx -= 1 {
680
+ priorPath := paths[pathIdx]
400
681
 
401
- // write out a different acknowledgement from the "contract", one block later.
402
- s.coordinator.CommitBlock(s.chainB)
403
- err = s.GetApp(s.chainB).VtransferKeeper.ReceiveWriteAcknowledgement(s.chainB.GetContext(), packet, contractAck)
404
- s.Require().NoError(err)
682
+ // Update Client
683
+ err = priorPath.EndpointA.UpdateClient()
684
+ s.Require().NoError(err)
405
685
 
406
- s.coordinator.CommitBlock(s.chainB)
407
- }
686
+ // Prove the PFM packet's acknowledgement.
687
+ ackRes, err := acknowledgePacketWithResult(priorPath.EndpointA, ackedPacket, ack.Acknowledgement())
688
+ s.Require().NoError(err)
408
689
 
409
- // Update Client
410
- err = path.EndpointA.UpdateClient()
411
- s.Require().NoError(err)
690
+ ackedPacket, err = ParsePacketFromFilteredEvents(ackRes.GetEvents(), channeltypes.EventTypeWriteAck)
691
+ s.Require().NoError(err)
412
692
 
413
- acknowledgementHeight := s.chainA.CurrentHeader.Height
414
- acknowledgementTime := s.chainA.CurrentHeader.Time.Unix()
693
+ ackData, err := ParseAckFromEvents(ackRes.GetEvents())
694
+ s.Require().NoError(err)
695
+ ack = vibctypes.NewRawAcknowledgement(ackData)
415
696
 
416
- // Prove the packet's acknowledgement.
417
- err = path.EndpointA.AcknowledgePacket(packet, contractAck.Acknowledgement())
418
- s.Require().NoError(err)
697
+ s.coordinator.CommitBlock(priorPath.EndpointA.Chain, priorPath.EndpointB.Chain)
698
+ }
419
699
 
420
- s.coordinator.CommitBlock(s.chainA, s.chainB)
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
+ }
756
+
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)
421
759
 
422
- {
423
- expectedRecords := []swingsettypes.InboundQueueRecord{
424
760
  {
425
- Action: &vibckeeper.WriteAcknowledgementEvent{
426
- ActionHeader: &vm.ActionHeader{
427
- Type: "VTRANSFER_IBC_EVENT",
428
- BlockHeight: acknowledgementHeight,
429
- BlockTime: acknowledgementTime,
430
- },
431
- Event: "acknowledgementPacket",
432
- Target: transferData.Sender,
433
- Packet: packet,
434
- Acknowledgement: contractAck.Acknowledgement(),
435
- Relayer: s.chainA.SenderAccount.GetAddress(),
436
- },
437
- Context: swingsettypes.ActionContext{
438
- BlockHeight: acknowledgementHeight,
439
- // TxHash is filled in below
440
- MsgIdx: 0,
441
- },
442
- },
443
- }
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
+ }
444
790
 
445
- 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
+ })
446
812
  }
447
- })
813
+ }
448
814
  }