Skip to content

Commit ed751e4

Browse files
damiannolanmergify-bot
authored and
mergify-bot
committed
chore: replace error string in transfer acks with const (#818)
* fix: adding ack error string const for transfer * updating godoc * adding warning note to godoc in 04-channel * updating to include abci error code, and copy tests from ica * adding changelog entry (cherry picked from commit ac46ac0) # Conflicts: # CHANGELOG.md # modules/apps/27-interchain-accounts/host/types/ack.go # modules/apps/transfer/ibc_module.go
1 parent 32c935e commit ed751e4

File tree

6 files changed

+474
-0
lines changed

6 files changed

+474
-0
lines changed

CHANGELOG.md

+37
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,44 @@ Ref: https://keepachangelog.com/en/1.0.0/
3434

3535
# Changelog
3636

37+
<<<<<<< HEAD
3738
## [v1.3.0](https://github.com/cosmos/ibc-go/releases/tag/v1.3.0) - 2022-03-01
39+
=======
40+
## [Unreleased]
41+
42+
### Dependencies
43+
44+
* [\#404](https://github.com/cosmos/ibc-go/pull/404) Bump Go version to 1.17
45+
* (core) [\#709](https://github.com/cosmos/ibc-go/pull/709) Replace github.com/pkg/errors with stdlib errors
46+
47+
### API Breaking
48+
49+
* (testing( [\#813](https://github.com/cosmos/ibc-go/pull/813) The `ack` argument to the testing function `RelayPacket` has been removed as it is no longer needed.
50+
* (testing) [\#774](https://github.com/cosmos/ibc-go/pull/774) Added `ChainID` arg to `SetupWithGenesisValSet` on the testing app. `Coordinator` generated ChainIDs now starts at index 1
51+
* (transfer) [\#675](https://github.com/cosmos/ibc-go/pull/675) Transfer `NewKeeper` now takes in an ICS4Wrapper. The ICS4Wrapper may be the IBC Channel Keeper when ICS20 is not used in a middleware stack. The ICS4Wrapper is required for applications wishing to connect middleware to ICS20.
52+
* (core) [\#650](https://github.com/cosmos/ibc-go/pull/650) Modify `OnChanOpenTry` IBC application module callback to return the negotiated app version. The version passed into the `MsgChanOpenTry` has been deprecated and will be ignored by core IBC.
53+
* (core) [\#629](https://github.com/cosmos/ibc-go/pull/629) Removes the `GetProofSpecs` from the ClientState interface. This function was previously unused by core IBC.
54+
* (transfer) [\#517](https://github.com/cosmos/ibc-go/pull/517) Separates the ICS 26 callback functions from `AppModule` into a new type `IBCModule` for ICS 20 transfer.
55+
* (modules/core/02-client) [\#536](https://github.com/cosmos/ibc-go/pull/536) `GetSelfConsensusState` return type changed from bool to error.
56+
* (channel) [\#644](https://github.com/cosmos/ibc-go/pull/644) Removes `CounterpartyHops` function from the ChannelKeeper.
57+
* (testing) [\#776](https://github.com/cosmos/ibc-go/pull/776) Adding helper fn to generate capability name for testing callbacks
58+
59+
60+
### State Machine Breaking
61+
62+
* (transfer) [\#818](https://github.com/cosmos/ibc-go/pull/818) Error acknowledgements returned from Transfer `OnRecvPacket` now include a deterministic ABCI code and error message.
63+
64+
### Improvements
65+
66+
* (testing) [\#810](https://github.com/cosmos/ibc-go/pull/810) Additional testing function added to `Endpoint` type called `RecvPacketWithResult`. Performs the same functionality as the existing `RecvPacket` function but also returns the message result. `path.RelayPacket` no longer uses the provided acknowledgement argument and instead obtains the acknowledgement via MsgRecvPacket events.
67+
* (connection) [\#721](https://github.com/cosmos/ibc-go/pull/721) Simplify connection handshake error messages when unpacking client state.
68+
* (channel) [\#692](https://github.com/cosmos/ibc-go/pull/692) Minimize channel logging by only emitting the packet sequence, source port/channel, destination port/channel upon packet receives, acknowledgements and timeouts.
69+
* [\#383](https://github.com/cosmos/ibc-go/pull/383) Adds helper functions for merging and splitting middleware versions from the underlying app version.
70+
* (modules/core/05-port) [\#288](https://github.com/cosmos/ibc-go/issues/288) Making the 05-port keeper function IsBound public. The IsBound function checks if the provided portID is already binded to a module.
71+
* (channel) [\#644](https://github.com/cosmos/ibc-go/pull/644) Adds `GetChannelConnection` to the ChannelKeeper. This function returns the connectionID and connection state associated with a channel.
72+
* (channel) [\647](https://github.com/cosmos/ibc-go/pull/647) Reorganizes channel handshake handling to set channel state after IBC application callbacks.
73+
* (client) [\#724](https://github.com/cosmos/ibc-go/pull/724) `IsRevisionFormat` and `IsClientIDFormat` have been updated to disallow newlines before the dash used to separate the chainID and revision number, and the client type and client sequence.
74+
>>>>>>> ac46ac0 (chore: replace error string in transfer acks with const (#818))
3875
3976
### Features
4077

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package types
2+
3+
import (
4+
"fmt"
5+
6+
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
7+
8+
channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types"
9+
)
10+
11+
const (
12+
// ackErrorString defines a string constant included in error acknowledgements
13+
// NOTE: Changing this const is state machine breaking as acknowledgements are written into state
14+
ackErrorString = "error handling packet on host chain: see events for details"
15+
)
16+
17+
// NewErrorAcknowledgement returns a deterministic error string which may be used in
18+
// the packet acknowledgement.
19+
func NewErrorAcknowledgement(err error) channeltypes.Acknowledgement {
20+
// the ABCI code is included in the abcitypes.ResponseDeliverTx hash
21+
// constructed in Tendermint and is therefore determinstic
22+
_, code, _ := sdkerrors.ABCIInfo(err, false) // discard non-deterministic codespace and log values
23+
24+
errorString := fmt.Sprintf("ABCI code: %d: %s", code, ackErrorString)
25+
26+
return channeltypes.NewErrorAcknowledgement(errorString)
27+
}

modules/apps/transfer/ibc_module.go

+280
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,280 @@
1+
package transfer
2+
3+
import (
4+
"fmt"
5+
"math"
6+
7+
sdk "github.com/cosmos/cosmos-sdk/types"
8+
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
9+
capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types"
10+
11+
"github.com/cosmos/ibc-go/v3/modules/apps/transfer/keeper"
12+
"github.com/cosmos/ibc-go/v3/modules/apps/transfer/types"
13+
channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types"
14+
porttypes "github.com/cosmos/ibc-go/v3/modules/core/05-port/types"
15+
host "github.com/cosmos/ibc-go/v3/modules/core/24-host"
16+
ibcexported "github.com/cosmos/ibc-go/v3/modules/core/exported"
17+
)
18+
19+
// IBCModule implements the ICS26 interface for transfer given the transfer keeper.
20+
type IBCModule struct {
21+
keeper keeper.Keeper
22+
}
23+
24+
// NewIBCModule creates a new IBCModule given the keeper
25+
func NewIBCModule(k keeper.Keeper) IBCModule {
26+
return IBCModule{
27+
keeper: k,
28+
}
29+
}
30+
31+
// ValidateTransferChannelParams does validation of a newly created transfer channel. A transfer
32+
// channel must be UNORDERED, use the correct port (by default 'transfer'), and use the current
33+
// supported version. Only 2^32 channels are allowed to be created.
34+
func ValidateTransferChannelParams(
35+
ctx sdk.Context,
36+
keeper keeper.Keeper,
37+
order channeltypes.Order,
38+
portID string,
39+
channelID string,
40+
) error {
41+
// NOTE: for escrow address security only 2^32 channels are allowed to be created
42+
// Issue: https://github.com/cosmos/cosmos-sdk/issues/7737
43+
channelSequence, err := channeltypes.ParseChannelSequence(channelID)
44+
if err != nil {
45+
return err
46+
}
47+
if channelSequence > uint64(math.MaxUint32) {
48+
return sdkerrors.Wrapf(types.ErrMaxTransferChannels, "channel sequence %d is greater than max allowed transfer channels %d", channelSequence, uint64(math.MaxUint32))
49+
}
50+
if order != channeltypes.UNORDERED {
51+
return sdkerrors.Wrapf(channeltypes.ErrInvalidChannelOrdering, "expected %s channel, got %s ", channeltypes.UNORDERED, order)
52+
}
53+
54+
// Require portID is the portID transfer module is bound to
55+
boundPort := keeper.GetPort(ctx)
56+
if boundPort != portID {
57+
return sdkerrors.Wrapf(porttypes.ErrInvalidPort, "invalid port: %s, expected %s", portID, boundPort)
58+
}
59+
60+
return nil
61+
}
62+
63+
// OnChanOpenInit implements the IBCModule interface
64+
func (im IBCModule) OnChanOpenInit(
65+
ctx sdk.Context,
66+
order channeltypes.Order,
67+
connectionHops []string,
68+
portID string,
69+
channelID string,
70+
chanCap *capabilitytypes.Capability,
71+
counterparty channeltypes.Counterparty,
72+
version string,
73+
) error {
74+
if err := ValidateTransferChannelParams(ctx, im.keeper, order, portID, channelID); err != nil {
75+
return err
76+
}
77+
78+
if version != types.Version {
79+
return sdkerrors.Wrapf(types.ErrInvalidVersion, "got %s, expected %s", version, types.Version)
80+
}
81+
82+
// Claim channel capability passed back by IBC module
83+
if err := im.keeper.ClaimCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)); err != nil {
84+
return err
85+
}
86+
87+
return nil
88+
}
89+
90+
// OnChanOpenTry implements the IBCModule interface.
91+
func (im IBCModule) OnChanOpenTry(
92+
ctx sdk.Context,
93+
order channeltypes.Order,
94+
connectionHops []string,
95+
portID,
96+
channelID string,
97+
chanCap *capabilitytypes.Capability,
98+
counterparty channeltypes.Counterparty,
99+
counterpartyVersion string,
100+
) (string, error) {
101+
if err := ValidateTransferChannelParams(ctx, im.keeper, order, portID, channelID); err != nil {
102+
return "", err
103+
}
104+
105+
if counterpartyVersion != types.Version {
106+
return "", sdkerrors.Wrapf(types.ErrInvalidVersion, "invalid counterparty version: got: %s, expected %s", counterpartyVersion, types.Version)
107+
}
108+
109+
// Module may have already claimed capability in OnChanOpenInit in the case of crossing hellos
110+
// (ie chainA and chainB both call ChanOpenInit before one of them calls ChanOpenTry)
111+
// If module can already authenticate the capability then module already owns it so we don't need to claim
112+
// Otherwise, module does not have channel capability and we must claim it from IBC
113+
if !im.keeper.AuthenticateCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)) {
114+
// Only claim channel capability passed back by IBC module if we do not already own it
115+
if err := im.keeper.ClaimCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)); err != nil {
116+
return "", err
117+
}
118+
}
119+
120+
return types.Version, nil
121+
}
122+
123+
// OnChanOpenAck implements the IBCModule interface
124+
func (im IBCModule) OnChanOpenAck(
125+
ctx sdk.Context,
126+
portID,
127+
channelID string,
128+
counterpartyVersion string,
129+
) error {
130+
if counterpartyVersion != types.Version {
131+
return sdkerrors.Wrapf(types.ErrInvalidVersion, "invalid counterparty version: %s, expected %s", counterpartyVersion, types.Version)
132+
}
133+
return nil
134+
}
135+
136+
// OnChanOpenConfirm implements the IBCModule interface
137+
func (im IBCModule) OnChanOpenConfirm(
138+
ctx sdk.Context,
139+
portID,
140+
channelID string,
141+
) error {
142+
return nil
143+
}
144+
145+
// OnChanCloseInit implements the IBCModule interface
146+
func (im IBCModule) OnChanCloseInit(
147+
ctx sdk.Context,
148+
portID,
149+
channelID string,
150+
) error {
151+
// Disallow user-initiated channel closing for transfer channels
152+
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "user cannot close channel")
153+
}
154+
155+
// OnChanCloseConfirm implements the IBCModule interface
156+
func (im IBCModule) OnChanCloseConfirm(
157+
ctx sdk.Context,
158+
portID,
159+
channelID string,
160+
) error {
161+
return nil
162+
}
163+
164+
// OnRecvPacket implements the IBCModule interface. A successful acknowledgement
165+
// is returned if the packet data is succesfully decoded and the receive application
166+
// logic returns without error.
167+
func (im IBCModule) OnRecvPacket(
168+
ctx sdk.Context,
169+
packet channeltypes.Packet,
170+
relayer sdk.AccAddress,
171+
) ibcexported.Acknowledgement {
172+
ack := channeltypes.NewResultAcknowledgement([]byte{byte(1)})
173+
174+
var data types.FungibleTokenPacketData
175+
if err := types.ModuleCdc.UnmarshalJSON(packet.GetData(), &data); err != nil {
176+
ack = channeltypes.NewErrorAcknowledgement("cannot unmarshal ICS-20 transfer packet data")
177+
}
178+
179+
// only attempt the application logic if the packet data
180+
// was successfully decoded
181+
if ack.Success() {
182+
err := im.keeper.OnRecvPacket(ctx, packet, data)
183+
if err != nil {
184+
ack = types.NewErrorAcknowledgement(err)
185+
}
186+
}
187+
188+
ctx.EventManager().EmitEvent(
189+
sdk.NewEvent(
190+
types.EventTypePacket,
191+
sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName),
192+
sdk.NewAttribute(types.AttributeKeyReceiver, data.Receiver),
193+
sdk.NewAttribute(types.AttributeKeyDenom, data.Denom),
194+
sdk.NewAttribute(types.AttributeKeyAmount, data.Amount),
195+
sdk.NewAttribute(types.AttributeKeyAckSuccess, fmt.Sprintf("%t", ack.Success())),
196+
),
197+
)
198+
199+
// NOTE: acknowledgement will be written synchronously during IBC handler execution.
200+
return ack
201+
}
202+
203+
// OnAcknowledgementPacket implements the IBCModule interface
204+
func (im IBCModule) OnAcknowledgementPacket(
205+
ctx sdk.Context,
206+
packet channeltypes.Packet,
207+
acknowledgement []byte,
208+
relayer sdk.AccAddress,
209+
) error {
210+
var ack channeltypes.Acknowledgement
211+
if err := types.ModuleCdc.UnmarshalJSON(acknowledgement, &ack); err != nil {
212+
return sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "cannot unmarshal ICS-20 transfer packet acknowledgement: %v", err)
213+
}
214+
var data types.FungibleTokenPacketData
215+
if err := types.ModuleCdc.UnmarshalJSON(packet.GetData(), &data); err != nil {
216+
return sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "cannot unmarshal ICS-20 transfer packet data: %s", err.Error())
217+
}
218+
219+
if err := im.keeper.OnAcknowledgementPacket(ctx, packet, data, ack); err != nil {
220+
return err
221+
}
222+
223+
ctx.EventManager().EmitEvent(
224+
sdk.NewEvent(
225+
types.EventTypePacket,
226+
sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName),
227+
sdk.NewAttribute(types.AttributeKeyReceiver, data.Receiver),
228+
sdk.NewAttribute(types.AttributeKeyDenom, data.Denom),
229+
sdk.NewAttribute(types.AttributeKeyAmount, data.Amount),
230+
sdk.NewAttribute(types.AttributeKeyAck, ack.String()),
231+
),
232+
)
233+
234+
switch resp := ack.Response.(type) {
235+
case *channeltypes.Acknowledgement_Result:
236+
ctx.EventManager().EmitEvent(
237+
sdk.NewEvent(
238+
types.EventTypePacket,
239+
sdk.NewAttribute(types.AttributeKeyAckSuccess, string(resp.Result)),
240+
),
241+
)
242+
case *channeltypes.Acknowledgement_Error:
243+
ctx.EventManager().EmitEvent(
244+
sdk.NewEvent(
245+
types.EventTypePacket,
246+
sdk.NewAttribute(types.AttributeKeyAckError, resp.Error),
247+
),
248+
)
249+
}
250+
251+
return nil
252+
}
253+
254+
// OnTimeoutPacket implements the IBCModule interface
255+
func (im IBCModule) OnTimeoutPacket(
256+
ctx sdk.Context,
257+
packet channeltypes.Packet,
258+
relayer sdk.AccAddress,
259+
) error {
260+
var data types.FungibleTokenPacketData
261+
if err := types.ModuleCdc.UnmarshalJSON(packet.GetData(), &data); err != nil {
262+
return sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "cannot unmarshal ICS-20 transfer packet data: %s", err.Error())
263+
}
264+
// refund tokens
265+
if err := im.keeper.OnTimeoutPacket(ctx, packet, data); err != nil {
266+
return err
267+
}
268+
269+
ctx.EventManager().EmitEvent(
270+
sdk.NewEvent(
271+
types.EventTypeTimeout,
272+
sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName),
273+
sdk.NewAttribute(types.AttributeKeyRefundReceiver, data.Sender),
274+
sdk.NewAttribute(types.AttributeKeyRefundDenom, data.Denom),
275+
sdk.NewAttribute(types.AttributeKeyRefundAmount, data.Amount),
276+
),
277+
)
278+
279+
return nil
280+
}

modules/apps/transfer/types/ack.go

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package types
2+
3+
import (
4+
"fmt"
5+
6+
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
7+
8+
channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types"
9+
)
10+
11+
const (
12+
// ackErrorString defines a string constant included in error acknowledgements
13+
// NOTE: Changing this const is state machine breaking as acknowledgements are written into state
14+
ackErrorString = "error handling packet on destination chain: see events for details"
15+
)
16+
17+
// NewErrorAcknowledgement returns a deterministic error string which may be used in
18+
// the packet acknowledgement.
19+
func NewErrorAcknowledgement(err error) channeltypes.Acknowledgement {
20+
// the ABCI code is included in the abcitypes.ResponseDeliverTx hash
21+
// constructed in Tendermint and is therefore deterministic
22+
_, code, _ := sdkerrors.ABCIInfo(err, false) // discard non-determinstic codespace and log values
23+
24+
errorString := fmt.Sprintf("ABCI code: %d: %s", code, ackErrorString)
25+
26+
return channeltypes.NewErrorAcknowledgement(errorString)
27+
}

0 commit comments

Comments
 (0)