Skip to content

Commit 19b5b5f

Browse files
authored
refactor: construct ics27 error acknowledgement with determinstic ABCI code (#794)
## Description Splitting #701 into 3 parts: - error acknowledgement changes - results acknowledgement changes - ADR explaining justification This pr handles the first case ref: #701 --- Before we can merge this PR, please make sure that all the following items have been checked off. If any of the checklist items are not applicable, please leave them but write a little note why. - [x] Targeted PR against correct branch (see [CONTRIBUTING.md](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#pr-targeting)) - [x] Linked to Github issue with discussion and accepted design OR link to spec that describes this work. - [x] Code follows the [module structure standards](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules/structure.md). - [x] Wrote unit and integration [tests](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#testing) - [x] Updated relevant documentation (`docs/`) or specification (`x/<module>/spec/`) - [x] Added relevant `godoc` [comments](https://blog.golang.org/godoc-documenting-go-code). - [x] Added a relevant changelog entry to the `Unreleased` section in `CHANGELOG.md` - [ ] Re-reviewed `Files changed` in the Github PR explorer - [ ] Review `Codecov Report` in the comment section below once CI passes
1 parent 4f70554 commit 19b5b5f

File tree

4 files changed

+129
-7
lines changed

4 files changed

+129
-7
lines changed

modules/apps/27-interchain-accounts/host/ibc_module.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ func (im IBCModule) OnRecvPacket(
111111
ack := channeltypes.NewResultAcknowledgement([]byte{byte(1)})
112112

113113
if err := im.keeper.OnRecvPacket(ctx, packet); err != nil {
114-
ack = channeltypes.NewErrorAcknowledgement(icatypes.AcknowledgementError)
114+
ack = types.NewErrorAcknowledgement(err)
115115

116116
// Emit an event including the error msg
117117
keeper.EmitWriteErrorAcknowledgementEvent(ctx, packet, err)
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+
// AcknowledgementErrorString 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-determinstic codespace and log values
23+
24+
errorString := fmt.Sprintf("ABCI code: %d: %s", code, ackErrorString)
25+
26+
return channeltypes.NewErrorAcknowledgement(errorString)
27+
}
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/apps/27-interchain-accounts/types/errors.go

-6
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,6 @@ import (
44
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
55
)
66

7-
const (
8-
// AcknowledgementError defines a string constant included in error acknowledgements
9-
// NOTE: Changing this const is state machine breaking as acknowledgements are written into state
10-
AcknowledgementError = "error handling packet on host chain: see events for details"
11-
)
12-
137
var (
148
ErrUnknownDataType = sdkerrors.Register(ModuleName, 2, "unknown data type")
159
ErrAccountAlreadyExist = sdkerrors.Register(ModuleName, 3, "account already exist")

0 commit comments

Comments
 (0)