-
Notifications
You must be signed in to change notification settings - Fork 592
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 msg server implementation for ChannelUpgradeAck
#3849
Changes from all commits
1293330
c4dce22
a8c6882
c17c8ed
152a873
fbacd83
dfaf589
2834124
f92edcb
0ae120f
73e9352
2b8b3fe
e53d1aa
f9c2167
566ab66
c241a63
b1958fd
ec60447
b82afaf
7082cd8
80326a0
c870ce1
cd3d443
d23c6f1
4ba1751
a710ee4
e75f1ca
0169d98
9d71ee5
970d006
d60bf1f
215280c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -805,7 +805,55 @@ func (k Keeper) ChannelUpgradeTry(goCtx context.Context, msg *channeltypes.MsgCh | |
|
||
// ChannelUpgradeAck defines a rpc handler method for MsgChannelUpgradeAck. | ||
func (k Keeper) ChannelUpgradeAck(goCtx context.Context, msg *channeltypes.MsgChannelUpgradeAck) (*channeltypes.MsgChannelUpgradeAckResponse, error) { | ||
return nil, nil | ||
ctx := sdk.UnwrapSDKContext(goCtx) | ||
|
||
module, _, err := k.ChannelKeeper.LookupModuleByChannel(ctx, msg.PortId, msg.ChannelId) | ||
if err != nil { | ||
ctx.Logger().Error("channel upgrade ack failed", "port-id", msg.PortId, "error", errorsmod.Wrap(err, "could not retrieve module from port-id")) | ||
return nil, errorsmod.Wrap(err, "could not retrieve module from port-id") | ||
} | ||
|
||
cbs, ok := k.Router.GetRoute(module) | ||
if !ok { | ||
ctx.Logger().Error("channel upgrade ack failed", "port-id", msg.PortId, "error", errorsmod.Wrapf(porttypes.ErrInvalidRoute, "route not found to module: %s", module)) | ||
return nil, errorsmod.Wrapf(porttypes.ErrInvalidRoute, "route not found to module: %s", module) | ||
} | ||
|
||
err = k.ChannelKeeper.ChanUpgradeAck(ctx, msg.PortId, msg.ChannelId, msg.CounterpartyFlushStatus, msg.CounterpartyUpgrade, msg.ProofChannel, msg.ProofUpgrade, msg.ProofHeight) | ||
if err != nil { | ||
ctx.Logger().Error("channel upgrade ack failed", "error", errorsmod.Wrap(err, "channel handshake upgrade ack failed")) | ||
if upgradeErr, ok := err.(*channeltypes.UpgradeError); ok { | ||
if err := k.ChannelKeeper.AbortUpgrade(ctx, msg.PortId, msg.ChannelId, upgradeErr); err != nil { | ||
return nil, errorsmod.Wrap(err, "channel upgrade ack (abort upgrade) failed") | ||
} | ||
|
||
// NOTE: a FAILURE result is returned to the client and an error receipt is written to state. | ||
// This signals to the relayer to begin the cancel upgrade handshake subprotocol. | ||
return &channeltypes.MsgChannelUpgradeAckResponse{Result: channeltypes.FAILURE}, nil | ||
} | ||
|
||
// NOTE: an error is returned to baseapp and transaction state is not committed. | ||
return nil, errorsmod.Wrap(err, "channel upgrade ack failed") | ||
} | ||
|
||
cacheCtx, writeFn := ctx.CacheContext() | ||
err = cbs.OnChanUpgradeAck(cacheCtx, msg.PortId, msg.ChannelId, msg.CounterpartyUpgrade.Fields.Version) | ||
if err != nil { | ||
ctx.Logger().Error("channel upgrade ack callback failed", "port-id", msg.PortId, "channel-id", msg.ChannelId, "error", err.Error()) | ||
if err := k.ChannelKeeper.AbortUpgrade(ctx, msg.PortId, msg.ChannelId, err); err != nil { | ||
return nil, errorsmod.Wrapf(err, "channel upgrade ack callback (abort upgrade) failed for port ID: %s, channel ID: %s", msg.PortId, msg.ChannelId) | ||
} | ||
|
||
return &channeltypes.MsgChannelUpgradeAckResponse{Result: channeltypes.FAILURE}, nil | ||
} | ||
|
||
writeFn() | ||
|
||
k.ChannelKeeper.WriteUpgradeAckChannel(ctx, msg.PortId, msg.ChannelId, msg.CounterpartyUpgrade.Fields.Version) | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe add: ctx.Logger().Info("channel upgrade ack succeeded", "channel-id", channelID, "port-id", msg.PortId) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep, good call. We should replicate the logging and error wrapping for the TRY handler in a follow up PR, looks like it needs it too |
||
ctx.Logger().Info("channel upgrade ack succeeded", "port-id", msg.PortId, "channel-id", msg.ChannelId) | ||
|
||
return &channeltypes.MsgChannelUpgradeAckResponse{Result: channeltypes.SUCCESS}, nil | ||
} | ||
|
||
// ChannelUpgradeOpen defines a rpc handler method for MsgChannelUpgradeOpen. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -925,6 +925,154 @@ func (suite *KeeperTestSuite) TestChannelUpgradeTry() { | |
} | ||
} | ||
|
||
func (suite *KeeperTestSuite) TestChannelUpgradeAck() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. really nice test cases 🥇 |
||
var ( | ||
path *ibctesting.Path | ||
msg *channeltypes.MsgChannelUpgradeAck | ||
) | ||
|
||
cases := []struct { | ||
name string | ||
malleate func() | ||
expResult func(res *channeltypes.MsgChannelUpgradeAckResponse, err error) | ||
}{ | ||
{ | ||
"success", | ||
func() {}, | ||
func(res *channeltypes.MsgChannelUpgradeAckResponse, err error) { | ||
suite.Require().NoError(err) | ||
suite.Require().NotNil(res) | ||
suite.Require().Equal(channeltypes.SUCCESS, res.Result) | ||
|
||
channel := path.EndpointA.GetChannel() | ||
suite.Require().Equal(channeltypes.ACKUPGRADE, channel.State) | ||
suite.Require().Equal(uint64(1), channel.UpgradeSequence) | ||
}, | ||
}, | ||
{ | ||
"module capability not found", | ||
func() { | ||
msg.PortId = ibctesting.InvalidID | ||
msg.ChannelId = ibctesting.InvalidID | ||
}, | ||
func(res *channeltypes.MsgChannelUpgradeAckResponse, err error) { | ||
suite.Require().Error(err) | ||
suite.Require().Nil(res) | ||
|
||
suite.Require().ErrorIs(err, capabilitytypes.ErrCapabilityNotFound) | ||
}, | ||
}, | ||
{ | ||
"core handler returns error and no upgrade error receipt is written", | ||
func() { | ||
// force an error by overriding the counterparty flush status to an invalid value | ||
msg.CounterpartyFlushStatus = channeltypes.NOTINFLUSH | ||
damiannolan marked this conversation as resolved.
Show resolved
Hide resolved
|
||
}, | ||
func(res *channeltypes.MsgChannelUpgradeAckResponse, err error) { | ||
suite.Require().Error(err) | ||
suite.Require().Nil(res) | ||
suite.Require().ErrorIs(err, channeltypes.ErrInvalidFlushStatus) | ||
|
||
errorReceipt, found := suite.chainA.GetSimApp().GetIBCKeeper().ChannelKeeper.GetUpgradeErrorReceipt(suite.chainA.GetContext(), path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) | ||
suite.Require().Empty(errorReceipt) | ||
suite.Require().False(found) | ||
}, | ||
}, | ||
{ | ||
"core handler returns error and writes upgrade error receipt", | ||
func() { | ||
// force an upgrade error by modifying the channel upgrade ordering to an incompatible value | ||
upgrade := path.EndpointA.GetChannelUpgrade() | ||
damiannolan marked this conversation as resolved.
Show resolved
Hide resolved
|
||
upgrade.Fields.Ordering = channeltypes.NONE | ||
|
||
path.EndpointA.SetChannelUpgrade(upgrade) | ||
}, | ||
func(res *channeltypes.MsgChannelUpgradeAckResponse, err error) { | ||
suite.Require().NoError(err) | ||
|
||
suite.Require().NotNil(res) | ||
suite.Require().Equal(channeltypes.FAILURE, res.Result) | ||
|
||
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(uint64(1), errorReceipt.Sequence) | ||
}, | ||
}, | ||
{ | ||
"application callback returns error and error receipt is written", | ||
func() { | ||
suite.chainA.GetSimApp().IBCMockModule.IBCApp.OnChanUpgradeAck = func( | ||
ctx sdk.Context, portID, channelID, counterpartyVersion string, | ||
) error { | ||
// set arbitrary value in store to mock application state changes | ||
store := ctx.KVStore(suite.chainA.GetSimApp().GetKey(exported.ModuleName)) | ||
store.Set([]byte("foo"), []byte("bar")) | ||
return fmt.Errorf("mock app callback failed") | ||
} | ||
}, | ||
func(res *channeltypes.MsgChannelUpgradeAckResponse, err error) { | ||
suite.Require().NoError(err) | ||
|
||
suite.Require().NotNil(res) | ||
suite.Require().Equal(channeltypes.FAILURE, res.Result) | ||
|
||
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(uint64(1), errorReceipt.Sequence) | ||
|
||
// assert application state changes are not committed | ||
store := suite.chainA.GetContext().KVStore(suite.chainA.GetSimApp().GetKey(exported.ModuleName)) | ||
suite.Require().False(store.Has([]byte("foo"))) | ||
}, | ||
}, | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe for completeness (and if it's not too difficult to add): should we also have a test where the application callback returns an error and There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Seems like its impossible to force the error here in the test setup. Since deleting the |
||
|
||
for _, tc := range cases { | ||
tc := tc | ||
suite.Run(tc.name, func() { | ||
suite.SetupTest() | ||
|
||
path = ibctesting.NewPath(suite.chainA, suite.chainB) | ||
suite.coordinator.Setup(path) | ||
|
||
// configure the channel upgrade version on testing endpoints | ||
path.EndpointA.ChannelConfig.ProposedUpgrade.Fields.Version = ibcmock.UpgradeVersion | ||
path.EndpointB.ChannelConfig.ProposedUpgrade.Fields.Version = ibcmock.UpgradeVersion | ||
|
||
err := path.EndpointA.ChanUpgradeInit() | ||
suite.Require().NoError(err) | ||
|
||
err = path.EndpointB.ChanUpgradeTry() | ||
suite.Require().NoError(err) | ||
|
||
err = path.EndpointA.UpdateClient() | ||
suite.Require().NoError(err) | ||
|
||
counterpartyChannel := path.EndpointB.GetChannel() | ||
counterpartyUpgrade := path.EndpointB.GetChannelUpgrade() | ||
|
||
proofChannel, proofUpgrade, proofHeight := path.EndpointA.QueryChannelUpgradeProof() | ||
|
||
msg = &channeltypes.MsgChannelUpgradeAck{ | ||
PortId: path.EndpointA.ChannelConfig.PortID, | ||
ChannelId: path.EndpointA.ChannelID, | ||
CounterpartyFlushStatus: counterpartyChannel.FlushStatus, | ||
CounterpartyUpgrade: counterpartyUpgrade, | ||
ProofChannel: proofChannel, | ||
ProofUpgrade: proofUpgrade, | ||
ProofHeight: proofHeight, | ||
Signer: suite.chainA.SenderAccount.GetAddress().String(), | ||
} | ||
|
||
tc.malleate() | ||
|
||
res, err := suite.chainA.GetSimApp().GetIBCKeeper().ChannelUpgradeAck(suite.chainA.GetContext(), msg) | ||
|
||
tc.expResult(res, err) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the plan was to rename this to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yup sure! |
||
}) | ||
} | ||
} | ||
|
||
func (suite *KeeperTestSuite) TestChannelUpgradeCancel() { | ||
var ( | ||
path *ibctesting.Path | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lets hammer down this flow in a followup and with offline discussion. Some discussion points
OnChannelUpgradeRestore
being handled in one locationAbortUpgrade
, primarily cancel doesn't need to write an upgrade receipt