Skip to content

Commit 0a22b7a

Browse files
imp: allow memo strings instead of keys for transfer authorizations (cosmos#6268)
* imp: allow memo strings instead of keys for transfer authorizations * add changelog * handle error from compact * return error * improve test * not enforce that memo strings of allowed packet data must be JSON-encoded strings * use slices contains to check if memo is allowed * Update modules/apps/transfer/types/transfer_authorization.go Co-authored-by: srdtrk <[email protected]> * Update modules/apps/transfer/types/transfer_authorization.go Co-authored-by: srdtrk <[email protected]> * lint --------- Co-authored-by: srdtrk <[email protected]>
1 parent 5279120 commit 0a22b7a

File tree

7 files changed

+39
-44
lines changed

7 files changed

+39
-44
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
6060
* (apps/27-interchain-accounts) [\#5533](https://github.com/cosmos/ibc-go/pull/5533) ICA host sets the host connection ID on `OnChanOpenTry`, so that ICA controller implementations are not obliged to set the value on `OnChanOpenInit` if they are not able.
6161
* (core/02-client, core/03-connection, apps/27-interchain-accounts) [\#6256](https://github.com/cosmos/ibc-go/pull/6256) Add length checking of array fields in messages.
6262
* (apps/27-interchain-accounts, apps/tranfer, apps/29-fee) [\#6253](https://github.com/cosmos/ibc-go/pull/6253) Allow channel handshake to succeed if fee middleware is wired up on one side, but not the other.
63+
* (apps/transfer) [\#6268](https://github.com/cosmos/ibc-go/pull/6268) Use memo strings instead of JSON keys in `AllowedPacketData` of transfer authorization.
6364

6465
### Features
6566

docs/docs/02-apps/01-transfer/08-authorizations.md

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ It takes:
2222

2323
- an `AllowList` list that specifies the list of addresses that are allowed to receive funds. If this list is empty, then all addresses are allowed to receive funds from the `TransferAuthorization`.
2424

25-
- an `AllowedPacketData` list that specifies the list of memo packet data keys that are allowed to send the packet. If this list is empty, then only an empty memo is allowed (a `memo` field with non-empty content will be denied). If this list includes a single element equal to `"*"`, then any content in `memo` field will be allowed.
25+
- an `AllowedPacketData` list that specifies the list of memo strings that are allowed to be included in the memo field of the packet. If this list is empty, then only an empty memo is allowed (a `memo` field with non-empty content will be denied). If this list includes a single element equal to `"*"`, then any content in `memo` field will be allowed.
2626

2727
Setting a `TransferAuthorization` is expected to fail if:
2828

@@ -51,9 +51,8 @@ type Allocation struct {
5151
SpendLimit sdk.Coins
5252
// allow list of receivers, an empty allow list permits any receiver address
5353
AllowList []string
54-
// allow list of packet data keys, an empty list prohibits all packet data keys;
55-
// a list only with "*" permits any packet data key
54+
// allow list of memo strings, an empty list prohibits all memo strings;
55+
// a list only with "*" permits any memo string
5656
AllowedPacketData []string
5757
}
58-
5958
```

modules/apps/transfer/types/authz.pb.go

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

modules/apps/transfer/types/keys.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ const (
3030
// DenomPrefix is the prefix used for internal SDK coin representation.
3131
DenomPrefix = "ibc"
3232

33-
// AllowAllPacketDataKeys holds the string key that allows all packet data keys in authz transfer messages
33+
// AllowAllPacketDataKeys holds the string key that allows all memo strings in authz transfer messages
3434
AllowAllPacketDataKeys = "*"
3535

3636
KeyTotalEscrowPrefix = "totalEscrowForDenom"

modules/apps/transfer/types/transfer_authorization.go

Lines changed: 10 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,8 @@ package types
22

33
import (
44
"context"
5-
"encoding/json"
65
"math/big"
7-
"sort"
6+
"slices"
87
"strings"
98

109
"github.com/cosmos/gogoproto/proto"
@@ -155,9 +154,9 @@ func isAllowedAddress(ctx sdk.Context, receiver string, allowedAddrs []string) b
155154
}
156155

157156
// validateMemo returns a nil error indicating if the memo is valid for transfer.
158-
func validateMemo(ctx sdk.Context, memo string, allowedPacketDataList []string) error {
157+
func validateMemo(ctx sdk.Context, memo string, allowedMemos []string) error {
159158
// if the allow list is empty, then the memo must be an empty string
160-
if len(allowedPacketDataList) == 0 {
159+
if len(allowedMemos) == 0 {
161160
if len(strings.TrimSpace(memo)) != 0 {
162161
return errorsmod.Wrapf(ErrInvalidAuthorization, "memo must be empty because allowed packet data in allocation is empty")
163162
}
@@ -166,36 +165,20 @@ func validateMemo(ctx sdk.Context, memo string, allowedPacketDataList []string)
166165
}
167166

168167
// if allowedPacketDataList has only 1 element and it equals AllowAllPacketDataKeys
169-
// then accept all the packet data keys
170-
if len(allowedPacketDataList) == 1 && allowedPacketDataList[0] == AllowAllPacketDataKeys {
168+
// then accept all the memo strings
169+
if len(allowedMemos) == 1 && allowedMemos[0] == AllowAllPacketDataKeys {
171170
return nil
172171
}
173172

174-
jsonObject := make(map[string]interface{})
175-
err := json.Unmarshal([]byte(memo), &jsonObject)
176-
if err != nil {
177-
return err
178-
}
179-
180173
gasCostPerIteration := ctx.KVGasConfig().IterNextCostFlat
181-
182-
for _, key := range allowedPacketDataList {
174+
isMemoAllowed := slices.ContainsFunc(allowedMemos, func(allowedMemo string) bool {
183175
ctx.GasMeter().ConsumeGas(gasCostPerIteration, "transfer authorization")
184176

185-
_, ok := jsonObject[key]
186-
if ok {
187-
delete(jsonObject, key)
188-
}
189-
}
190-
191-
keys := make([]string, 0, len(jsonObject))
192-
for k := range jsonObject {
193-
keys = append(keys, k)
194-
}
195-
sort.Strings(keys)
177+
return strings.TrimSpace(memo) == strings.TrimSpace(allowedMemo)
178+
})
196179

197-
if len(jsonObject) != 0 {
198-
return errorsmod.Wrapf(ErrInvalidAuthorization, "not allowed packet data keys: %s", keys)
180+
if !isMemoAllowed {
181+
return errorsmod.Wrapf(ErrInvalidAuthorization, "not allowed memo: %s", memo)
199182
}
200183

201184
return nil

modules/apps/transfer/types/transfer_authorization_test.go

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package types_test
22

33
import (
4+
"fmt"
5+
46
sdkmath "cosmossdk.io/math"
57

68
sdk "github.com/cosmos/cosmos-sdk/types"
@@ -11,7 +13,10 @@ import (
1113
"github.com/cosmos/ibc-go/v8/testing/mock"
1214
)
1315

14-
const testMemo = `{"wasm":{"contract":"osmo1c3ljch9dfw5kf52nfwpxd2zmj2ese7agnx0p9tenkrryasrle5sqf3ftpg","msg":{"osmosis_swap":{"output_denom":"uosmo","slippage":{"twap":{"slippage_percentage":"20","window_seconds":10}},"receiver":"feeabs/feeabs1efd63aw40lxf3n4mhf7dzhjkr453axurwrhrrw","on_failed_delivery":"do_nothing"}}}}`
16+
const (
17+
testMemo1 = `{"wasm":{"contract":"osmo1c3ljch9dfw5kf52nfwpxd2zmj2ese7agnx0p9tenkrryasrle5sqf3ftpg","msg":{"osmosis_swap":{"output_denom":"uosmo","slippage":{"twap":{"slippage_percentage":"20","window_seconds":10}},"receiver":"feeabs/feeabs1efd63aw40lxf3n4mhf7dzhjkr453axurwrhrrw","on_failed_delivery":"do_nothing"}}}}`
18+
testMemo2 = `{"forward":{"channel":"channel-11","port":"transfer","receiver":"stars1twfv52yxcyykx2lcvgl42svw46hsm5dd4ww6xy","retries":2,"timeout":1712146014542131200}}`
19+
)
1520

1621
func (suite *TypesTestSuite) TestTransferAuthorizationAccept() {
1722
var (
@@ -122,7 +127,7 @@ func (suite *TypesTestSuite) TestTransferAuthorizationAccept() {
122127
func() {
123128
allowedList := []string{"*"}
124129
transferAuthz.Allocations[0].AllowedPacketData = allowedList
125-
msgTransfer.Memo = testMemo
130+
msgTransfer.Memo = testMemo1
126131
},
127132
func(res authz.AcceptResponse, err error) {
128133
suite.Require().NoError(err)
@@ -135,9 +140,9 @@ func (suite *TypesTestSuite) TestTransferAuthorizationAccept() {
135140
{
136141
"success: transfer memo allowed",
137142
func() {
138-
allowedList := []string{"wasm", "forward"}
143+
allowedList := []string{testMemo1, testMemo2}
139144
transferAuthz.Allocations[0].AllowedPacketData = allowedList
140-
msgTransfer.Memo = testMemo
145+
msgTransfer.Memo = testMemo1
141146
},
142147
func(res authz.AcceptResponse, err error) {
143148
suite.Require().NoError(err)
@@ -152,7 +157,7 @@ func (suite *TypesTestSuite) TestTransferAuthorizationAccept() {
152157
func() {
153158
allowedList := []string{}
154159
transferAuthz.Allocations[0].AllowedPacketData = allowedList
155-
msgTransfer.Memo = testMemo
160+
msgTransfer.Memo = testMemo1
156161
},
157162
func(res authz.AcceptResponse, err error) {
158163
suite.Require().Error(err)
@@ -161,13 +166,13 @@ func (suite *TypesTestSuite) TestTransferAuthorizationAccept() {
161166
{
162167
"memo not allowed",
163168
func() {
164-
allowedList := []string{"forward"}
169+
allowedList := []string{testMemo1}
165170
transferAuthz.Allocations[0].AllowedPacketData = allowedList
166-
msgTransfer.Memo = testMemo
171+
msgTransfer.Memo = testMemo2
167172
},
168173
func(res authz.AcceptResponse, err error) {
169174
suite.Require().Error(err)
170-
suite.Require().ErrorContains(err, "not allowed packet data keys: [wasm]")
175+
suite.Require().ErrorContains(err, fmt.Sprintf("not allowed memo: %s", testMemo2))
171176
},
172177
},
173178
{
@@ -310,6 +315,13 @@ func (suite *TypesTestSuite) TestTransferAuthorizationValidateBasic() {
310315
},
311316
true,
312317
},
318+
{
319+
"success: wildcard allowed packet data",
320+
func() {
321+
transferAuthz.Allocations[0].AllowedPacketData = []string{"*"}
322+
},
323+
true,
324+
},
313325
{
314326
"empty allocations",
315327
func() {

proto/ibc/applications/transfer/v1/authz.proto

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ message Allocation {
1919
[(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"];
2020
// allow list of receivers, an empty allow list permits any receiver address
2121
repeated string allow_list = 4;
22-
// allow list of packet data keys, an empty list prohibits all packet data keys;
23-
// a list only with "*" permits any packet data key
22+
// allow list of memo strings, an empty list prohibits all memo strings;
23+
// a list only with "*" permits any memo string
2424
repeated string allowed_packet_data = 5;
2525
}
2626

0 commit comments

Comments
 (0)