diff --git a/modules/core/02-client/keeper/keeper.go b/modules/core/02-client/keeper/keeper.go index 3588b117f8c..85cf2984827 100644 --- a/modules/core/02-client/keeper/keeper.go +++ b/modules/core/02-client/keeper/keeper.go @@ -435,3 +435,28 @@ func (k Keeper) SetParams(ctx sdk.Context, params types.Params) { bz := k.cdc.MustMarshal(¶ms) store.Set([]byte(types.ParamsKey), bz) } + +// ScheduleIBCSoftwareUpgrade schedules an upgrade for the IBC client. +func (k Keeper) ScheduleIBCSoftwareUpgrade(ctx sdk.Context, plan upgradetypes.Plan, upgradedClientState exported.ClientState) error { + // zero out any custom fields before setting + cs := upgradedClientState.ZeroCustomFields() + bz, err := types.MarshalClientState(k.cdc, cs) + if err != nil { + return errorsmod.Wrap(err, "could not marshal UpgradedClientState") + } + + if err := k.upgradeKeeper.ScheduleUpgrade(ctx, plan); err != nil { + return err + } + + // sets the new upgraded client last height committed on this chain at plan.Height, + // since the chain will panic at plan.Height and new chain will resume at plan.Height + if err = k.upgradeKeeper.SetUpgradedClient(ctx, plan.Height, bz); err != nil { + return err + } + + // emitting an event for handling client upgrade proposal + emitUpgradeClientProposalEvent(ctx, plan.Name, plan.Height) + + return nil +} diff --git a/modules/core/02-client/keeper/keeper_test.go b/modules/core/02-client/keeper/keeper_test.go index 9bb1a6704c7..46edef8d584 100644 --- a/modules/core/02-client/keeper/keeper_test.go +++ b/modules/core/02-client/keeper/keeper_test.go @@ -12,7 +12,9 @@ import ( "github.com/cosmos/cosmos-sdk/codec" cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" tmbytes "github.com/cometbft/cometbft/libs/bytes" tmproto "github.com/cometbft/cometbft/proto/tendermint/types" @@ -483,3 +485,113 @@ func (suite *KeeperTestSuite) TestUnsetParams() { suite.chainA.GetSimApp().IBCKeeper.ClientKeeper.GetParams(ctx) }) } + +// TestIBCSoftwareUpgrade tests that an IBC client upgrade has been properly scheduled +func (suite *KeeperTestSuite) TestIBCSoftwareUpgrade() { + var ( + upgradedClientState *ibctm.ClientState + oldPlan, plan upgradetypes.Plan + ) + + testCases := []struct { + name string + malleate func() + expError error + }{ + { + "valid upgrade proposal", + func() {}, + nil, + }, + { + "valid upgrade proposal with previous IBC state", func() { + oldPlan = upgradetypes.Plan{ + Name: "upgrade IBC clients", + Height: 100, + } + }, + nil, + }, + { + "fail: scheduling upgrade with plan height 0", + func() { + plan.Height = 0 + }, + sdkerrors.ErrInvalidRequest, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + suite.SetupTest() // reset + oldPlan.Height = 0 // reset + + path := ibctesting.NewPath(suite.chainA, suite.chainB) + suite.coordinator.SetupClients(path) + upgradedClientState = suite.chainA.GetClientState(path.EndpointA.ClientID).ZeroCustomFields().(*ibctm.ClientState) + + // use height 1000 to distinguish from old plan + plan = upgradetypes.Plan{ + Name: "upgrade IBC clients", + Height: 1000, + } + + tc.malleate() + + // set the old plan if it is not empty + if oldPlan.Height != 0 { + // set upgrade plan in the upgrade store + store := suite.chainA.GetContext().KVStore(suite.chainA.GetSimApp().GetKey(upgradetypes.StoreKey)) + bz := suite.chainA.App.AppCodec().MustMarshal(&oldPlan) + store.Set(upgradetypes.PlanKey(), bz) + + bz, err := types.MarshalClientState(suite.chainA.App.AppCodec(), upgradedClientState) + suite.Require().NoError(err) + + suite.Require().NoError(suite.chainA.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainA.GetContext(), oldPlan.Height, bz)) + } + + err := suite.chainA.App.GetIBCKeeper().ClientKeeper.ScheduleIBCSoftwareUpgrade(suite.chainA.GetContext(), plan, upgradedClientState) + + if tc.expError == nil { + suite.Require().NoError(err) + + // check that the correct plan is returned + storedPlan, found := suite.chainA.GetSimApp().UpgradeKeeper.GetUpgradePlan(suite.chainA.GetContext()) + suite.Require().True(found) + suite.Require().Equal(plan, storedPlan) + + // check that old upgraded client state is cleared + cs, found := suite.chainA.GetSimApp().UpgradeKeeper.GetUpgradedClient(suite.chainA.GetContext(), oldPlan.Height) + suite.Require().False(found) + suite.Require().Empty(cs) + + // check that client state was set + storedClientState, found := suite.chainA.GetSimApp().UpgradeKeeper.GetUpgradedClient(suite.chainA.GetContext(), plan.Height) + suite.Require().True(found) + clientState, err := types.UnmarshalClientState(suite.chainA.App.AppCodec(), storedClientState) + suite.Require().NoError(err) + suite.Require().Equal(upgradedClientState, clientState) + } else { + // check that the new plan wasn't stored + storedPlan, found := suite.chainA.GetSimApp().UpgradeKeeper.GetUpgradePlan(suite.chainA.GetContext()) + if oldPlan.Height != 0 { + // NOTE: this is only true if the ScheduleUpgrade function + // returns an error before clearing the old plan + suite.Require().True(found) + suite.Require().Equal(oldPlan, storedPlan) + } else { + suite.Require().False(found) + suite.Require().Empty(storedPlan) + } + + // check that client state was not set + cs, found := suite.chainA.GetSimApp().UpgradeKeeper.GetUpgradedClient(suite.chainA.GetContext(), plan.Height) + suite.Require().Empty(cs) + suite.Require().False(found) + } + }) + } +} diff --git a/modules/core/02-client/types/codec.go b/modules/core/02-client/types/codec.go index dbe5b036c20..e7250824d82 100644 --- a/modules/core/02-client/types/codec.go +++ b/modules/core/02-client/types/codec.go @@ -49,6 +49,7 @@ func RegisterInterfaces(registry codectypes.InterfaceRegistry) { &MsgUpgradeClient{}, &MsgSubmitMisbehaviour{}, &MsgUpdateParams{}, + &MsgIBCSoftwareUpgrade{}, &MsgRecoverClient{}, ) diff --git a/modules/core/02-client/types/msgs.go b/modules/core/02-client/types/msgs.go index b854155fdb6..f0894736ae7 100644 --- a/modules/core/02-client/types/msgs.go +++ b/modules/core/02-client/types/msgs.go @@ -5,6 +5,7 @@ import ( codectypes "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" + upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" host "github.com/cosmos/ibc-go/v7/modules/core/24-host" ibcerrors "github.com/cosmos/ibc-go/v7/modules/core/errors" @@ -17,6 +18,7 @@ var ( _ sdk.Msg = (*MsgSubmitMisbehaviour)(nil) _ sdk.Msg = (*MsgUpgradeClient)(nil) _ sdk.Msg = (*MsgUpdateParams)(nil) + _ sdk.Msg = (*MsgIBCSoftwareUpgrade)(nil) _ sdk.Msg = (*MsgRecoverClient)(nil) _ codectypes.UnpackInterfacesMessage = (*MsgCreateClient)(nil) @@ -285,17 +287,41 @@ func (msg *MsgUpdateParams) ValidateBasic() error { return msg.Params.Validate() } -// NewMsgRecoverClient creates a new MsgRecoverClient instance -func NewMsgRecoverClient(signer, subjectClientID, substituteClientID string) *MsgRecoverClient { - return &MsgRecoverClient{ - Signer: signer, - SubjectClientId: subjectClientID, - SubstituteClientId: substituteClientID, +// NewMsgIBCSoftwareUpgrade creates a new MsgIBCSoftwareUpgrade instance +func NewMsgIBCSoftwareUpgrade(signer string, plan upgradetypes.Plan, upgradedClientState exported.ClientState) (*MsgIBCSoftwareUpgrade, error) { + anyClient, err := PackClientState(upgradedClientState) + if err != nil { + return nil, err } + + return &MsgIBCSoftwareUpgrade{ + Signer: signer, + Plan: plan, + UpgradedClientState: anyClient, + }, nil } -// GetSigners returns the expected signers for a MsgRecoverClient message. -func (msg *MsgRecoverClient) GetSigners() []sdk.AccAddress { +// ValidateBasic performs basic checks on a MsgIBCSoftwareUpgrade. +func (msg *MsgIBCSoftwareUpgrade) ValidateBasic() error { + if _, err := sdk.AccAddressFromBech32(msg.Signer); err != nil { + return errorsmod.Wrapf(ibcerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) + } + + clientState, err := UnpackClientState(msg.UpgradedClientState) + if err != nil { + return err + } + + // for the time being, we should implicitly be on tendermint when using ibc-go + if clientState.ClientType() != exported.Tendermint { + return errorsmod.Wrapf(ErrInvalidUpgradeClient, "upgraded client state must be a Tendermint client") + } + + return msg.Plan.ValidateBasic() +} + +// GetSigners returns the expected signers for a MsgIBCSoftwareUpgrade message. +func (msg *MsgIBCSoftwareUpgrade) GetSigners() []sdk.AccAddress { accAddr, err := sdk.AccAddressFromBech32(msg.Signer) if err != nil { panic(err) @@ -303,6 +329,15 @@ func (msg *MsgRecoverClient) GetSigners() []sdk.AccAddress { return []sdk.AccAddress{accAddr} } +// NewMsgRecoverClient creates a new MsgRecoverClient instance +func NewMsgRecoverClient(signer, subjectClientID, substituteClientID string) *MsgRecoverClient { + return &MsgRecoverClient{ + Signer: signer, + SubjectClientId: subjectClientID, + SubstituteClientId: substituteClientID, + } +} + // ValidateBasic performs basic checks on a MsgRecoverClient. func (msg *MsgRecoverClient) ValidateBasic() error { if _, err := sdk.AccAddressFromBech32(msg.Signer); err != nil { @@ -315,3 +350,12 @@ func (msg *MsgRecoverClient) ValidateBasic() error { return host.ClientIdentifierValidator(msg.SubstituteClientId) } + +// GetSigners returns the expected signers for a MsgRecoverClient message. +func (msg *MsgRecoverClient) GetSigners() []sdk.AccAddress { + accAddr, err := sdk.AccAddressFromBech32(msg.Signer) + if err != nil { + panic(err) + } + return []sdk.AccAddress{accAddr} +} diff --git a/modules/core/02-client/types/msgs_test.go b/modules/core/02-client/types/msgs_test.go index 03fc09b59b9..af6ad66d976 100644 --- a/modules/core/02-client/types/msgs_test.go +++ b/modules/core/02-client/types/msgs_test.go @@ -1,6 +1,7 @@ package types_test import ( + "errors" "testing" "time" @@ -9,11 +10,13 @@ import ( testifysuite "github.com/stretchr/testify/suite" sdk "github.com/cosmos/cosmos-sdk/types" + upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" host "github.com/cosmos/ibc-go/v7/modules/core/24-host" ibcerrors "github.com/cosmos/ibc-go/v7/modules/core/errors" + "github.com/cosmos/ibc-go/v7/modules/core/exported" solomachine "github.com/cosmos/ibc-go/v7/modules/light-clients/06-solomachine" ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v7/testing" @@ -763,3 +766,86 @@ func TestMsgRecoverClientGetSigners(t *testing.T) { } } } + +// TestMsgIBCSoftwareUpgrade_NewMsgIBCSoftwareUpgrade tests NewMsgIBCSoftwareUpgrade +func (suite *TypesTestSuite) TestMsgIBCSoftwareUpgrade_NewMsgIBCSoftwareUpgrade() { + testCases := []struct { + name string + upgradedClientState exported.ClientState + expPass bool + }{ + { + "success", + ibctm.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath), + true, + }, + { + "fail: failed to pack ClientState", + nil, + false, + }, + } + + for _, tc := range testCases { + plan := upgradetypes.Plan{ + Name: "upgrade IBC clients", + Height: 1000, + } + msg, err := types.NewMsgIBCSoftwareUpgrade( + ibctesting.TestAccAddress, + plan, + tc.upgradedClientState, + ) + + if tc.expPass { + suite.Require().NoError(err) + suite.Assert().Equal(ibctesting.TestAccAddress, msg.Signer) + suite.Assert().Equal(plan, msg.Plan) + unpackedClientState, err := types.UnpackClientState(msg.UpgradedClientState) + suite.Require().NoError(err) + suite.Assert().Equal(tc.upgradedClientState, unpackedClientState) + } else { + suite.Require().True(errors.Is(err, ibcerrors.ErrPackAny)) + } + } +} + +// TestMsgIBCSoftwareUpgrade_GetSigners tests GetSigners for MsgIBCSoftwareUpgrade +func (suite *TypesTestSuite) TestMsgIBCSoftwareUpgrade_GetSigners() { + testCases := []struct { + name string + address sdk.AccAddress + expPass bool + }{ + { + "success: valid address", + sdk.AccAddress(ibctesting.TestAccAddress), + true, + }, + { + "failure: nil address", + nil, + false, + }, + } + + for _, tc := range testCases { + clientState := ibctm.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath) + plan := upgradetypes.Plan{ + Name: "upgrade IBC clients", + Height: 1000, + } + msg, err := types.NewMsgIBCSoftwareUpgrade( + tc.address.String(), + plan, + clientState, + ) + suite.Require().NoError(err) + + if tc.expPass { + suite.Require().Equal([]sdk.AccAddress{tc.address}, msg.GetSigners()) + } else { + suite.Require().Panics(func() { msg.GetSigners() }) + } + } +} diff --git a/modules/core/02-client/types/tx.pb.go b/modules/core/02-client/types/tx.pb.go index d52b6bb51f5..95a5d100d55 100644 --- a/modules/core/02-client/types/tx.pb.go +++ b/modules/core/02-client/types/tx.pb.go @@ -8,6 +8,7 @@ import ( fmt "fmt" types "github.com/cosmos/cosmos-sdk/codec/types" _ "github.com/cosmos/cosmos-sdk/types/msgservice" + types1 "github.com/cosmos/cosmos-sdk/x/upgrade/types" _ "github.com/cosmos/gogoproto/gogoproto" grpc1 "github.com/cosmos/gogoproto/grpc" proto "github.com/cosmos/gogoproto/proto" @@ -280,6 +281,113 @@ func (m *MsgUpgradeClientResponse) XXX_DiscardUnknown() { var xxx_messageInfo_MsgUpgradeClientResponse proto.InternalMessageInfo +// MsgIBCSoftwareUpgrade defines an sdk.Msg to schedule an upgrade of an IBC client using a v1 governance proposal +type MsgIBCSoftwareUpgrade struct { + Plan types1.Plan `protobuf:"bytes,1,opt,name=plan,proto3" json:"plan"` + // An UpgradedClientState must be provided to perform an IBC breaking upgrade. + // This will make the chain commit to the correct upgraded (self) client state + // before the upgrade occurs, so that connecting chains can verify that the + // new upgraded client is valid by verifying a proof on the previous version + // of the chain. This will allow IBC connections to persist smoothly across + // planned chain upgrades. Correspondingly, the UpgradedClientState field has been + // deprecated in the Cosmos SDK to allow for this logic to exist solely in + // the 02-client module. + UpgradedClientState *types.Any `protobuf:"bytes,2,opt,name=upgraded_client_state,json=upgradedClientState,proto3" json:"upgraded_client_state,omitempty"` + // signer defaults to the governance account address unless otherwise specified. + Signer string `protobuf:"bytes,3,opt,name=signer,proto3" json:"signer,omitempty"` +} + +func (m *MsgIBCSoftwareUpgrade) Reset() { *m = MsgIBCSoftwareUpgrade{} } +func (m *MsgIBCSoftwareUpgrade) String() string { return proto.CompactTextString(m) } +func (*MsgIBCSoftwareUpgrade) ProtoMessage() {} +func (*MsgIBCSoftwareUpgrade) Descriptor() ([]byte, []int) { + return fileDescriptor_cb5dc4651eb49a04, []int{6} +} +func (m *MsgIBCSoftwareUpgrade) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgIBCSoftwareUpgrade) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgIBCSoftwareUpgrade.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgIBCSoftwareUpgrade) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgIBCSoftwareUpgrade.Merge(m, src) +} +func (m *MsgIBCSoftwareUpgrade) XXX_Size() int { + return m.Size() +} +func (m *MsgIBCSoftwareUpgrade) XXX_DiscardUnknown() { + xxx_messageInfo_MsgIBCSoftwareUpgrade.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgIBCSoftwareUpgrade proto.InternalMessageInfo + +func (m *MsgIBCSoftwareUpgrade) GetPlan() types1.Plan { + if m != nil { + return m.Plan + } + return types1.Plan{} +} + +func (m *MsgIBCSoftwareUpgrade) GetUpgradedClientState() *types.Any { + if m != nil { + return m.UpgradedClientState + } + return nil +} + +func (m *MsgIBCSoftwareUpgrade) GetSigner() string { + if m != nil { + return m.Signer + } + return "" +} + +// MsgIBCSoftwareUpgradeResponse defines the Msg/IBCSoftwareUpgrade response type. +type MsgIBCSoftwareUpgradeResponse struct { +} + +func (m *MsgIBCSoftwareUpgradeResponse) Reset() { *m = MsgIBCSoftwareUpgradeResponse{} } +func (m *MsgIBCSoftwareUpgradeResponse) String() string { return proto.CompactTextString(m) } +func (*MsgIBCSoftwareUpgradeResponse) ProtoMessage() {} +func (*MsgIBCSoftwareUpgradeResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_cb5dc4651eb49a04, []int{7} +} +func (m *MsgIBCSoftwareUpgradeResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgIBCSoftwareUpgradeResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgIBCSoftwareUpgradeResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgIBCSoftwareUpgradeResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgIBCSoftwareUpgradeResponse.Merge(m, src) +} +func (m *MsgIBCSoftwareUpgradeResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgIBCSoftwareUpgradeResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgIBCSoftwareUpgradeResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgIBCSoftwareUpgradeResponse proto.InternalMessageInfo + // MsgSubmitMisbehaviour defines an sdk.Msg type that submits Evidence for // light client misbehaviour. // This message has been deprecated. Use MsgUpdateClient instead. @@ -298,7 +406,7 @@ func (m *MsgSubmitMisbehaviour) Reset() { *m = MsgSubmitMisbehaviour{} } func (m *MsgSubmitMisbehaviour) String() string { return proto.CompactTextString(m) } func (*MsgSubmitMisbehaviour) ProtoMessage() {} func (*MsgSubmitMisbehaviour) Descriptor() ([]byte, []int) { - return fileDescriptor_cb5dc4651eb49a04, []int{6} + return fileDescriptor_cb5dc4651eb49a04, []int{8} } func (m *MsgSubmitMisbehaviour) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -336,7 +444,7 @@ func (m *MsgSubmitMisbehaviourResponse) Reset() { *m = MsgSubmitMisbehav func (m *MsgSubmitMisbehaviourResponse) String() string { return proto.CompactTextString(m) } func (*MsgSubmitMisbehaviourResponse) ProtoMessage() {} func (*MsgSubmitMisbehaviourResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_cb5dc4651eb49a04, []int{7} + return fileDescriptor_cb5dc4651eb49a04, []int{9} } func (m *MsgSubmitMisbehaviourResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -379,7 +487,7 @@ func (m *MsgUpdateParams) Reset() { *m = MsgUpdateParams{} } func (m *MsgUpdateParams) String() string { return proto.CompactTextString(m) } func (*MsgUpdateParams) ProtoMessage() {} func (*MsgUpdateParams) Descriptor() ([]byte, []int) { - return fileDescriptor_cb5dc4651eb49a04, []int{8} + return fileDescriptor_cb5dc4651eb49a04, []int{10} } func (m *MsgUpdateParams) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -416,7 +524,7 @@ func (m *MsgUpdateParamsResponse) Reset() { *m = MsgUpdateParamsResponse func (m *MsgUpdateParamsResponse) String() string { return proto.CompactTextString(m) } func (*MsgUpdateParamsResponse) ProtoMessage() {} func (*MsgUpdateParamsResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_cb5dc4651eb49a04, []int{9} + return fileDescriptor_cb5dc4651eb49a04, []int{11} } func (m *MsgUpdateParamsResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -533,6 +641,8 @@ func init() { proto.RegisterType((*MsgUpdateClientResponse)(nil), "ibc.core.client.v1.MsgUpdateClientResponse") proto.RegisterType((*MsgUpgradeClient)(nil), "ibc.core.client.v1.MsgUpgradeClient") proto.RegisterType((*MsgUpgradeClientResponse)(nil), "ibc.core.client.v1.MsgUpgradeClientResponse") + proto.RegisterType((*MsgIBCSoftwareUpgrade)(nil), "ibc.core.client.v1.MsgIBCSoftwareUpgrade") + proto.RegisterType((*MsgIBCSoftwareUpgradeResponse)(nil), "ibc.core.client.v1.MsgIBCSoftwareUpgradeResponse") proto.RegisterType((*MsgSubmitMisbehaviour)(nil), "ibc.core.client.v1.MsgSubmitMisbehaviour") proto.RegisterType((*MsgSubmitMisbehaviourResponse)(nil), "ibc.core.client.v1.MsgSubmitMisbehaviourResponse") proto.RegisterType((*MsgUpdateParams)(nil), "ibc.core.client.v1.MsgUpdateParams") @@ -612,6 +722,8 @@ type MsgClient interface { UpdateClient(ctx context.Context, in *MsgUpdateClient, opts ...grpc.CallOption) (*MsgUpdateClientResponse, error) // UpgradeClient defines a rpc handler method for MsgUpgradeClient. UpgradeClient(ctx context.Context, in *MsgUpgradeClient, opts ...grpc.CallOption) (*MsgUpgradeClientResponse, error) + // IBCSoftwareUpgrade defines a rpc handler method for MsgIBCSoftwareUpgrade. + IBCSoftwareUpgrade(ctx context.Context, in *MsgIBCSoftwareUpgrade, opts ...grpc.CallOption) (*MsgIBCSoftwareUpgradeResponse, error) // SubmitMisbehaviour defines a rpc handler method for MsgSubmitMisbehaviour. SubmitMisbehaviour(ctx context.Context, in *MsgSubmitMisbehaviour, opts ...grpc.CallOption) (*MsgSubmitMisbehaviourResponse, error) // UpdateClientParams defines a rpc handler method for MsgUpdateParams. @@ -655,6 +767,15 @@ func (c *msgClient) UpgradeClient(ctx context.Context, in *MsgUpgradeClient, opt return out, nil } +func (c *msgClient) IBCSoftwareUpgrade(ctx context.Context, in *MsgIBCSoftwareUpgrade, opts ...grpc.CallOption) (*MsgIBCSoftwareUpgradeResponse, error) { + out := new(MsgIBCSoftwareUpgradeResponse) + err := c.cc.Invoke(ctx, "/ibc.core.client.v1.Msg/IBCSoftwareUpgrade", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *msgClient) SubmitMisbehaviour(ctx context.Context, in *MsgSubmitMisbehaviour, opts ...grpc.CallOption) (*MsgSubmitMisbehaviourResponse, error) { out := new(MsgSubmitMisbehaviourResponse) err := c.cc.Invoke(ctx, "/ibc.core.client.v1.Msg/SubmitMisbehaviour", in, out, opts...) @@ -690,6 +811,8 @@ type MsgServer interface { UpdateClient(context.Context, *MsgUpdateClient) (*MsgUpdateClientResponse, error) // UpgradeClient defines a rpc handler method for MsgUpgradeClient. UpgradeClient(context.Context, *MsgUpgradeClient) (*MsgUpgradeClientResponse, error) + // IBCSoftwareUpgrade defines a rpc handler method for MsgIBCSoftwareUpgrade. + IBCSoftwareUpgrade(context.Context, *MsgIBCSoftwareUpgrade) (*MsgIBCSoftwareUpgradeResponse, error) // SubmitMisbehaviour defines a rpc handler method for MsgSubmitMisbehaviour. SubmitMisbehaviour(context.Context, *MsgSubmitMisbehaviour) (*MsgSubmitMisbehaviourResponse, error) // UpdateClientParams defines a rpc handler method for MsgUpdateParams. @@ -711,6 +834,9 @@ func (*UnimplementedMsgServer) UpdateClient(ctx context.Context, req *MsgUpdateC func (*UnimplementedMsgServer) UpgradeClient(ctx context.Context, req *MsgUpgradeClient) (*MsgUpgradeClientResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method UpgradeClient not implemented") } +func (*UnimplementedMsgServer) IBCSoftwareUpgrade(ctx context.Context, req *MsgIBCSoftwareUpgrade) (*MsgIBCSoftwareUpgradeResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method IBCSoftwareUpgrade not implemented") +} func (*UnimplementedMsgServer) SubmitMisbehaviour(ctx context.Context, req *MsgSubmitMisbehaviour) (*MsgSubmitMisbehaviourResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method SubmitMisbehaviour not implemented") } @@ -779,6 +905,24 @@ func _Msg_UpgradeClient_Handler(srv interface{}, ctx context.Context, dec func(i return interceptor(ctx, in, info, handler) } +func _Msg_IBCSoftwareUpgrade_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgIBCSoftwareUpgrade) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).IBCSoftwareUpgrade(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ibc.core.client.v1.Msg/IBCSoftwareUpgrade", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).IBCSoftwareUpgrade(ctx, req.(*MsgIBCSoftwareUpgrade)) + } + return interceptor(ctx, in, info, handler) +} + func _Msg_SubmitMisbehaviour_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(MsgSubmitMisbehaviour) if err := dec(in); err != nil { @@ -849,6 +993,10 @@ var _Msg_serviceDesc = grpc.ServiceDesc{ MethodName: "UpgradeClient", Handler: _Msg_UpgradeClient_Handler, }, + { + MethodName: "IBCSoftwareUpgrade", + Handler: _Msg_IBCSoftwareUpgrade_Handler, + }, { MethodName: "SubmitMisbehaviour", Handler: _Msg_SubmitMisbehaviour_Handler, @@ -1113,6 +1261,81 @@ func (m *MsgUpgradeClientResponse) MarshalToSizedBuffer(dAtA []byte) (int, error return len(dAtA) - i, nil } +func (m *MsgIBCSoftwareUpgrade) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgIBCSoftwareUpgrade) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgIBCSoftwareUpgrade) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Signer) > 0 { + i -= len(m.Signer) + copy(dAtA[i:], m.Signer) + i = encodeVarintTx(dAtA, i, uint64(len(m.Signer))) + i-- + dAtA[i] = 0x1a + } + if m.UpgradedClientState != nil { + { + size, err := m.UpgradedClientState.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + { + size, err := m.Plan.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *MsgIBCSoftwareUpgradeResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgIBCSoftwareUpgradeResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgIBCSoftwareUpgradeResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + func (m *MsgSubmitMisbehaviour) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -1428,6 +1651,34 @@ func (m *MsgUpgradeClientResponse) Size() (n int) { return n } +func (m *MsgIBCSoftwareUpgrade) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Plan.Size() + n += 1 + l + sovTx(uint64(l)) + if m.UpgradedClientState != nil { + l = m.UpgradedClientState.Size() + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.Signer) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgIBCSoftwareUpgradeResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + func (m *MsgSubmitMisbehaviour) Size() (n int) { if m == nil { return 0 @@ -2226,6 +2477,207 @@ func (m *MsgUpgradeClientResponse) Unmarshal(dAtA []byte) error { } return nil } +func (m *MsgIBCSoftwareUpgrade) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgIBCSoftwareUpgrade: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgIBCSoftwareUpgrade: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Plan", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Plan.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UpgradedClientState", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.UpgradedClientState == nil { + m.UpgradedClientState = &types.Any{} + } + if err := m.UpgradedClientState.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signer", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Signer = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgIBCSoftwareUpgradeResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgIBCSoftwareUpgradeResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgIBCSoftwareUpgradeResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *MsgSubmitMisbehaviour) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 diff --git a/modules/core/keeper/msg_server.go b/modules/core/keeper/msg_server.go index 8e2fae23a8c..ff466352ebf 100644 --- a/modules/core/keeper/msg_server.go +++ b/modules/core/keeper/msg_server.go @@ -714,6 +714,12 @@ func (k Keeper) UpdateClientParams(goCtx context.Context, msg *clienttypes.MsgUp return &clienttypes.MsgUpdateParamsResponse{}, nil } +// IBCSoftwareUpgrade defines a rpc handler method for MsgIBCSoftwareUpgrade. +func (Keeper) IBCSoftwareUpgrade(goCtx context.Context, msg *clienttypes.MsgIBCSoftwareUpgrade) (*clienttypes.MsgIBCSoftwareUpgradeResponse, error) { + // TODO + return nil, nil +} + // UpdateConnectionParams defines a rpc handler method for MsgUpdateParams for the 03-connection submodule. func (k Keeper) UpdateConnectionParams(goCtx context.Context, msg *connectiontypes.MsgUpdateParams) (*connectiontypes.MsgUpdateParamsResponse, error) { if k.GetAuthority() != msg.Authority { diff --git a/modules/core/keeper/msg_server_test.go b/modules/core/keeper/msg_server_test.go index fdcc5f172e4..a11ded00ee2 100644 --- a/modules/core/keeper/msg_server_test.go +++ b/modules/core/keeper/msg_server_test.go @@ -826,6 +826,10 @@ func (suite *KeeperTestSuite) TestUpdateClientParams() { } } +// TestIBCSoftwareUpgrade tests the IBCSoftwareUpgrade rpc handler +func (*KeeperTestSuite) TestIBCSoftwareUpgrade() { +} + // TestUpdateConnectionParams tests the UpdateConnectionParams rpc handler func (suite *KeeperTestSuite) TestUpdateConnectionParams() { validAuthority := suite.chainA.App.GetIBCKeeper().GetAuthority() diff --git a/proto/ibc/core/client/v1/tx.proto b/proto/ibc/core/client/v1/tx.proto index bb96ac2c70c..59e80600e7e 100644 --- a/proto/ibc/core/client/v1/tx.proto +++ b/proto/ibc/core/client/v1/tx.proto @@ -5,6 +5,7 @@ package ibc.core.client.v1; option go_package = "github.com/cosmos/ibc-go/v7/modules/core/02-client/types"; import "cosmos/msg/v1/msg.proto"; +import "cosmos/upgrade/v1beta1/upgrade.proto"; import "gogoproto/gogo.proto"; import "google/protobuf/any.proto"; import "ibc/core/client/v1/client.proto"; @@ -22,6 +23,9 @@ service Msg { // UpgradeClient defines a rpc handler method for MsgUpgradeClient. rpc UpgradeClient(MsgUpgradeClient) returns (MsgUpgradeClientResponse); + // IBCSoftwareUpgrade defines a rpc handler method for MsgIBCSoftwareUpgrade. + rpc IBCSoftwareUpgrade(MsgIBCSoftwareUpgrade) returns (MsgIBCSoftwareUpgradeResponse); + // SubmitMisbehaviour defines a rpc handler method for MsgSubmitMisbehaviour. rpc SubmitMisbehaviour(MsgSubmitMisbehaviour) returns (MsgSubmitMisbehaviourResponse); @@ -93,6 +97,26 @@ message MsgUpgradeClient { // MsgUpgradeClientResponse defines the Msg/UpgradeClient response type. message MsgUpgradeClientResponse {} +// MsgIBCSoftwareUpgrade defines an sdk.Msg to schedule an upgrade of an IBC client using a v1 governance proposal +message MsgIBCSoftwareUpgrade { + option (cosmos.msg.v1.signer) = "signer"; + cosmos.upgrade.v1beta1.Plan plan = 1 [(gogoproto.nullable) = false]; + // An UpgradedClientState must be provided to perform an IBC breaking upgrade. + // This will make the chain commit to the correct upgraded (self) client state + // before the upgrade occurs, so that connecting chains can verify that the + // new upgraded client is valid by verifying a proof on the previous version + // of the chain. This will allow IBC connections to persist smoothly across + // planned chain upgrades. Correspondingly, the UpgradedClientState field has been + // deprecated in the Cosmos SDK to allow for this logic to exist solely in + // the 02-client module. + google.protobuf.Any upgraded_client_state = 2; + // signer defaults to the governance account address unless otherwise specified. + string signer = 3; +} + +// MsgIBCSoftwareUpgradeResponse defines the Msg/IBCSoftwareUpgrade response type. +message MsgIBCSoftwareUpgradeResponse {} + // MsgSubmitMisbehaviour defines an sdk.Msg type that submits Evidence for // light client misbehaviour. // This message has been deprecated. Use MsgUpdateClient instead.