Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: adding OnChanUpgradeAck handler implementation to 29-fee #4028

Merged
merged 25 commits into from
Jul 18, 2023
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
3765d93
WIP: adding fee upgrade cbs and testing
damiannolan Jul 5, 2023
79cb7f8
imp: allow failure expectations when using chain.SendMsgs
damiannolan Jul 5, 2023
64563e8
fixing import errors from cherry-pick
damiannolan Jul 5, 2023
e951cc1
updating tests and rm try code
damiannolan Jul 5, 2023
9907ea9
Merge branch '04-channel-upgrades' into damian/fee-upgrade-cbs
damiannolan Jul 5, 2023
391d44b
rm diff onChanUpgradeTry
damiannolan Jul 5, 2023
66ad804
Update modules/apps/29-fee/ibc_middleware.go
damiannolan Jul 6, 2023
e277493
adding OnChanUpgradeTry implementation for 29-fee, adding tests
damiannolan Jul 6, 2023
60a1482
rm CR in test expectation
damiannolan Jul 6, 2023
e084894
remove goconst linter as discussed async
damiannolan Jul 6, 2023
965efdc
adding onChanUpgradeAck implementation to 29-fee, adding tests
damiannolan Jul 6, 2023
09fbbac
Merge branch '04-channel-upgrades' into damian/fee-upgrade-cbs
damiannolan Jul 12, 2023
b19b53b
adding MetadataFromVersion func to pkg types
damiannolan Jul 12, 2023
1f53cd6
addressing pr feedback, disable fees on err, rename args, adding test…
damiannolan Jul 12, 2023
db256b2
Update modules/apps/29-fee/ibc_middleware_test.go
damiannolan Jul 16, 2023
ae1ea37
abstract out expIsFeeEnabled check in tests
damiannolan Jul 16, 2023
fb6a42a
adding additional error context to MetadataFromVersion
damiannolan Jul 16, 2023
28a7db2
Merge branch 'damian/fee-upgrade-cbs' into damian/1900-fee-chanupgrad…
damiannolan Jul 16, 2023
e3b75b4
Merge branch '04-channel-upgrades' into damian/1900-fee-chanupgradetry
damiannolan Jul 17, 2023
2ec1e73
propagate changes from onChanUpgradeInit PR
damiannolan Jul 17, 2023
98a9dfd
addressing test assertion feedback
damiannolan Jul 17, 2023
57016ef
Merge branch 'damian/1900-fee-chanupgradetry' into damian/1900-fee-ch…
damiannolan Jul 17, 2023
8047e3a
Merge branch '04-channel-upgrades' into damian/1900-fee-chanupgradeack
damiannolan Jul 17, 2023
ff3f639
updating to use types.MetadataFromVersion in OnChanUpgradeAck
damiannolan Jul 17, 2023
12c2b32
updating tests to add additional checks
damiannolan Jul 18, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ linters:
- dogsled
- exportloopref
- errcheck
- goconst
- gocritic
- gofumpt
- gosec
Expand Down
69 changes: 67 additions & 2 deletions modules/apps/29-fee/ibc_middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -322,16 +322,81 @@ func (im IBCMiddleware) OnTimeoutPacket(

// OnChanUpgradeInit implements the IBCModule interface
func (im IBCMiddleware) OnChanUpgradeInit(ctx sdk.Context, portID, channelID string, order channeltypes.Order, connectionHops []string, sequence uint64, version, previousVersion string) (string, error) {
return im.app.OnChanUpgradeInit(ctx, portID, channelID, order, connectionHops, sequence, version, previousVersion)
var versionMetadata types.Metadata
if err := types.ModuleCdc.UnmarshalJSON([]byte(version), &versionMetadata); err != nil {
// Since it is valid for fee version to not be specified, the above upgrade version may be for a middleware
// or application further down in the stack. Thus, if it is not a fee version we pass the entire version string onto the underlying application.
return im.app.OnChanUpgradeInit(ctx, portID, channelID, order, connectionHops, sequence, version, previousVersion)
}

if versionMetadata.FeeVersion != types.Version {
return "", errorsmod.Wrapf(types.ErrInvalidVersion, "expected %s, got %s", types.Version, versionMetadata.FeeVersion)
}

appVersion, err := im.app.OnChanUpgradeInit(ctx, portID, channelID, order, connectionHops, sequence, versionMetadata.AppVersion, previousVersion)
if err != nil {
return "", err
}

versionMetadata.AppVersion = appVersion
versionBz, err := types.ModuleCdc.MarshalJSON(&versionMetadata)
if err != nil {
return "", err
}

im.keeper.SetFeeEnabled(ctx, portID, channelID)

return string(versionBz), nil
}

// OnChanUpgradeTry implement s the IBCModule interface
func (im IBCMiddleware) OnChanUpgradeTry(ctx sdk.Context, portID, channelID string, order channeltypes.Order, connectionHops []string, counterpartyVersion string) (string, error) {
return im.app.OnChanUpgradeTry(ctx, portID, channelID, order, connectionHops, counterpartyVersion)
var versionMetadata types.Metadata
if err := types.ModuleCdc.UnmarshalJSON([]byte(counterpartyVersion), &versionMetadata); err != nil {
// Since it is valid for fee version to not be specified, the above upgrade version may be for a middleware
// or application further down in the stack. Thus, if it is not a fee version we pass the entire version string onto the underlying application.
return im.app.OnChanUpgradeTry(ctx, portID, channelID, order, connectionHops, counterpartyVersion)
}

if versionMetadata.FeeVersion != types.Version {
return "", errorsmod.Wrapf(types.ErrInvalidVersion, "expected %s, got %s", types.Version, versionMetadata.FeeVersion)
}

appVersion, err := im.app.OnChanUpgradeTry(ctx, portID, channelID, order, connectionHops, versionMetadata.AppVersion)
if err != nil {
return "", err
}

versionMetadata.AppVersion = appVersion
versionBz, err := types.ModuleCdc.MarshalJSON(&versionMetadata)
if err != nil {
return "", err
}

im.keeper.SetFeeEnabled(ctx, portID, channelID)

return string(versionBz), nil
}

// OnChanUpgradeAck implements the IBCModule interface
func (im IBCMiddleware) OnChanUpgradeAck(ctx sdk.Context, portID, channelID, counterpartyVersion string) error {
// If upgrade handshake was initialized with fee enabled it must complete with fee enabled.
// If upgrade handshake was initialized with fee disabled it must complete with fee disabled.
if im.keeper.IsFeeEnabled(ctx, portID, channelID) {
var versionMetadata types.Metadata
if err := types.ModuleCdc.UnmarshalJSON([]byte(counterpartyVersion), &versionMetadata); err != nil {
return errorsmod.Wrapf(types.ErrInvalidVersion, "failed to unmarshal ICS29 counterparty version metadata: %s", counterpartyVersion)
}

if versionMetadata.FeeVersion != types.Version {
return errorsmod.Wrapf(types.ErrInvalidVersion, "expected counterparty fee version: %s, got: %s", types.Version, versionMetadata.FeeVersion)
}

// call underlying app's OnChanUpgradeAck callback with the counterparty app version.
return im.app.OnChanUpgradeAck(ctx, portID, channelID, versionMetadata.AppVersion)
}

// call underlying app's OnChanUpgradeAck callback with the counterparty app version.
return im.app.OnChanUpgradeAck(ctx, portID, channelID, counterpartyVersion)
}

Expand Down
286 changes: 286 additions & 0 deletions modules/apps/29-fee/ibc_middleware_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1004,6 +1004,292 @@ func (suite *FeeTestSuite) TestOnTimeoutPacket() {
}
}

