Skip to content

Commit 3a247fc

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)
1 parent 60fdc75 commit 3a247fc

File tree

6 files changed

+135
-3
lines changed

6 files changed

+135
-3
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ Ref: https://keepachangelog.com/en/1.0.0/
5656

5757
### State Machine Breaking
5858

59+
* (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.
60+
5961
### Improvements
6062

6163
* (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.

modules/apps/27-interchain-accounts/host/types/ack.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,12 @@ const (
1414
ackErrorString = "error handling packet on host chain: see events for details"
1515
)
1616

17-
// AcknowledgementErrorString returns a deterministic error string which may be used in
17+
// NewErrorAcknowledgement returns a deterministic error string which may be used in
1818
// the packet acknowledgement.
1919
func NewErrorAcknowledgement(err error) channeltypes.Acknowledgement {
2020
// the ABCI code is included in the abcitypes.ResponseDeliverTx hash
2121
// constructed in Tendermint and is therefore determinstic
22-
_, code, _ := sdkerrors.ABCIInfo(err, false) // discard non-determinstic codespace and log values
22+
_, code, _ := sdkerrors.ABCIInfo(err, false) // discard non-deterministic codespace and log values
2323

2424
errorString := fmt.Sprintf("ABCI code: %d: %s", code, ackErrorString)
2525

modules/apps/transfer/ibc_module.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ func (im IBCModule) OnRecvPacket(
181181
if ack.Success() {
182182
err := im.keeper.OnRecvPacket(ctx, packet, data)
183183
if err != nil {
184-
ack = channeltypes.NewErrorAcknowledgement(err.Error())
184+
ack = types.NewErrorAcknowledgement(err)
185185
}
186186
}
187187

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+
}
+101
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
package types_test
2+
3+
import (
4+
"testing"
5+
6+
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
7+
"github.com/stretchr/testify/suite"
8+
abcitypes "github.com/tendermint/tendermint/abci/types"
9+
tmprotostate "github.com/tendermint/tendermint/proto/tendermint/state"
10+
tmstate "github.com/tendermint/tendermint/state"
11+
12+
"github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/host/types"
13+
ibctesting "github.com/cosmos/ibc-go/v3/testing"
14+
)
15+
16+
const (
17+
gasUsed = uint64(100)
18+
gasWanted = uint64(100)
19+
)
20+
21+
type TypesTestSuite struct {
22+
suite.Suite
23+
24+
coordinator *ibctesting.Coordinator
25+
26+
chainA *ibctesting.TestChain
27+
chainB *ibctesting.TestChain
28+
}
29+
30+
func (suite *TypesTestSuite) SetupTest() {
31+
suite.coordinator = ibctesting.NewCoordinator(suite.T(), 2)
32+
33+
suite.chainA = suite.coordinator.GetChain(ibctesting.GetChainID(1))
34+
suite.chainB = suite.coordinator.GetChain(ibctesting.GetChainID(2))
35+
}
36+
37+
func TestTypesTestSuite(t *testing.T) {
38+
suite.Run(t, new(TypesTestSuite))
39+
}
40+
41+
// The safety of including ABCI error codes in the acknowledgement rests
42+
// on the inclusion of these ABCI error codes in the abcitypes.ResposneDeliverTx
43+
// hash. If the ABCI codes get removed from consensus they must no longer be used
44+
// in the packet acknowledgement.
45+
//
46+
// This test acts as an indicator that the ABCI error codes may no longer be deterministic.
47+
func (suite *TypesTestSuite) TestABCICodeDeterminism() {
48+
// same ABCI error code used
49+
err := sdkerrors.Wrap(sdkerrors.ErrOutOfGas, "error string 1")
50+
errSameABCICode := sdkerrors.Wrap(sdkerrors.ErrOutOfGas, "error string 2")
51+
52+
// different ABCI error code used
53+
errDifferentABCICode := sdkerrors.ErrNotFound
54+
55+
deliverTx := sdkerrors.ResponseDeliverTx(err, gasUsed, gasWanted, false)
56+
responses := tmprotostate.ABCIResponses{
57+
DeliverTxs: []*abcitypes.ResponseDeliverTx{
58+
&deliverTx,
59+
},
60+
}
61+
62+
deliverTxSameABCICode := sdkerrors.ResponseDeliverTx(errSameABCICode, gasUsed, gasWanted, false)
63+
responsesSameABCICode := tmprotostate.ABCIResponses{
64+
DeliverTxs: []*abcitypes.ResponseDeliverTx{
65+
&deliverTxSameABCICode,
66+
},
67+
}
68+
69+
deliverTxDifferentABCICode := sdkerrors.ResponseDeliverTx(errDifferentABCICode, gasUsed, gasWanted, false)
70+
responsesDifferentABCICode := tmprotostate.ABCIResponses{
71+
DeliverTxs: []*abcitypes.ResponseDeliverTx{
72+
&deliverTxDifferentABCICode,
73+
},
74+
}
75+
76+
hash := tmstate.ABCIResponsesResultsHash(&responses)
77+
hashSameABCICode := tmstate.ABCIResponsesResultsHash(&responsesSameABCICode)
78+
hashDifferentABCICode := tmstate.ABCIResponsesResultsHash(&responsesDifferentABCICode)
79+
80+
suite.Require().Equal(hash, hashSameABCICode)
81+
suite.Require().NotEqual(hash, hashDifferentABCICode)
82+
}
83+
84+
// TestAcknowledgementError will verify that only a constant string and
85+
// ABCI error code are used in constructing the acknowledgement error string
86+
func (suite *TypesTestSuite) TestAcknowledgementError() {
87+
// same ABCI error code used
88+
err := sdkerrors.Wrap(sdkerrors.ErrOutOfGas, "error string 1")
89+
errSameABCICode := sdkerrors.Wrap(sdkerrors.ErrOutOfGas, "error string 2")
90+
91+
// different ABCI error code used
92+
errDifferentABCICode := sdkerrors.ErrNotFound
93+
94+
ack := types.NewErrorAcknowledgement(err)
95+
ackSameABCICode := types.NewErrorAcknowledgement(errSameABCICode)
96+
ackDifferentABCICode := types.NewErrorAcknowledgement(errDifferentABCICode)
97+
98+
suite.Require().Equal(ack, ackSameABCICode)
99+
suite.Require().NotEqual(ack, ackDifferentABCICode)
100+
101+
}

modules/core/04-channel/types/acknowledgement.go

+2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ func NewResultAcknowledgement(result []byte) Acknowledgement {
2020

2121
// NewErrorAcknowledgement returns a new instance of Acknowledgement using an Acknowledgement_Error
2222
// type in the Response field.
23+
// NOTE: Acknowledgements are written into state and thus, changes made to error strings included in packet acknowledgements
24+
// risk an app hash divergence when nodes in a network are running different patch versions of software.
2325
func NewErrorAcknowledgement(err string) Acknowledgement {
2426
return Acknowledgement{
2527
Response: &Acknowledgement_Error{

0 commit comments

Comments
 (0)