Skip to content

Commit 879a703

Browse files
colin-axnermergify[bot]
authored andcommitted
fix: prevent blocked addresses from sending ICS 20 transfers (#1907)
* fix bug, add test Ensures that a sender account isn't a blocked address Added test cases for MsgTransfer handling * update documentation * move blocked address check to SendTransfer * add changelog entry (cherry picked from commit f891c29) # Conflicts: # modules/apps/transfer/keeper/relay_test.go
1 parent 3e43129 commit 879a703

File tree

6 files changed

+99
-3
lines changed

6 files changed

+99
-3
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

6161
### State Machine Breaking
6262

63+
* (apps/transfer) [\#1907](https://github.com/cosmos/ibc-go/pull/1907) Blocked module account addresses are no longer allowed to send IBC transfers.
6364
* (apps/27-interchain-accounts) [\#1882](https://github.com/cosmos/ibc-go/pull/1882) Explicitly check length of interchain account packet data in favour of nil check.
6465

6566
### Improvements

modules/apps/transfer/keeper/msg_server.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@ import (
1010

1111
var _ types.MsgServer = Keeper{}
1212

13-
// See createOutgoingPacket in spec:https://github.com/cosmos/ibc/tree/master/spec/app/ics-020-fungible-token-transfer#packet-relay
14-
1513
// Transfer defines a rpc handler method for MsgTransfer.
1614
func (k Keeper) Transfer(goCtx context.Context, msg *types.MsgTransfer) (*types.MsgTransferResponse, error) {
1715
ctx := sdk.UnwrapSDKContext(goCtx)
@@ -20,6 +18,7 @@ func (k Keeper) Transfer(goCtx context.Context, msg *types.MsgTransfer) (*types.
2018
if err != nil {
2119
return nil, err
2220
}
21+
2322
if err := k.SendTransfer(
2423
ctx, msg.SourcePort, msg.SourceChannel, msg.Token, sender, msg.Receiver, msg.TimeoutHeight, msg.TimeoutTimestamp,
2524
); err != nil {
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package keeper_test
2+
3+
import (
4+
sdk "github.com/cosmos/cosmos-sdk/types"
5+
6+
"github.com/cosmos/ibc-go/v5/modules/apps/transfer/types"
7+
)
8+
9+
func (suite *KeeperTestSuite) TestMsgTransfer() {
10+
var msg *types.MsgTransfer
11+
12+
testCases := []struct {
13+
name string
14+
malleate func()
15+
expPass bool
16+
}{
17+
{
18+
"success",
19+
func() {},
20+
true,
21+
},
22+
{
23+
"invalid sender",
24+
func() {
25+
msg.Sender = "address"
26+
},
27+
false,
28+
},
29+
{
30+
"sender is a blocked address",
31+
func() {
32+
msg.Sender = suite.chainA.GetSimApp().AccountKeeper.GetModuleAddress(types.ModuleName).String()
33+
},
34+
false,
35+
},
36+
{
37+
"channel does not exist",
38+
func() {
39+
msg.SourceChannel = "channel-100"
40+
},
41+
false,
42+
},
43+
}
44+
45+
for _, tc := range testCases {
46+
suite.SetupTest()
47+
48+
path := NewTransferPath(suite.chainA, suite.chainB)
49+
suite.coordinator.Setup(path)
50+
51+
coin := sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100))
52+
msg = types.NewMsgTransfer(
53+
path.EndpointA.ChannelConfig.PortID,
54+
path.EndpointA.ChannelID,
55+
coin, suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(),
56+
suite.chainB.GetTimeoutHeight(), 0, // only use timeout height
57+
)
58+
59+
tc.malleate()
60+
61+
res, err := suite.chainA.GetSimApp().TransferKeeper.Transfer(sdk.WrapSDKContext(suite.chainA.GetContext()), msg)
62+
63+
if tc.expPass {
64+
suite.Require().NoError(err)
65+
suite.Require().NotNil(res)
66+
} else {
67+
suite.Require().Error(err)
68+
suite.Require().Nil(res)
69+
}
70+
}
71+
}

modules/apps/transfer/keeper/relay.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ import (
4848
// 4. A -> C : sender chain is sink zone. Denom upon receiving: 'C/B/denom'
4949
// 5. C -> B : sender chain is sink zone. Denom upon receiving: 'B/denom'
5050
// 6. B -> A : sender chain is sink zone. Denom upon receiving: 'denom'
51+
//
52+
// Note: An IBC Transfer must be initiated using a MsgTransfer via the Transfer rpc handler
5153
func (k Keeper) SendTransfer(
5254
ctx sdk.Context,
5355
sourcePort,
@@ -62,6 +64,10 @@ func (k Keeper) SendTransfer(
6264
return types.ErrSendDisabled
6365
}
6466

67+
if k.bankKeeper.BlockedAddr(sender) {
68+
return sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "%s is not allowed to send funds", sender)
69+
}
70+
6571
sourceChannelEnd, found := k.channelKeeper.GetChannel(ctx, sourcePort, sourceChannel)
6672
if !found {
6773
return sdkerrors.Wrapf(channeltypes.ErrChannelNotFound, "port ID (%s) channel ID (%s)", sourcePort, sourceChannel)

modules/apps/transfer/keeper/relay_test.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ func (suite *KeeperTestSuite) TestSendTransfer() {
1919
var (
2020
amount sdk.Coin
2121
path *ibctesting.Path
22+
sender sdk.AccAddress
2223
err error
2324
)
2425

@@ -67,7 +68,14 @@ func (suite *KeeperTestSuite) TestSendTransfer() {
6768
amount = sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100))
6869
}, true, false,
6970
},
70-
71+
{
72+
"transfer failed - sender account is blocked",
73+
func() {
74+
suite.coordinator.CreateTransferChannels(path)
75+
amount = sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100))
76+
sender = suite.chainA.GetSimApp().AccountKeeper.GetModuleAddress(types.ModuleName)
77+
}, true, false,
78+
},
7179
// createOutgoingPacket tests
7280
// - source chain
7381
{
@@ -105,6 +113,7 @@ func (suite *KeeperTestSuite) TestSendTransfer() {
105113
suite.SetupTest() // reset
106114
path = NewTransferPath(suite.chainA, suite.chainB)
107115
suite.coordinator.SetupConnections(path)
116+
sender = suite.chainA.SenderAccount.GetAddress()
108117

109118
tc.malleate()
110119

@@ -132,7 +141,11 @@ func (suite *KeeperTestSuite) TestSendTransfer() {
132141

133142
err = suite.chainA.GetSimApp().TransferKeeper.SendTransfer(
134143
suite.chainA.GetContext(), path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, amount,
144+
<<<<<<< HEAD
135145
suite.chainA.SenderAccount.GetAddress(), suite.chainB.SenderAccount.GetAddress().String(), clienttypes.NewHeight(0, 110), 0,
146+
=======
147+
sender, suite.chainB.SenderAccount.GetAddress().String(), suite.chainB.GetTimeoutHeight(), 0,
148+
>>>>>>> f891c29 (fix: prevent blocked addresses from sending ICS 20 transfers (#1907))
136149
)
137150

138151
if tc.expPass {

testing/chain.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -572,3 +572,9 @@ func (chain *TestChain) GetChannelCapability(portID, channelID string) *capabili
572572

573573
return cap
574574
}
575+
576+
// GetTimeoutHeight is a convenience function which returns a IBC packet timeout height
577+
// to be used for testing. It returns the current IBC height + 100 blocks
578+
func (chain *TestChain) GetTimeoutHeight() clienttypes.Height {
579+
return clienttypes.NewHeight(clienttypes.ParseChainID(chain.ChainID), uint64(chain.GetContext().BlockHeight())+100)
580+
}

0 commit comments

Comments
 (0)