func (suite *FeeTestSuite) TestOnChanUpgradeInit() {
var path *ibctesting.Path

testCases := []struct {
name string
malleate func()
expError error
}{
{
"success",
func() {},
nil,
},
{
"invalid upgrade version",
func() {
path.EndpointA.ChannelConfig.ProposedUpgrade.Fields.Version = "invalid-version"
path.EndpointB.ChannelConfig.ProposedUpgrade.Fields.Version = "invalid-version"

suite.chainA.GetSimApp().FeeMockModule.IBCApp.OnChanUpgradeInit = func(_ sdk.Context, _, _ string, _ channeltypes.Order, _ []string, _ uint64, _, _ string) (string, error) {
return "", ibcmock.MockApplicationCallbackError
}
},
ibcmock.MockApplicationCallbackError,
},
{
"invalid fee version",
func() {
upgradeVersion := string(types.ModuleCdc.MustMarshalJSON(&types.Metadata{FeeVersion: "invalid-version", AppVersion: ibcmock.Version}))
path.EndpointA.ChannelConfig.ProposedUpgrade.Fields.Version = upgradeVersion
path.EndpointB.ChannelConfig.ProposedUpgrade.Fields.Version = upgradeVersion
},
types.ErrInvalidVersion,
},
{
"underlying app callback returns error",
func() {
suite.chainA.GetSimApp().FeeMockModule.IBCApp.OnChanUpgradeInit = func(_ sdk.Context, _, _ string, _ channeltypes.Order, _ []string, _ uint64, _, _ string) (string, error) {
return "", ibcmock.MockApplicationCallbackError
}
},
ibcmock.MockApplicationCallbackError,
},
}

for _, tc := range testCases {
tc := tc
suite.Run(tc.name, func() {
suite.SetupTest()

path = ibctesting.NewPath(suite.chainA, suite.chainB)

// configure the initial path to create an unincentivized mock channel
path.EndpointA.ChannelConfig.PortID = ibctesting.MockFeePort
path.EndpointB.ChannelConfig.PortID = ibctesting.MockFeePort
path.EndpointA.ChannelConfig.Version = ibcmock.Version
path.EndpointB.ChannelConfig.Version = ibcmock.Version

suite.coordinator.Setup(path)

// configure the channel upgrade version to enabled ics29 fee middleware
upgradeVersion := string(types.ModuleCdc.MustMarshalJSON(&types.Metadata{FeeVersion: types.Version, AppVersion: ibcmock.Version}))
path.EndpointA.ChannelConfig.ProposedUpgrade.Fields.Version = upgradeVersion
path.EndpointB.ChannelConfig.ProposedUpgrade.Fields.Version = upgradeVersion

tc.malleate()

err := path.EndpointA.ChanUpgradeInit()

isFeeEnabled := suite.chainA.GetSimApp().IBCFeeKeeper.IsFeeEnabled(suite.chainA.GetContext(), path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID)

expPass := tc.expError == nil
if expPass {
suite.Require().True(isFeeEnabled)
suite.Require().NoError(err)
} else {
suite.Require().False(isFeeEnabled)
suite.Require().Error(err)
suite.Require().ErrorIs(err, tc.expError)
}
})
}
}

