@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.
- package/CHANGELOG.md +14 -0
- package/app/app.go +1 -1
- package/app/upgrade.go +4 -190
- package/git-revision.txt +1 -1
- package/go.mod +82 -71
- package/go.sum +200 -167
- package/package.json +2 -2
- package/types/address_hooks.go +48 -19
- package/types/address_hooks_test.go +7 -4
- package/x/swingset/testing/queue.go +8 -0
- package/x/vibc/keeper/keeper.go +2 -5
- package/x/vibc/keeper/triggers.go +3 -6
- package/x/vibc/types/receiver.go +11 -5
- package/x/vstorage/testing/queue.go +6 -0
- package/x/vtransfer/ibc_middleware.go +5 -1
- package/x/vtransfer/ibc_middleware_test.go +510 -148
- package/x/vtransfer/keeper/keeper.go +190 -27
- 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,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
|
-
|
|
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
|
-
|
|
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 <
|
|
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
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
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.
|
|
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(
|
|
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 :=
|
|
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:
|
|
345
|
+
Data: string(bz),
|
|
270
346
|
},
|
|
271
347
|
&reply,
|
|
272
348
|
)
|
|
273
349
|
s.Require().NoError(err)
|
|
274
|
-
s.Require().Equal(
|
|
350
|
+
s.Require().Equal("true", reply)
|
|
275
351
|
}
|
|
276
352
|
|
|
277
|
-
func (s *IntegrationTestSuite)
|
|
278
|
-
|
|
353
|
+
func (s *IntegrationTestSuite) TransferFromEndpoint(
|
|
354
|
+
srcContext sdk.Context,
|
|
355
|
+
src *ibctesting.Endpoint,
|
|
279
356
|
data ibctransfertypes.FungibleTokenPacketData,
|
|
280
|
-
|
|
281
|
-
) (channeltypes.Packet, error) {
|
|
357
|
+
) error {
|
|
282
358
|
tokenAmt, ok := sdk.NewIntFromString(data.Amount)
|
|
283
359
|
s.Require().True(ok)
|
|
284
360
|
|
|
285
|
-
timeoutHeight :=
|
|
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.
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
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,
|
|
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
|
-
//
|
|
324
|
-
//
|
|
325
|
-
//
|
|
326
|
-
//
|
|
327
|
-
//
|
|
328
|
-
//
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
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
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
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
|
-
|
|
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
|
-
|
|
354
|
-
|
|
355
|
-
|
|
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
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
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
|
-
|
|
364
|
-
|
|
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
|
-
|
|
367
|
-
|
|
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
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
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
|
-
|
|
575
|
+
s.mintToAddress(s.chainA, baseSenderAddr, transferData.Denom, transferData.Amount)
|
|
375
576
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
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
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
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
|
-
|
|
678
|
+
// Send the acks back.
|
|
679
|
+
for pathIdx := hops - 1; pathIdx > 0; pathIdx -= 1 {
|
|
680
|
+
priorPath := paths[pathIdx]
|
|
404
681
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
s.Require().NoError(err)
|
|
682
|
+
// Update Client
|
|
683
|
+
err = priorPath.EndpointA.UpdateClient()
|
|
684
|
+
s.Require().NoError(err)
|
|
409
685
|
|
|
410
|
-
|
|
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
|
-
|
|
414
|
-
|
|
415
|
-
s.Require().NoError(err)
|
|
690
|
+
ackedPacket, err = ParsePacketFromFilteredEvents(ackRes.GetEvents(), channeltypes.EventTypeWriteAck)
|
|
691
|
+
s.Require().NoError(err)
|
|
416
692
|
|
|
417
|
-
|
|
418
|
-
|
|
693
|
+
ackData, err := ParseAckFromEvents(ackRes.GetEvents())
|
|
694
|
+
s.Require().NoError(err)
|
|
695
|
+
ack = vibctypes.NewRawAcknowledgement(ackData)
|
|
419
696
|
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
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
|
-
|
|
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
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
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
|
-
|
|
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
|
}
|