Skip to content

Commit f891c29

Browse files
authoredAug 9, 2022
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
1 parent f0f7ce4 commit f891c29

File tree

6 files changed

+96
-4
lines changed

6 files changed

+96
-4
lines changed
 

‎CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
7777

7878
### State Machine Breaking
7979

80+
* (apps/transfer) [\#1907](https://github.com/cosmos/ibc-go/pull/1907) Blocked module account addresses are no longer allowed to send IBC transfers.
8081
* (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.
8182

8283
### Improvements

‎modules/apps/transfer/keeper/msg_server.go

+1-2
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 {
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

+6
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

+11-2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ func (suite *KeeperTestSuite) TestSendTransfer() {
2020
var (
2121
amount sdk.Coin
2222
path *ibctesting.Path
23+
sender sdk.AccAddress
2324
err error
2425
)
2526

@@ -68,7 +69,14 @@ func (suite *KeeperTestSuite) TestSendTransfer() {
6869
amount = sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100))
6970
}, true, false,
7071
},
71-
72+
{
73+
"transfer failed - sender account is blocked",
74+
func() {
75+
suite.coordinator.CreateTransferChannels(path)
76+
amount = sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100))
77+
sender = suite.chainA.GetSimApp().AccountKeeper.GetModuleAddress(types.ModuleName)
78+
}, true, false,
79+
},
7280
// createOutgoingPacket tests
7381
// - source chain
7482
{
@@ -106,6 +114,7 @@ func (suite *KeeperTestSuite) TestSendTransfer() {
106114
suite.SetupTest() // reset
107115
path = NewTransferPath(suite.chainA, suite.chainB)
108116
suite.coordinator.SetupConnections(path)
117+
sender = suite.chainA.SenderAccount.GetAddress()
109118

110119
tc.malleate()
111120

@@ -133,7 +142,7 @@ func (suite *KeeperTestSuite) TestSendTransfer() {
133142

134143
err = suite.chainA.GetSimApp().TransferKeeper.SendTransfer(
135144
suite.chainA.GetContext(), path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, amount,
136-
suite.chainA.SenderAccount.GetAddress(), suite.chainB.SenderAccount.GetAddress().String(), clienttypes.NewHeight(1, 110), 0,
145+
sender, suite.chainB.SenderAccount.GetAddress().String(), suite.chainB.GetTimeoutHeight(), 0,
137146
)
138147

139148
if tc.expPass {

‎testing/chain.go

+6
Original file line numberDiff line numberDiff line change
@@ -593,3 +593,9 @@ func (chain *TestChain) GetChannelCapability(portID, channelID string) *capabili
593593

594594
return cap
595595
}
596+
597+
// GetTimeoutHeight is a convenience function which returns a IBC packet timeout height
598+
// to be used for testing. It returns the current IBC height + 100 blocks
599+
func (chain *TestChain) GetTimeoutHeight() clienttypes.Height {
600+
return clienttypes.NewHeight(clienttypes.ParseChainID(chain.ChainID), uint64(chain.GetContext().BlockHeight())+100)
601+
}

0 commit comments

Comments
 (0)
Please sign in to comment.