func (suite *FeeTestSuite) TestOnChanUpgradeTry() {
var path *ibctesting.Path

testCases := []struct {
name string
malleate func()
expError error
}{
{
"success",
func() {},
nil,
},
{
"invalid upgrade version",
func() {
counterpartyUpgrade := path.EndpointA.GetChannelUpgrade()
counterpartyUpgrade.Fields.Version = "invalid-version"
path.EndpointA.SetChannelUpgrade(counterpartyUpgrade)

suite.coordinator.CommitBlock(suite.chainA)

suite.chainB.GetSimApp().FeeMockModule.IBCApp.OnChanUpgradeTry = func(_ sdk.Context, _, _ string, _ channeltypes.Order, _ []string, _ string) (string, error) {
return "", ibcmock.MockApplicationCallbackError
}
},
channeltypes.NewUpgradeError(1, ibcmock.MockApplicationCallbackError),
},
{
"invalid fee version",
func() {
upgradeVersion := string(types.ModuleCdc.MustMarshalJSON(&types.Metadata{FeeVersion: "invalid-version", AppVersion: ibcmock.Version}))

counterpartyUpgrade := path.EndpointA.GetChannelUpgrade()
counterpartyUpgrade.Fields.Version = upgradeVersion
path.EndpointA.SetChannelUpgrade(counterpartyUpgrade)

suite.coordinator.CommitBlock(suite.chainA)
},
channeltypes.NewUpgradeError(1, types.ErrInvalidVersion),
},
{
"underlying app callback returns error",
func() {
suite.chainB.GetSimApp().FeeMockModule.IBCApp.OnChanUpgradeTry = func(_ sdk.Context, _, _ string, _ channeltypes.Order, _ []string, _ string) (string, error) {
return "", ibcmock.MockApplicationCallbackError
}
},
channeltypes.NewUpgradeError(1, ibcmock.MockApplicationCallbackError),
},
}

for _, tc := range testCases {
tc := tc
suite.Run(tc.name, func() {
suite.SetupTest()

path = ibctesting.NewPath(suite.chainA, suite.chainB)

// configure the initial path to create an unincentivized mock channel
path.EndpointA.ChannelConfig.PortID = ibctesting.MockFeePort
path.EndpointB.ChannelConfig.PortID = ibctesting.MockFeePort
path.EndpointA.ChannelConfig.Version = ibcmock.Version
path.EndpointB.ChannelConfig.Version = ibcmock.Version

suite.coordinator.Setup(path)

// configure the channel upgrade version to enabled ics29 fee middleware
upgradeVersion := string(types.ModuleCdc.MustMarshalJSON(&types.Metadata{FeeVersion: types.Version, AppVersion: ibcmock.Version}))
path.EndpointA.ChannelConfig.ProposedUpgrade.Fields.Version = upgradeVersion
path.EndpointB.ChannelConfig.ProposedUpgrade.Fields.Version = upgradeVersion

err := path.EndpointA.ChanUpgradeInit()
suite.Require().NoError(err)

tc.malleate()

err = path.EndpointB.ChanUpgradeTry()

isFeeEnabled := suite.chainB.GetSimApp().IBCFeeKeeper.IsFeeEnabled(suite.chainB.GetContext(), path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID)

expPass := tc.expError == nil
if expPass {
suite.Require().True(isFeeEnabled)
suite.Require().NoError(err)
} else {
suite.Require().False(isFeeEnabled)
suite.Require().NoError(err)

// NOTE: application callback failure in OnChanUpgradeTry results in an ErrorReceipt being written to state signaling for cancellation
if expUpgradeError, ok := tc.expError.(*channeltypes.UpgradeError); ok {
errorReceipt, found := suite.chainB.GetSimApp().GetIBCKeeper().ChannelKeeper.GetUpgradeErrorReceipt(suite.chainB.GetContext(), path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID)
suite.Require().True(found)
suite.Require().Equal(expUpgradeError.GetErrorReceipt(), errorReceipt)
}
}
})
}
}

