Skip to content
This repository was archived by the owner on Feb 23, 2024. It is now read-only.

Commit 1c2bb22

Browse files
authored
Accept JSON or JSON escaped string for next (#63)
1 parent 88aa25b commit 1c2bb22

File tree

8 files changed

+240
-41
lines changed

8 files changed

+240
-41
lines changed

README.md

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -81,25 +81,40 @@ Utilizing the packet `memo` field, instructions can be encoded as JSON for multi
8181

8282
In the case of a timeout after 10 minutes for either forward, the packet would be retried up to 2 times, at which case an error ack would be written to issue a refund on the prior chain.
8383

84-
`next` is a string since it could be any `memo` for the next hop, so it must be an escaped JSON string.
84+
`next` is the `memo` to pass for the next transfer hop. Per `memo` intended usage of a JSON string, it should be either JSON which will be Marshaled retaining key order, or an escaped JSON string which will be passed directly.
8585

86+
`next` as JSON
8687
```
8788
{
8889
"forward": {
8990
"receiver": "chain-c-bech32-address",
9091
"port": "transfer",
91-
"channel": "channel-123"
92+
"channel": "channel-123",
93+
"timeout": "10m",
94+
"retries": 2,
95+
"next": {
96+
"forward": {
97+
"receiver": "chain-d-bech32-address",
98+
"port": "transfer",
99+
"channel":"channel-234",
100+
"timeout":"10m",
101+
"retries": 2
102+
}
103+
}
104+
}
105+
}
106+
```
107+
108+
`next` as escaped JSON string
109+
```
110+
{
111+
"forward": {
112+
"receiver": "chain-c-bech32-address",
113+
"port": "transfer",
114+
"channel": "channel-123",
92115
"timeout": "10m",
93-
"retries: 2,
94-
"next" : "\{
95-
\"forward\":\{
96-
\"receiver\":\"chain-d-bech32-address\",
97-
\"port\":\"transfer\",
98-
\"channel\":\"channel-234\",
99-
\"timeout\":\"10m\",
100-
\"retries\":2
101-
\}
102-
\}"
116+
"retries": 2,
117+
"next": "{\"forward\":{\"receiver\":\"chain-d-bech32-address\",\"port\":\"transfer\",\"channel\":\"channel-234\",\"timeout\":\"10m\",\"retries\":2}}"
103118
}
104119
}
105120
```

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ require (
1313
github.com/golang/protobuf v1.5.2
1414
github.com/gorilla/mux v1.8.0
1515
github.com/grpc-ecosystem/grpc-gateway v1.16.0
16+
github.com/iancoleman/orderedmap v0.2.0
1617
github.com/spf13/cobra v1.6.1
1718
github.com/stretchr/testify v1.8.1
1819
github.com/tendermint/tendermint v0.34.24

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,8 @@ github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T
300300
github.com/hdevalence/ed25519consensus v0.0.0-20220222234857-c00d1f31bab3 h1:aSVUgRRRtOrZOC1fYmY9gV0e9z/Iu+xNVSASWjsuyGU=
301301
github.com/hdevalence/ed25519consensus v0.0.0-20220222234857-c00d1f31bab3/go.mod h1:5PC6ZNPde8bBqU/ewGZig35+UIZtw9Ytxez8/q5ZyFE=
302302
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
303+
github.com/iancoleman/orderedmap v0.2.0 h1:sq1N/TFpYH++aViPcaKjys3bDClUEU7s5B+z6jq8pNA=
304+
github.com/iancoleman/orderedmap v0.2.0/go.mod h1:N0Wam8K1arqPXNWjMo21EXnBPOPp36vB07FNRdD2geA=
303305
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
304306
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
305307
github.com/improbable-eng/grpc-web v0.15.0 h1:BN+7z6uNXZ1tQGcNAuaU1YjsLTApzkjt2tzCixLaUPQ=

router/keeper/keeper.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package keeper
22

33
import (
4+
"encoding/json"
45
"fmt"
56
"strings"
67
"time"
@@ -230,7 +231,14 @@ func (k *Keeper) ForwardTransferPacket(
230231

231232
// set memo for next transfer with next from this transfer.
232233
if metadata.Next != nil {
233-
memo = *metadata.Next
234+
memoBz, err := json.Marshal(metadata.Next)
235+
if err != nil {
236+
k.Logger(ctx).Error("packetForwardMiddleware error marshaling next as JSON",
237+
"error", err,
238+
)
239+
return sdkerrors.Wrapf(sdkerrors.ErrJSONMarshal, err.Error())
240+
}
241+
memo = string(memoBz)
234242
}
235243

236244
msgTransfer := transfertypes.NewMsgTransfer(
@@ -356,7 +364,9 @@ func (k *Keeper) RetryTimeout(
356364
}
357365

358366
if data.Memo != "" {
359-
metadata.Next = &data.Memo
367+
if err := json.Unmarshal([]byte(data.Memo), metadata.Next); err != nil {
368+
return fmt.Errorf("error unmarshaling memo json: %w", err)
369+
}
360370
}
361371

362372
amount, ok := sdk.NewIntFromString(data.Amount)

router/module.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ func (AppModuleBasic) RegisterRESTRoutes(clientCtx client.Context, rtr *mux.Rout
6262

6363
// RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for the ibc-router module.
6464
func (AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *runtime.ServeMux) {
65-
types.RegisterQueryHandlerClient(context.Background(), mux, types.NewQueryClient(clientCtx))
65+
_ = types.RegisterQueryHandlerClient(context.Background(), mux, types.NewQueryClient(clientCtx))
6666
}
6767

6868
// GetTxCmd implements AppModuleBasic interface

router/module_test.go

Lines changed: 150 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
transfertypes "github.com/cosmos/ibc-go/v6/modules/apps/transfer/types"
1111
channeltypes "github.com/cosmos/ibc-go/v6/modules/core/04-channel/types"
1212
"github.com/golang/mock/gomock"
13+
"github.com/iancoleman/orderedmap"
1314
"github.com/strangelove-ventures/packet-forward-middleware/v6/router/keeper"
1415
"github.com/strangelove-ventures/packet-forward-middleware/v6/router/types"
1516
"github.com/strangelove-ventures/packet-forward-middleware/v6/test"
@@ -35,17 +36,21 @@ func emptyPacket() channeltypes.Packet {
3536
return channeltypes.Packet{}
3637
}
3738

38-
func transferPacket(t *testing.T, receiver string, metadata *types.PacketMetadata) channeltypes.Packet {
39+
func transferPacket(t *testing.T, receiver string, metadata any) channeltypes.Packet {
3940
transferPacket := transfertypes.FungibleTokenPacketData{
4041
Denom: testDenom,
4142
Amount: testAmount,
4243
Receiver: receiver,
4344
}
4445

4546
if metadata != nil {
46-
memo, err := json.Marshal(metadata)
47-
require.NoError(t, err)
48-
transferPacket.Memo = string(memo)
47+
if mStr, ok := metadata.(string); ok {
48+
transferPacket.Memo = mStr
49+
} else {
50+
memo, err := json.Marshal(metadata)
51+
require.NoError(t, err)
52+
transferPacket.Memo = string(memo)
53+
}
4954
}
5055

5156
transferData, err := transfertypes.ModuleCdc.MarshalJSON(&transferPacket)
@@ -171,10 +176,12 @@ func TestOnRecvPacket_ForwardNoFee(t *testing.T) {
171176
forwardMiddleware := setup.ForwardMiddleware
172177

173178
// Test data
174-
hostAddr := "cosmos1vzxkv3lxccnttr9rs0002s93sgw72h7ghukuhs"
175-
destAddr := "cosmos16plylpsgxechajltx9yeseqexzdzut9g8vla4k"
176-
port := "transfer"
177-
channel := "channel-0"
179+
const (
180+
hostAddr = "cosmos1vzxkv3lxccnttr9rs0002s93sgw72h7ghukuhs"
181+
destAddr = "cosmos16plylpsgxechajltx9yeseqexzdzut9g8vla4k"
182+
port = "transfer"
183+
channel = "channel-0"
184+
)
178185
denom := makeIBCDenom(testDestinationPort, testDestinationChannel, testDenom)
179186
senderAccAddr := test.AccAddress()
180187
testCoin := sdk.NewCoin(denom, sdk.NewInt(100))
@@ -235,10 +242,12 @@ func TestOnRecvPacket_ForwardWithFee(t *testing.T) {
235242
setup.Keepers.RouterKeeper.SetParams(ctx, types.NewParams(sdk.NewDecWithPrec(10, 2)))
236243

237244
// Test data
238-
hostAddr := "cosmos1vzxkv3lxccnttr9rs0002s93sgw72h7ghukuhs"
239-
destAddr := "cosmos16plylpsgxechajltx9yeseqexzdzut9g8vla4k"
240-
port := "transfer"
241-
channel := "channel-0"
245+
const (
246+
hostAddr = "cosmos1vzxkv3lxccnttr9rs0002s93sgw72h7ghukuhs"
247+
destAddr = "cosmos16plylpsgxechajltx9yeseqexzdzut9g8vla4k"
248+
port = "transfer"
249+
channel = "channel-0"
250+
)
242251
denom := makeIBCDenom(testDestinationPort, testDestinationChannel, testDenom)
243252
senderAccAddr := test.AccAddress()
244253
hostAccAddr := test.AccAddressFromBech32(t, hostAddr)
@@ -293,7 +302,7 @@ func TestOnRecvPacket_ForwardWithFee(t *testing.T) {
293302
require.NoError(t, err)
294303
}
295304

296-
func TestOnRecvPacket_ForwardMultihop(t *testing.T) {
305+
func TestOnRecvPacket_ForwardMultihopStringNext(t *testing.T) {
297306
var err error
298307
ctl := gomock.NewController(t)
299308
defer ctl.Finish()
@@ -303,12 +312,15 @@ func TestOnRecvPacket_ForwardMultihop(t *testing.T) {
303312
forwardMiddleware := setup.ForwardMiddleware
304313

305314
// Test data
306-
hostAddr := "cosmos1vzxkv3lxccnttr9rs0002s93sgw72h7ghukuhs"
307-
hostAddr2 := "cosmos1q4p4gx889lfek5augdurrjclwtqvjhuntm6j4m"
308-
destAddr := "cosmos16plylpsgxechajltx9yeseqexzdzut9g8vla4k"
309-
port := "transfer"
310-
channel := "channel-0"
311-
channel2 := "channel-1"
315+
const (
316+
hostAddr = "cosmos1vzxkv3lxccnttr9rs0002s93sgw72h7ghukuhs"
317+
hostAddr2 = "cosmos1q4p4gx889lfek5augdurrjclwtqvjhuntm6j4m"
318+
destAddr = "cosmos16plylpsgxechajltx9yeseqexzdzut9g8vla4k"
319+
port = "transfer"
320+
channel = "channel-0"
321+
channel2 = "channel-1"
322+
)
323+
312324
denom := makeIBCDenom(testDestinationPort, testDestinationChannel, testDenom)
313325
senderAccAddr := test.AccAddress()
314326
senderAccAddr2 := test.AccAddress()
@@ -323,14 +335,131 @@ func TestOnRecvPacket_ForwardMultihop(t *testing.T) {
323335
nextBz, err := json.Marshal(nextMetadata)
324336
require.NoError(t, err)
325337

326-
next := string(nextBz)
338+
packetOrig := transferPacket(t, hostAddr, &types.PacketMetadata{
339+
Forward: &types.ForwardMetadata{
340+
Receiver: hostAddr2,
341+
Port: port,
342+
Channel: channel,
343+
Next: types.NewJSONObject(false, nextBz, orderedmap.OrderedMap{}),
344+
},
345+
})
346+
packet2 := transferPacket(t, hostAddr2, nextMetadata)
347+
packetFwd := transferPacket(t, destAddr, nil)
348+
349+
memo1, err := json.Marshal(nextMetadata)
350+
require.NoError(t, err)
351+
352+
msgTransfer1 := transfertypes.NewMsgTransfer(
353+
port,
354+
channel,
355+
testCoin,
356+
hostAddr,
357+
hostAddr2,
358+
keeper.DefaultTransferPacketTimeoutHeight,
359+
uint64(ctx.BlockTime().UnixNano())+uint64(keeper.DefaultForwardTransferPacketTimeoutTimestamp.Nanoseconds()),
360+
string(memo1),
361+
)
362+
363+
// no memo on final forward
364+
msgTransfer2 := transfertypes.NewMsgTransfer(
365+
port,
366+
channel2,
367+
testCoin,
368+
hostAddr2,
369+
destAddr,
370+
keeper.DefaultTransferPacketTimeoutHeight,
371+
uint64(ctx.BlockTime().UnixNano())+uint64(keeper.DefaultForwardTransferPacketTimeoutTimestamp.Nanoseconds()),
372+
"",
373+
)
374+
375+
acknowledgement := channeltypes.NewResultAcknowledgement([]byte("test"))
376+
successAck := cdc.MustMarshalJSON(&acknowledgement)
377+
378+
// Expected mocks
379+
gomock.InOrder(
380+
setup.Mocks.IBCModuleMock.EXPECT().OnRecvPacket(ctx, packetOrig, senderAccAddr).
381+
Return(acknowledgement),
382+
383+
setup.Mocks.TransferKeeperMock.EXPECT().Transfer(
384+
sdk.WrapSDKContext(ctx),
385+
msgTransfer1,
386+
).Return(&apptypes.MsgTransferResponse{Sequence: 0}, nil),
387+
388+
setup.Mocks.IBCModuleMock.EXPECT().OnRecvPacket(ctx, packet2, senderAccAddr2).
389+
Return(acknowledgement),
390+
391+
setup.Mocks.TransferKeeperMock.EXPECT().Transfer(
392+
sdk.WrapSDKContext(ctx),
393+
msgTransfer2,
394+
).Return(&apptypes.MsgTransferResponse{Sequence: 0}, nil),
395+
396+
setup.Mocks.IBCModuleMock.EXPECT().OnAcknowledgementPacket(ctx, packetFwd, successAck, senderAccAddr2).
397+
Return(nil),
398+
399+
setup.Mocks.IBCModuleMock.EXPECT().OnAcknowledgementPacket(ctx, packet2, successAck, senderAccAddr).
400+
Return(nil),
401+
)
402+
403+
// chain B with router module receives packet and forwards. ack should be nil so that it is not written yet.
404+
ack := forwardMiddleware.OnRecvPacket(ctx, packetOrig, senderAccAddr)
405+
require.Nil(t, ack)
406+
407+
// chain C with router module receives packet and forwards. ack should be nil so that it is not written yet.
408+
ack = forwardMiddleware.OnRecvPacket(ctx, packet2, senderAccAddr2)
409+
require.Nil(t, ack)
410+
411+
// ack returned from chain D to chain C
412+
err = forwardMiddleware.OnAcknowledgementPacket(ctx, packetFwd, successAck, senderAccAddr2)
413+
require.NoError(t, err)
414+
415+
// ack returned from chain C to chain B
416+
err = forwardMiddleware.OnAcknowledgementPacket(ctx, packet2, successAck, senderAccAddr)
417+
require.NoError(t, err)
418+
}
419+
420+
func TestOnRecvPacket_ForwardMultihopJSONNext(t *testing.T) {
421+
var err error
422+
ctl := gomock.NewController(t)
423+
defer ctl.Finish()
424+
setup := test.NewTestSetup(t, ctl)
425+
ctx := setup.Initializer.Ctx
426+
cdc := setup.Initializer.Marshaler
427+
forwardMiddleware := setup.ForwardMiddleware
428+
429+
// Test data
430+
const (
431+
hostAddr = "cosmos1vzxkv3lxccnttr9rs0002s93sgw72h7ghukuhs"
432+
hostAddr2 = "cosmos1q4p4gx889lfek5augdurrjclwtqvjhuntm6j4m"
433+
destAddr = "cosmos16plylpsgxechajltx9yeseqexzdzut9g8vla4k"
434+
port = "transfer"
435+
channel = "channel-0"
436+
channel2 = "channel-1"
437+
)
438+
439+
denom := makeIBCDenom(testDestinationPort, testDestinationChannel, testDenom)
440+
senderAccAddr := test.AccAddress()
441+
senderAccAddr2 := test.AccAddress()
442+
testCoin := sdk.NewCoin(denom, sdk.NewInt(100))
443+
nextMetadata := &types.PacketMetadata{
444+
Forward: &types.ForwardMetadata{
445+
Receiver: destAddr,
446+
Port: port,
447+
Channel: channel2,
448+
},
449+
}
450+
nextBz, err := json.Marshal(nextMetadata)
451+
require.NoError(t, err)
452+
453+
nextJSONObject := new(types.JSONObject)
454+
err = json.Unmarshal(nextBz, nextJSONObject)
455+
require.NoError(t, err)
327456

328457
packetOrig := transferPacket(t, hostAddr, &types.PacketMetadata{
329458
Forward: &types.ForwardMetadata{
330459
Receiver: hostAddr2,
331460
Port: port,
332461
Channel: channel,
333-
Next: &next,
462+
Next: nextJSONObject,
334463
},
335464
})
336465
packet2 := transferPacket(t, hostAddr2, nextMetadata)

0 commit comments

Comments
 (0)