func (suite *FeeTestSuite) TestOnChanUpgradeAck() {
var path *ibctesting.Path

testCases := []struct {
name string
malleate func()
expError error
}{
{
"success",
func() {},
nil,
},
{
"success with fee middleware disabled",
func() {
suite.chainA.GetSimApp().IBCFeeKeeper.DeleteFeeEnabled(suite.chainA.GetContext(), path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID)
},
nil,
},
{
"invalid upgrade version",
func() {
counterpartyUpgrade := path.EndpointB.GetChannelUpgrade()
counterpartyUpgrade.Fields.Version = "invalid-version"
path.EndpointB.SetChannelUpgrade(counterpartyUpgrade)

suite.coordinator.CommitBlock(suite.chainB)
},
channeltypes.NewUpgradeError(1, types.ErrInvalidVersion),
},
{
"invalid fee version",
func() {
upgradeVersion := string(types.ModuleCdc.MustMarshalJSON(&types.Metadata{FeeVersion: "invalid-version", AppVersion: ibcmock.Version}))

counterpartyUpgrade := path.EndpointB.GetChannelUpgrade()
counterpartyUpgrade.Fields.Version = upgradeVersion
path.EndpointB.SetChannelUpgrade(counterpartyUpgrade)

suite.coordinator.CommitBlock(suite.chainB)
},
channeltypes.NewUpgradeError(1, types.ErrInvalidVersion),
},
{
"underlying app callback returns error",
func() {
suite.chainA.GetSimApp().FeeMockModule.IBCApp.OnChanUpgradeAck = func(_ sdk.Context, _, _, _ string) error {
return ibcmock.MockApplicationCallbackError
}
},
channeltypes.NewUpgradeError(1, ibcmock.MockApplicationCallbackError),
},
}

for _, tc := range testCases {
tc := tc
suite.Run(tc.name, func() {
suite.SetupTest()

path = ibctesting.NewPath(suite.chainA, suite.chainB)

// configure the initial path to create an unincentivized mock channel
path.EndpointA.ChannelConfig.PortID = ibctesting.MockFeePort
path.EndpointB.ChannelConfig.PortID = ibctesting.MockFeePort
path.EndpointA.ChannelConfig.Version = ibcmock.Version
path.EndpointB.ChannelConfig.Version = ibcmock.Version

suite.coordinator.Setup(path)

// configure the channel upgrade version to enabled ics29 fee middleware
upgradeVersion := string(types.ModuleCdc.MustMarshalJSON(&types.Metadata{FeeVersion: types.Version, AppVersion: ibcmock.Version}))
path.EndpointA.ChannelConfig.ProposedUpgrade.Fields.Version = upgradeVersion
path.EndpointB.ChannelConfig.ProposedUpgrade.Fields.Version = upgradeVersion

err := path.EndpointA.ChanUpgradeInit()
suite.Require().NoError(err)

err = path.EndpointB.ChanUpgradeTry()
suite.Require().NoError(err)

tc.malleate()

err = path.EndpointA.ChanUpgradeAck()

expPass := tc.expError == nil
if expPass {
suite.Require().NoError(err)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we check the feeEnabled bool here to see if it's been enabled?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah totally, I'll update these tests like what I've done in #4019 and #4023

} else {
suite.Require().NoError(err)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we see if the feeEnabled bool is false here?


// NOTE: application callback failure in OnChanUpgradeAck results in an ErrorReceipt being written to state signaling for cancellation
if expUpgradeError, ok := tc.expError.(*channeltypes.UpgradeError); ok {
errorReceipt, found := suite.chainA.GetSimApp().GetIBCKeeper().ChannelKeeper.GetUpgradeErrorReceipt(suite.chainA.GetContext(), path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID)
suite.Require().True(found)
suite.Require().Equal(expUpgradeError.GetErrorReceipt(), errorReceipt)
}
}
})
}
}

func (suite *FeeTestSuite) TestGetAppVersion() {
var (
portID string
Expand Down
Loading